diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 3c1f41bdcca6c..0000000000000 --- a/.editorconfig +++ /dev/null @@ -1,25 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - - -[*] -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 4 - -[*.rs] -indent_style = space -indent_size = 4 - -[*.toml] -indent_style = space -indent_size = 4 - -[*.md] -trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index 309fbd95345a4..57407a2399a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -95,7 +95,6 @@ config.stamp keywords.md lexer.ml src/etc/dl -src/librustc_llvm/llvmdeps.rs tmp.*.rs version.md version.ml diff --git a/.gitmodules b/.gitmodules index 2802c8d63913f..0a1188e83eae6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,3 +42,9 @@ [submodule "src/tools/miri"] path = src/tools/miri url = https://github.com/solson/miri.git +[submodule "src/dlmalloc"] + path = src/dlmalloc + url = https://github.com/alexcrichton/dlmalloc-rs.git +[submodule "src/binaryen"] + path = src/binaryen + url = https://github.com/alexcrichton/binaryen.git diff --git a/.travis.yml b/.travis.yml index 9e3225a103ab9..b2840ac3121f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,18 +12,17 @@ matrix: fast_finish: true include: # Images used in testing PR and try-build should be run first. - - env: IMAGE=x86_64-gnu-llvm-3.7 RUST_BACKTRACE=1 + - env: IMAGE=x86_64-gnu-llvm-3.9 RUST_BACKTRACE=1 if: type = pull_request OR branch = auto - env: IMAGE=dist-x86_64-linux DEPLOY=1 if: branch = try OR branch = auto - # "alternate" deployments, these are "nightlies" but don't have assertions - # turned on, they're deployed to a different location primarily for projects - # which are stuck on nightly and don't want llvm assertions in the artifacts - # that they use. + # "alternate" deployments, these are "nightlies" but have LLVM assertions + # turned on, they're deployed to a different location primarily for + # additional testing. - env: IMAGE=dist-x86_64-linux DEPLOY_ALT=1 - if: branch = auto + if: branch = try OR branch = auto - env: > RUST_CHECK_TARGET=dist @@ -36,7 +35,7 @@ matrix: NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 os: osx - osx_image: xcode7 + osx_image: xcode7.3 if: branch = auto # macOS builders. These are placed near the beginning because they are very @@ -57,7 +56,7 @@ matrix: NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 os: osx - osx_image: xcode8.2 + osx_image: xcode8.3 if: branch = auto - env: > @@ -71,7 +70,7 @@ matrix: NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 os: osx - osx_image: xcode8.2 + osx_image: xcode8.3 if: branch = auto # OSX builders producing releases. These do not run the full test suite and @@ -91,7 +90,7 @@ matrix: NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 os: osx - osx_image: xcode7 + osx_image: xcode7.3 if: branch = auto - env: > @@ -105,7 +104,7 @@ matrix: NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 os: osx - osx_image: xcode7 + osx_image: xcode7.3 if: branch = auto # Linux builders, remaining docker images @@ -113,7 +112,9 @@ matrix: if: branch = auto - env: IMAGE=armhf-gnu if: branch = auto - - env: IMAGE=cross DEPLOY=1 + - env: IMAGE=dist-various-1 DEPLOY=1 + if: branch = auto + - env: IMAGE=dist-various-2 DEPLOY=1 if: branch = auto - env: IMAGE=dist-aarch64-linux DEPLOY=1 if: branch = auto @@ -125,8 +126,6 @@ matrix: if: branch = auto - env: IMAGE=dist-armv7-linux DEPLOY=1 if: branch = auto - - env: IMAGE=dist-fuchsia DEPLOY=1 - if: branch = auto - env: IMAGE=dist-i586-gnu-i686-musl DEPLOY=1 if: branch = auto - env: IMAGE=dist-i686-freebsd DEPLOY=1 @@ -161,14 +160,16 @@ matrix: if: branch = auto - env: IMAGE=i686-gnu-nopt if: branch = auto - # - env: IMAGE=wasm32 issue 42646 - # if: branch = auto + - env: IMAGE=wasm32-unknown + if: branch = auto - env: IMAGE=x86_64-gnu if: branch = auto - env: IMAGE=x86_64-gnu-full-bootstrap if: branch = auto - env: IMAGE=x86_64-gnu-aux if: branch = auto + - env: IMAGE=x86_64-gnu-tools + if: branch = auto - env: IMAGE=x86_64-gnu-debug if: branch = auto - env: IMAGE=x86_64-gnu-nopt @@ -251,7 +252,14 @@ after_failure: # Random attempt at debugging currently. Just poking around in here to see if # anything shows up. - - ls $HOME/Library/Logs/DiagnosticReports/ + - ls -lat $HOME/Library/Logs/DiagnosticReports/ + - find $HOME/Library/Logs/DiagnosticReports + -type f + -not -name '*.stage2-*.crash' + -not -name 'com.apple.CoreSimulator.CoreSimulatorService-*.crash' + -exec printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" {} \; + -exec head -750 {} \; + -exec echo travis_fold":"end:crashlog \; # attempt to debug anything killed by the oom killer on linux, just to see if # it happened @@ -284,6 +292,7 @@ before_deploy: rm -rf obj/build/dist/doc && cp -r obj/build/dist/* deploy/$TRAVIS_COMMIT; fi + - travis_retry gem update --system deploy: - provider: s3 @@ -300,11 +309,30 @@ deploy: branch: auto condition: $DEPLOY = 1 + # this is the same as the above deployment provider except that it uploads to + # a slightly different directory and has a different trigger - provider: s3 bucket: rust-lang-ci2 skip_cleanup: true local_dir: deploy - upload_dir: rustc-builds-try + upload_dir: rustc-builds-alt + acl: public_read + region: us-west-1 + access_key_id: AKIAJVBODR3IA4O72THQ + secret_access_key: + secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0=" + on: + branch: auto + condition: $DEPLOY_ALT = 1 + + # These two providers are the same as the two above, except deploy on the + # try branch. Travis does not appear to provide a way to use "or" in these + # conditions. + - provider: s3 + bucket: rust-lang-ci2 + skip_cleanup: true + local_dir: deploy + upload_dir: rustc-builds acl: public_read region: us-west-1 access_key_id: AKIAJVBODR3IA4O72THQ @@ -314,8 +342,6 @@ deploy: branch: try condition: $DEPLOY = 1 - # this is the same as the above deployment provider except that it uploads to - # a slightly different directory and has a different trigger - provider: s3 bucket: rust-lang-ci2 skip_cleanup: true @@ -327,5 +353,5 @@ deploy: secret_access_key: secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0=" on: - branch: auto + branch: try condition: $DEPLOY_ALT = 1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 6bec933018686..d42476bc4130d 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ A version of this document [can be found online](https://www.rust-lang.org/condu **Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) -* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. * Please be kind and courteous. There's no need to be mean or rude. * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3834c54d6c4f7..e3767df2808fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,5 @@ # Contributing to Rust +[contributing-to-rust]: #contributing-to-rust Thank you for your interest in contributing to Rust! There are many ways to contribute, and we appreciate all of them. This document is a bit long, so here's @@ -18,11 +19,12 @@ hop on [#rust-internals][pound-rust-internals]. As a reminder, all contributors are expected to follow our [Code of Conduct][coc]. -[pound-rust-internals]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals +[pound-rust-internals]: https://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals [internals]: https://internals.rust-lang.org [coc]: https://www.rust-lang.org/conduct.html ## Feature Requests +[feature-requests]: #feature-requests To request a change to the way that the Rust language works, please open an issue in the [RFCs repository](https://github.com/rust-lang/rfcs/issues/new) @@ -30,6 +32,7 @@ rather than this one. New features and other significant language changes must go through the RFC process. ## Bug Reports +[bug-reports]: #bug-reports While bugs are unfortunate, they're a reality in software. We can't fix what we don't know about, so please report liberally. If you're not sure if something @@ -80,6 +83,7 @@ $ RUST_BACKTRACE=1 rustc ... ``` ## The Build System +[the-build-system]: #the-build-system Rust's build system allows you to bootstrap the compiler, run tests & benchmarks, generate documentation, install a fresh build of Rust, and more. @@ -94,6 +98,7 @@ system internals, try asking in [`#rust-internals`][pound-rust-internals]. [bootstrap]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/ ### Configuration +[configuration]: #configuration Before you can start building the compiler you need to configure the build for your system. In most cases, that will just mean using the defaults provided @@ -125,6 +130,11 @@ file. If you still have a `config.mk` file in your directory - from `./configure` - you may need to delete it for `config.toml` to work. ### Building +[building]: #building + +Dependencies +- [build dependencies](README.md#building-from-source) +- `gdb` 6.2.0 minimum, 7.1 or later recommended for test builds The build system uses the `x.py` script to control the build process. This script is used to build, test, and document various parts of the compiler. You can @@ -194,6 +204,7 @@ Note: Previously `./configure` and `make` were used to build this project. They are still available, but `x.py` is the recommended build system. ### Useful commands +[useful-commands]: #useful-commands Some common invocations of `x.py` are: @@ -234,6 +245,7 @@ Some common invocations of `x.py` are: code. ### Using your local build +[using-local-build]: #using-local-build If you use Rustup to manage your rust install, it has a feature called ["custom toolchains"][toolchain-link] that you can use to access your newly-built compiler @@ -262,6 +274,7 @@ stage 1. `python x.py build --stage 1 src/libstd src/tools/rustdoc` will build rustdoc and libstd, which will allow rustdoc to be run with that toolchain.) ## Pull Requests +[pull-requests]: #pull-requests Pull requests are the primary mechanism we use to change Rust. GitHub itself has some [great documentation][pull-requests] on using the Pull Request feature. @@ -326,6 +339,7 @@ it can be found [here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md). ### External Dependencies +[external-dependencies]: #external-dependencies Currently building Rust will also build the following external projects: @@ -333,13 +347,136 @@ Currently building Rust will also build the following external projects: * [miri](https://github.com/solson/miri) If your changes break one of these projects, you need to fix them by opening -a pull request against the broken project. When you have opened a pull request, -you can disable the tool via `src/tools/toolstate.toml`. +a pull request against the broken project asking to put the fix on a branch. +Then you can disable the tool building via `src/tools/toolstate.toml`. +Once the branch containing your fix is likely to be merged, you can point +the affected submodule at this branch. + +Don't forget to also add your changes with + +``` +git add path/to/submodule +``` + +outside the submodule. + +In order to prepare your PR, you can run the build locally by doing +`./x.py build src/tools/TOOL`. If you will be editing the sources +there, you may wish to set `submodules = false` in the `config.toml` +to prevent `x.py` from resetting to the original branch. + +#### Breaking Tools Built With The Compiler +[breaking-tools-built-with-the-compiler]: #breaking-tools-built-with-the-compiler + +Rust's build system builds a number of tools that make use of the +internals of the compiler. This includes clippy, +[RLS](https://github.com/rust-lang-nursery/rls) and +[rustfmt](https://github.com/rust-lang-nursery/rustfmt). If these tools +break because of your changes, you may run into a sort of "chicken and egg" +problem. These tools rely on the latest compiler to be built so you can't update +them to reflect your changes to the compiler until those changes are merged into +the compiler. At the same time, you can't get your changes merged into the compiler +because the rust-lang/rust build won't pass until those tools build and pass their +tests. + +That means that, in the default state, you can't update the compiler without first +fixing rustfmt, rls and the other tools that the compiler builds. + +Luckily, a feature was [added to Rust's build](https://github.com/rust-lang/rust/pull/45243) +to make all of this easy to handle. The idea is that you mark the tools as "broken", +so that the rust-lang/rust build passes without trying to build them, then land the change +in the compiler, wait for a nightly, and go update the tools that you broke. Once you're done +and the tools are working again, you go back in the compiler and change the tools back +from "broken". + +This should avoid a bunch of synchronization dances and is also much easier on contributors as +there's no need to block on rls/rustfmt/other tools changes going upstream. + +Here are those same steps in detail: + +1. (optional) First, if it doesn't exist already, create a `config.toml` by copying + `config.toml.example` in the root directory of the Rust repository. + Set `submodules = false` in the `[build]` section. This will prevent `x.py` + from resetting to the original branch after you make your changes. If you + need to [update any submodules to their latest versions][updating-submodules], + see the section of this file about that for more information. +2. (optional) Run `./x.py test src/tools/rustfmt` (substituting the submodule + that broke for `rustfmt`). Fix any errors in the submodule (and possibly others). +3. (optional) Make commits for your changes and send them to upstream repositories as a PR. +4. (optional) Maintainers of these submodules will **not** merge the PR. The PR can't be + merged because CI will be broken. You'll want to write a message on the PR referencing + your change, and how the PR should be merged once your change makes it into a nightly. +5. Update `src/tools/toolstate.toml` to indicate that the tool in question is "broken", + that will disable building it on CI. See the documentation in that file for the exact + configuration values you can use. +6. Commit the changes to `src/tools/toolstate.toml`, **do not update submodules in your commit**, + and then update the PR you have for rust-lang/rust. +7. Wait for your PR to merge. +8. Wait for a nightly +9. (optional) Help land your PR on the upstream repository now that your changes are in nightly. +10. (optional) Send a PR to rust-lang/rust updating the submodule, reverting `src/tools/toolstate.toml` back to a "building" or "testing" state. + +#### Updating submodules +[updating-submodules]: #updating-submodules + +These instructions are specific to updating `rustfmt`, however they may apply +to the other submodules as well. Please help by improving these instructions +if you find any discrepancies or special cases that need to be addressed. + +To update the `rustfmt` submodule, start by running the appropriate +[`git submodule` command](https://git-scm.com/book/en/v2/Git-Tools-Submodules). +For example, to update to the latest commit on the remote master branch, +you may want to run: +``` +git submodule update --remote src/tools/rustfmt +``` +If you run `./x.py build` now, and you are lucky, it may just work. If you see +an error message about patches that did not resolve to any crates, you will need +to complete a few more steps which are outlined with their rationale below. + +*(This error may change in the future to include more information.)* +``` +error: failed to resolve patches for `https://github.com/rust-lang-nursery/rustfmt` + +Caused by: + patch for `rustfmt-nightly` in `https://github.com/rust-lang-nursery/rustfmt` did not resolve to any crates +failed to run: ~/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo build --manifest-path ~/rust/src/bootstrap/Cargo.toml +``` + +If you haven't used the `[patch]` +section of `Cargo.toml` before, there is [some relevant documentation about it +in the cargo docs](http://doc.crates.io/manifest.html#the-patch-section). In +addition to that, you should read the +[Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies) +section of the documentation as well. + +Specifically, the following [section in Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#testing-a-bugfix) reveals what the problem is: + +> Next up we need to ensure that our lock file is updated to use this new version of uuid so our project uses the locally checked out copy instead of one from crates.io. The way [patch] works is that it'll load the dependency at ../path/to/uuid and then whenever crates.io is queried for versions of uuid it'll also return the local version. +> +> This means that the version number of the local checkout is significant and will affect whether the patch is used. Our manifest declared uuid = "1.0" which means we'll only resolve to >= 1.0.0, < 2.0.0, and Cargo's greedy resolution algorithm also means that we'll resolve to the maximum version within that range. Typically this doesn't matter as the version of the git repository will already be greater or match the maximum version published on crates.io, but it's important to keep this in mind! + +This says that when we updated the submodule, the version number in our +`src/tools/rustfmt/Cargo.toml` changed. The new version is different from +the version in `Cargo.lock`, so the build can no longer continue. + +To resolve this, we need to update `Cargo.lock`. Luckily, cargo provides a +command to do this easily. + +First, go into the `src/` directory since that is where `Cargo.toml` is in +the rust repository. Then run, `cargo update -p rustfmt-nightly` to solve +the problem. + +``` +$ cd src +$ cargo update -p rustfmt-nightly +``` -It can also be more convenient during development to set `submodules = false` -in the `config.toml` to prevent `x.py` from resetting to the original branch. +This should change the version listed in `src/Cargo.lock` to the new version you updated +the submodule to. Running `./x.py build` should work now. ## Writing Documentation +[writing-documentation]: #writing-documentation Documentation improvements are very welcome. The source of `doc.rust-lang.org` is located in `src/doc` in the tree, and standard API documentation is generated @@ -370,6 +507,7 @@ reference to `doc/reference.html`. The CSS might be messed up, but you can verify that the HTML is right. ## Issue Triage +[issue-triage]: #issue-triage Sometimes, an issue will stay open, even though the bug has been fixed. And sometimes, the original bug may go stale because something has changed in the @@ -437,6 +575,7 @@ If you're looking for somewhere to start, check out the [E-easy][eeasy] tag. [rfcbot]: https://github.com/dikaiosune/rust-dashboard/blob/master/RFCBOT.md ## Out-of-tree Contributions +[out-of-tree-contributions]: #out-of-tree-contributions There are a number of other ways to contribute to Rust that don't deal with this repository. @@ -456,6 +595,7 @@ valuable! [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library ## Helpful Links and Information +[helpful-info]: #helpful-info For people new to Rust, and just starting to contribute, or even for more seasoned developers, some useful places to look for information diff --git a/README.md b/README.md index 78a9f509bbcb4..589aa1afe35ec 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ standard library, and documentation. [Rust]: https://www.rust-lang.org ## Quick Start +[quick-start]: #quick-start Read ["Installation"] from [The Book]. @@ -13,7 +14,9 @@ Read ["Installation"] from [The Book]. [The Book]: https://doc.rust-lang.org/book/index.html ## Building from Source +[building-from-source]: #building-from-source +### Building on *nix 1. Make sure you have installed the dependencies: * `g++` 4.7 or later or `clang++` 3.x or later @@ -52,6 +55,7 @@ Read ["Installation"] from [The Book]. [Cargo]: https://github.com/rust-lang/cargo ### Building on Windows +[building-on-windows]: #building-on-windows There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by Visual Studio, and the GNU ABI used by the GCC toolchain. Which version of Rust @@ -61,6 +65,7 @@ for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU build. #### MinGW +[windows-mingw]: #windows-mingw [MSYS2][msys2] can be used to easily build Rust on Windows: @@ -101,6 +106,7 @@ build. ``` #### MSVC +[windows-msvc]: #windows-msvc MSVC builds of Rust additionally require an installation of Visual Studio 2013 (or later) so `rustc` can use its linker. Make sure to check the “C++ tools” @@ -123,7 +129,11 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64. python x.py build ``` +If you are seeing build failure when compiling `rustc_binaryen`, make sure the path +length of the rust folder is not longer than 22 characters. + #### Specifying an ABI +[specifying-an-abi]: #specifying-an-abi Each specific ABI can also be used from either environment (for example, using the GNU ABI in powershell) by using an explicit build triple. The available @@ -141,6 +151,7 @@ in Building From Source), and modifying the `build` option under the `[build]` section. ### Configure and Make +[configure-and-make]: #configure-and-make While it's not the recommended build system, this project also provides a configure script and makefile (the latter of which just invokes `x.py`). @@ -155,6 +166,7 @@ When using the configure script, the generated `config.mk` file may override the `config.mk` file. ## Building Documentation +[building-documentation]: #building-documentation If you’d like to build the documentation, it’s almost the same: @@ -167,6 +179,7 @@ the ABI used. I.e., if the ABI was `x86_64-pc-windows-msvc`, the directory will `build\x86_64-pc-windows-msvc\doc`. ## Notes +[notes]: #notes Since the Rust compiler is written in Rust, it must be built by a precompiled "snapshot" version of itself (made in an earlier state of @@ -184,7 +197,7 @@ Snapshot binaries are currently built and tested on several platforms: You may find that other platforms work, but these are our officially supported build environments that are most likely to work. -Rust currently needs between 600MiB and 1.5GiB to build, depending on platform. +Rust currently needs between 600MiB and 1.5GiB of RAM to build, depending on platform. If it hits swap, it will take a very long time to build. There is more advice about hacking on Rust in [CONTRIBUTING.md]. @@ -192,6 +205,7 @@ There is more advice about hacking on Rust in [CONTRIBUTING.md]. [CONTRIBUTING.md]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md ## Getting Help +[getting-help]: #getting-help The Rust community congregates in a few places: @@ -204,6 +218,7 @@ The Rust community congregates in a few places: [users.rust-lang.org]: https://users.rust-lang.org/ ## Contributing +[contributing]: #contributing To contribute to Rust, please see [CONTRIBUTING](CONTRIBUTING.md). @@ -217,6 +232,7 @@ Rust. And a good place to ask for help would be [#rust-beginners]. [#rust-beginners]: irc://irc.mozilla.org/rust-beginners ## License +[license]: #license Rust is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various diff --git a/RELEASES.md b/RELEASES.md index 2979ffe136c90..57434ebc1f600 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,193 @@ +Version 1.22.1 (2017-11-22) +========================== + +- [Update Cargo to fix an issue with macOS 10.13 "High Sierra"][46183] + +[46183]: https://github.com/rust-lang/rust/pull/46183 + +Version 1.22.0 (2017-11-22) +========================== + +Language +-------- +- [`non_snake_case` lint now allows extern no-mangle functions][44966] +- [Now accepts underscores in unicode escapes][43716] +- [`T op= &T` now works for numeric types.][44287] eg. `let mut x = 2; x += &8;` +- [types that impl `Drop` are now allowed in `const` and `static` types][44456] + +Compiler +-------- +- [rustc now defaults to having 16 codegen units at debug on supported platforms.][45064] +- [rustc will no longer inline in codegen units when compiling for debug][45075] + This should decrease compile times for debug builds. +- [strict memory alignment now enabled on ARMv6][45094] +- [Remove support for the PNaCl target `le32-unknown-nacl`][45041] + +Libraries +--------- +- [Allow atomic operations up to 32 bits + on `armv5te_unknown_linux_gnueabi`][44978] +- [`Box` now impls `From>`][44466] +- [`std::mem::Discriminant` is now guaranteed to be `Send + Sync`][45095] +- [`fs::copy` now returns the length of the main stream on NTFS.][44895] +- [Properly detect overflow in `Instant += Duration`.][44220] +- [impl `Hasher` for `{&mut Hasher, Box}`][44015] +- [impl `fmt::Debug` for `SplitWhitespace`.][44303] +- [`Option` now impls `Try`][42526] This allows for using `?` with `Option` types. + +Stabilized APIs +--------------- + +Cargo +----- +- [Cargo will now build multi file examples in subdirectories of the `examples` + folder that have a `main.rs` file.][cargo/4496] +- [Changed `[root]` to `[package]` in `Cargo.lock`][cargo/4571] Packages with + the old format will continue to work and can be updated with `cargo update`. +- [Now supports vendoring git repositories][cargo/3992] + +Misc +---- +- [`libbacktrace` is now available on Apple platforms.][44251] +- [Stabilised the `compile_fail` attribute for code fences in doc-comments.][43949] + This now lets you specify that a given code example will fail to compile. + +Compatibility Notes +------------------- +- [The minimum Android version that rustc can build for has been bumped + to `4.0` from `2.3`][45656] +- [Allowing `T op= &T` for numeric types has broken some type + inference cases][45480] + + +[42526]: https://github.com/rust-lang/rust/pull/42526 +[43017]: https://github.com/rust-lang/rust/pull/43017 +[43716]: https://github.com/rust-lang/rust/pull/43716 +[43949]: https://github.com/rust-lang/rust/pull/43949 +[44015]: https://github.com/rust-lang/rust/pull/44015 +[44220]: https://github.com/rust-lang/rust/pull/44220 +[44251]: https://github.com/rust-lang/rust/pull/44251 +[44287]: https://github.com/rust-lang/rust/pull/44287 +[44303]: https://github.com/rust-lang/rust/pull/44303 +[44456]: https://github.com/rust-lang/rust/pull/44456 +[44466]: https://github.com/rust-lang/rust/pull/44466 +[44895]: https://github.com/rust-lang/rust/pull/44895 +[44966]: https://github.com/rust-lang/rust/pull/44966 +[44978]: https://github.com/rust-lang/rust/pull/44978 +[45041]: https://github.com/rust-lang/rust/pull/45041 +[45064]: https://github.com/rust-lang/rust/pull/45064 +[45075]: https://github.com/rust-lang/rust/pull/45075 +[45094]: https://github.com/rust-lang/rust/pull/45094 +[45095]: https://github.com/rust-lang/rust/pull/45095 +[45480]: https://github.com/rust-lang/rust/issues/45480 +[45656]: https://github.com/rust-lang/rust/pull/45656 +[cargo/3992]: https://github.com/rust-lang/cargo/pull/3992 +[cargo/4496]: https://github.com/rust-lang/cargo/pull/4496 +[cargo/4571]: https://github.com/rust-lang/cargo/pull/4571 + + + + + + +Version 1.21.0 (2017-10-12) +========================== + +Language +-------- +- [You can now use static references for literals.][43838] + Example: + ```rust + fn main() { + let x: &'static u32 = &0; + } + ``` +- [Relaxed path syntax. Optional `::` before `<` is now allowed in all contexts.][43540] + Example: + ```rust + my_macro!(Vec::new); // Always worked + my_macro!(Vec::::new); // Now works + ``` + +Compiler +-------- +- [Upgraded jemalloc to 4.5.0][43911] +- [Enabled unwinding panics on Redox][43917] +- [Now runs LLVM in parallel during translation phase.][43506] + This should reduce peak memory usage. + +Libraries +--------- +- [Generate builtin impls for `Clone` for all arrays and tuples that + are `T: Clone`][43690] +- [`Stdin`, `Stdout`, and `Stderr` now implement `AsRawFd`.][43459] +- [`Rc` and `Arc` now implement `From<&[T]> where T: Clone`, `From`, + `From`, `From> where T: ?Sized`, and `From>`.][42565] + +Stabilized APIs +--------------- + +[`std::mem::discriminant`] + +Cargo +----- +- [You can now call `cargo install` with multiple package names][cargo/4216] +- [Cargo commands inside a virtual workspace will now implicitly + pass `--all`][cargo/4335] +- [Added a `[patch]` section to `Cargo.toml` to handle + prepublication dependencies][cargo/4123] [RFC 1969] +- [`include` & `exclude` fields in `Cargo.toml` now accept gitignore + like patterns][cargo/4270] +- [Added the `--all-targets` option][cargo/4400] +- [Using required dependencies as a feature is now deprecated and emits + a warning][cargo/4364] + + +Misc +---- +- [Cargo docs are moving][43916] + to [doc.rust-lang.org/cargo](https://doc.rust-lang.org/cargo) +- [The rustdoc book is now available][43863] + at [doc.rust-lang.org/rustdoc](https://doc.rust-lang.org/rustdoc) +- [Added a preview of RLS has been made available through rustup][44204] + Install with `rustup component add rls-preview` +- [`std::os` documentation for Unix, Linux, and Windows now appears on doc.rust-lang.org][43348] + Previously only showed `std::os::unix`. + +Compatibility Notes +------------------- +- [Changes in method matching against higher-ranked types][43880] This may cause + breakage in subtyping corner cases. [A more in-depth explanation is available.][info/43880] +- [rustc's JSON error output's byte position start at top of file.][42973] + Was previously relative to the rustc's internal `CodeMap` struct which + required the unstable library `libsyntax` to correctly use. +- [`unused_results` lint no longer ignores booleans][43728] + +[42565]: https://github.com/rust-lang/rust/pull/42565 +[42973]: https://github.com/rust-lang/rust/pull/42973 +[43348]: https://github.com/rust-lang/rust/pull/43348 +[43459]: https://github.com/rust-lang/rust/pull/43459 +[43506]: https://github.com/rust-lang/rust/pull/43506 +[43540]: https://github.com/rust-lang/rust/pull/43540 +[43690]: https://github.com/rust-lang/rust/pull/43690 +[43728]: https://github.com/rust-lang/rust/pull/43728 +[43838]: https://github.com/rust-lang/rust/pull/43838 +[43863]: https://github.com/rust-lang/rust/pull/43863 +[43880]: https://github.com/rust-lang/rust/pull/43880 +[43911]: https://github.com/rust-lang/rust/pull/43911 +[43916]: https://github.com/rust-lang/rust/pull/43916 +[43917]: https://github.com/rust-lang/rust/pull/43917 +[44204]: https://github.com/rust-lang/rust/pull/44204 +[cargo/4123]: https://github.com/rust-lang/cargo/pull/4123 +[cargo/4216]: https://github.com/rust-lang/cargo/pull/4216 +[cargo/4270]: https://github.com/rust-lang/cargo/pull/4270 +[cargo/4335]: https://github.com/rust-lang/cargo/pull/4335 +[cargo/4364]: https://github.com/rust-lang/cargo/pull/4364 +[cargo/4400]: https://github.com/rust-lang/cargo/pull/4400 +[RFC 1969]: https://github.com/rust-lang/rfcs/pull/1969 +[info/43880]: https://github.com/rust-lang/rust/issues/44224#issuecomment-330058902 +[`std::mem::discriminant`]: https://doc.rust-lang.org/std/mem/fn.discriminant.html + Version 1.20.0 (2017-08-31) =========================== @@ -110,7 +300,7 @@ Compatibility Notes - [Functions with `'static` in their return types will now not be as usable as if they were using lifetime parameters instead.][42417] - [The reimplementation of `{f32, f64}::is_sign_{negative, positive}` now - takes the sign of NaN into account where previously didn't.][42430] + takes the sign of NaN into account where previously didn't.][42430] [42033]: https://github.com/rust-lang/rust/pull/42033 [42155]: https://github.com/rust-lang/rust/pull/42155 @@ -438,7 +628,7 @@ Misc ---- - [rustdoc can now use pulldown-cmark with the `--enable-commonmark` flag][40338] -- [Added rust-winbg script for better debugging on Windows][39983] +- [Added rust-windbg script for better debugging on Windows][39983] - [Rust now uses the official cross compiler for NetBSD][40612] - [rustdoc now accepts `#` at the start of files][40828] - [Fixed jemalloc support for musl][41168] @@ -1472,7 +1662,7 @@ Diagnostics ----------- * [Replace macro backtraces with labeled local uses][35702] -* [Improve error message for missplaced doc comments][33922] +* [Improve error message for misplaced doc comments][33922] * [Buffer unix and lock windows to prevent message interleaving][35975] * [Update lifetime errors to specifically note temporaries][36171] * [Special case a few colors for Windows][36178] @@ -1780,7 +1970,7 @@ Language useful](https://github.com/rust-lang/rust/pull/34908) * [`macro_rules!` `stmt` matchers correctly consume the entire contents when inside non-braces invocations](https://github.com/rust-lang/rust/pull/34886) -* [Semicolons are properly required as statement delimeters inside +* [Semicolons are properly required as statement delimiters inside `macro_rules!` invocations](https://github.com/rust-lang/rust/pull/34660) * [`cfg_attr` works on `path` attributes](https://github.com/rust-lang/rust/pull/34546) @@ -2005,7 +2195,7 @@ Compatibility Notes * [`const`s and `static`s may not have unsized types](https://github.com/rust-lang/rust/pull/34443) * [The new follow-set rules that place restrictions on `macro_rules!` in order to ensure syntax forward-compatibility have been enabled](https://github.com/rust-lang/rust/pull/33982) - This was an [ammendment to RFC 550](https://github.com/rust-lang/rfcs/pull/1384), + This was an [amendment to RFC 550](https://github.com/rust-lang/rfcs/pull/1384), and has been a warning since 1.10. * [`cfg` attribute process has been refactored to fix various bugs](https://github.com/rust-lang/rust/pull/33706). This causes breakage in some corner cases. @@ -3162,7 +3352,7 @@ Libraries * `FromStr` is [implemented for `SockAddrV4` and `SockAddrV6`][1.5s]. * There are now `From` conversions [between floating point types][1.5f] where the conversions are lossless. -* Thera are now `From` conversions [between integer types][1.5i] where +* There are now `From` conversions [between integer types][1.5i] where the conversions are lossless. * [`fs::Metadata` implements `Clone`][1.5fs]. * The `parse` method [accepts a leading "+" when parsing @@ -3362,7 +3552,7 @@ Libraries * [`IntoIterator` is implemented for references to `Option` and `Result`][into2]. * [`HashMap` and `HashSet` implement `Extend<&T>` where `T: - Copy`][ext] as part of [RFC 839]. This will cause type inferance + Copy`][ext] as part of [RFC 839]. This will cause type inference breakage in rare situations. * [`BinaryHeap` implements `Debug`][bh2]. * [`Borrow` and `BorrowMut` are implemented for fixed-size @@ -3373,7 +3563,7 @@ Libraries * `&mut T` where `T: std::fmt::Write` [also implements `std::fmt::Write`][mutw]. * [A stable regression in `VecDeque::push_back` and other - capicity-altering methods that caused panics for zero-sized types + capacity-altering methods that caused panics for zero-sized types was fixed][vd]. * [Function pointers implement traits for up to 12 parameters][fp2]. @@ -3560,7 +3750,7 @@ Libraries [better for long data][sh]. * [`AtomicPtr`] implements [`Send`]. * The [`read_to_end`] implementations for [`Stdin`] and [`File`] - are now [specialized to use uninitalized buffers for increased + are now [specialized to use uninitialized buffers for increased performance][rte]. * Lifetime parameters of foreign functions [are now resolved properly][f]. @@ -3689,7 +3879,7 @@ Highlights * This is the first release with [experimental support for linking with the MSVC linker and lib C on Windows (instead of using the GNU variants via MinGW)][win]. It is yet recommended only for the most - intrepid Rusticians. + intrepid Rustaceans. * Benchmark compilations are showing a 30% improvement in bootstrapping over 1.1. @@ -4555,7 +4745,7 @@ Version 0.11.0 (2014-07-02) * Libraries * The standard library is now a "facade" over a number of underlying libraries. This means that development on the standard library should - be speeder due to smaller crates, as well as a clearer line between + be speedier due to smaller crates, as well as a clearer line between all dependencies. * A new library, libcore, lives under the standard library's facade which is Rust's "0-assumption" library, suitable for embedded and diff --git a/appveyor.yml b/appveyor.yml index 599d1b40ceb1e..b8fd479d0f149 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,6 +25,11 @@ environment: RUST_CHECK_TARGET: check-aux RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc + # MSVC tools tests + - MSYS_BITS: 64 + SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py toolstates.json + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=toolstates.json + # 32/64-bit MinGW builds. # # We are using MinGW with posix threads since LLVM does not compile with diff --git a/config.toml.example b/config.toml.example index a3790c8d20258..18c1f160c03d2 100644 --- a/config.toml.example +++ b/config.toml.example @@ -35,7 +35,7 @@ # If an external LLVM root is specified, we automatically check the version by # default to make sure it's within the range that we're expecting, but setting # this flag will indicate that this version check should not be done. -#version-check = false +#version-check = true # Link libstdc++ statically into the librustc_llvm instead of relying on a # dynamic version to be available. @@ -60,10 +60,9 @@ # LLVM experimental targets to build support for. These targets are specified in # the same format as above, but since these targets are experimental, they are # not built by default and the experimental Rust compilation targets that depend -# on them will not work unless the user opts in to building them. Possible -# experimental LLVM targets include WebAssembly for the -# wasm32-experimental-emscripten Rust target. -#experimental-targets = "" +# on them will not work unless the user opts in to building them. By default the +# `WebAssembly` target is enabled when compiling LLVM from scratch. +#experimental-targets = "WebAssembly" # Cap the number of parallel linker invocations when compiling LLVM. # This can be useful when building LLVM with debug info, which significantly @@ -203,6 +202,16 @@ # Where to install man pages in `prefix` above #mandir = "share/man" +# Where to install data in `prefix` above (currently unused) +#datadir = "share" + +# Where to install additional info in `prefix` above (currently unused) +#infodir = "share/info" + +# Where to install local state (currently unused) +# If this is a relative path, it will get installed in `prefix` above +#localstatedir = "/var/lib" + # ============================================================================= # Options for compiling Rust code itself # ============================================================================= @@ -250,14 +259,11 @@ # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE) #backtrace = true -# The default linker that will be used by the generated compiler. Note that this -# is not the linker used to link said compiler. +# The default linker that will be hard-coded into the generated compiler for +# targets that don't specify linker explicitly in their target specifications. +# Note that this is not the linker used to link said compiler. #default-linker = "cc" -# The default ar utility that will be used by the generated compiler if LLVM -# cannot be used. Note that this is not used to assemble said compiler. -#default-ar = "ar" - # The "channel" for the Rust build to produce. The stable/beta channels only # allow using stable features, whereas the nightly and dev channels allow using # nightly features @@ -295,6 +301,10 @@ # As a side-effect also generates MIR for all libraries. #test-miri = false +# After building or testing extended tools (e.g. clippy and rustfmt), append the +# result (broken, compiling, testing) into this JSON file. +#save-toolstates = "/path/to/toolstates.json" + # ============================================================================= # Options for specific targets # @@ -303,7 +313,7 @@ # ============================================================================= [target.x86_64-unknown-linux-gnu] -# C compiler to be used to compiler C code and link Rust code. Note that the +# C compiler to be used to compiler C code. Note that the # default value is platform specific, and if not specified it may also depend on # what platform is crossing to what platform. #cc = "cc" @@ -312,6 +322,15 @@ # This is only used for host targets. #cxx = "c++" +# Archiver to be used to assemble static libraries compiled from C/C++ code. +# Note: an absolute path should be used, otherwise LLVM build will break. +#ar = "ar" + +# Linker to be used to link Rust code. Note that the +# default value is platform specific, and if not specified it may also depend on +# what platform is crossing to what platform. +#linker = "cc" + # Path to the `llvm-config` binary of the installation of a custom LLVM to link # against. Note that if this is specifed we don't compile LLVM at all for this # target. diff --git a/src/Cargo.lock b/src/Cargo.lock index 5e775a0f61e0f..bfb442ce3db95 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1,7 +1,3 @@ -[root] -name = "workspace_symbol" -version = "0.1.0" - [[package]] name = "advapi32-sys" version = "0.2.0" @@ -24,7 +20,7 @@ name = "aho-corasick" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -32,6 +28,7 @@ name = "alloc" version = "0.0.0" dependencies = [ "core 0.0.0", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "std_unicode 0.0.0", ] @@ -42,7 +39,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "libc 0.0.0", ] @@ -53,17 +50,18 @@ version = "0.0.0" dependencies = [ "alloc 0.0.0", "core 0.0.0", + "dlmalloc 0.0.0", "libc 0.0.0", ] [[package]] name = "ansi_term" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ar" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -72,55 +70,47 @@ version = "0.0.0" [[package]] name = "atty" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bin_lib" version = "0.1.0" -[[package]] -name = "bin_lib_no_cfg_test" -version = "0.1.0" - [[package]] name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "bitflags" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bitflags" version = "0.9.1" @@ -128,7 +118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -136,16 +126,16 @@ name = "bootstrap" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -162,8 +152,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "build-manifest" version = "0.1.0" dependencies = [ - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -171,7 +161,7 @@ dependencies = [ name = "build_helper" version = "0.1.0" dependencies = [ - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -181,20 +171,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo" -version = "0.23.0" +version = "0.24.0" dependencies = [ - "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cargotest 0.1.0", - "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crates-io 0.12.0", + "core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crates-io 0.13.0", "crossbeam 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -204,45 +194,66 @@ dependencies = [ "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "home 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ignore 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jobserver 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "psapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cargo_metadata" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cargo_metadata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cargotest" version = "0.1.0" dependencies = [ - "cargo 0.23.0", - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo 0.24.0", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -251,7 +262,7 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -261,103 +272,163 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.26.2" +version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clippy" +version = "0.0.174" +dependencies = [ + "cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy-mini-macro-test 0.1.0", + "clippy_lints 0.0.174", + "compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "duct 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clippy-mini-macro-test" +version = "0.1.0" + +[[package]] +name = "clippy_lints" +version = "0.0.174" +dependencies = [ + "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cmake" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "collections" -version = "0.0.0" +name = "coco" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "alloc 0.0.0", - "core 0.0.0", + "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "commoncrypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "commoncrypto-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compiler_builtins" version = "0.0.0" dependencies = [ + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compiletest" version = "0.0.0" dependencies = [ - "diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "completion" -version = "0.1.0" - -[[package]] -name = "conv" +name = "compiletest_rs" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "completion" +version = "0.1.0" + [[package]] name = "core" version = "0.0.0" -dependencies = [ - "rand 0.0.0", -] [[package]] name = "core-foundation" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crates-io" -version = "0.12.0" +version = "0.13.0" dependencies = [ "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -370,6 +441,18 @@ name = "crossbeam" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crypto-hash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cssparser" version = "0.13.7" @@ -378,7 +461,7 @@ dependencies = [ "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "procedural-masquerade 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -389,7 +472,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "procedural-masquerade 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -400,10 +483,10 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -412,20 +495,15 @@ name = "curl-sys" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libz-sys 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "custom_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "dbghelp-sys" version = "0.2.0" @@ -443,29 +521,41 @@ dependencies = [ "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "deglob" +version = "0.1.0" + [[package]] name = "derive-new" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diff" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dlmalloc" +version = "0.0.0" +dependencies = [ + "alloc 0.0.0", + "core 0.0.0", +] + [[package]] name = "docopt" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -474,6 +564,22 @@ name = "dtoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "duct" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shared_child 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "enum_primitive" version = "0.1.1" @@ -502,10 +608,10 @@ dependencies = [ [[package]] name = "error-chain" -version = "0.10.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -513,7 +619,7 @@ name = "error-chain" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -523,19 +629,20 @@ dependencies = [ "rustdoc 0.0.0", ] +[[package]] +name = "features" +version = "0.1.0" + [[package]] name = "filetime" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "find_all_refs" -version = "0.1.0" - [[package]] name = "find_all_refs_no_cfg_test" version = "0.1.0" @@ -549,7 +656,7 @@ name = "flate2" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -559,12 +666,20 @@ version = "0.0.0" [[package]] name = "fnv" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foreign-types" -version = "0.2.0" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -573,10 +688,26 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fuchsia-zircon" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "futf" version = "0.1.3" @@ -588,12 +719,7 @@ dependencies = [ [[package]] name = "futures" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gcc" -version = "0.3.54" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -607,11 +733,11 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -622,7 +748,7 @@ dependencies = [ "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -632,20 +758,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "globset" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "goto_def" -version = "0.1.0" - [[package]] name = "graphviz" version = "0.0.0" @@ -661,16 +783,16 @@ dependencies = [ [[package]] name = "handlebars" -version = "0.27.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -678,10 +800,6 @@ name = "hex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "highlight" -version = "0.1.0" - [[package]] name = "home" version = "0.3.0" @@ -694,26 +812,22 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hover" -version = "0.1.0" - [[package]] name = "html-diff" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kuchiki 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kuchiki 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "html5ever" -version = "0.18.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "markup5ever 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "markup5ever 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -728,16 +842,21 @@ dependencies = [ "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "if_chain" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ignore" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -759,17 +878,25 @@ version = "0.1.0" name = "installer" version = "0.0.0" dependencies = [ - "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "xz2 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itertools" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.3.4" @@ -777,11 +904,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jobserver" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -789,11 +916,11 @@ name = "jsonrpc-core" version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -807,31 +934,36 @@ dependencies = [ [[package]] name = "kuchiki" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "languageserver-types" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" -version = "0.2.8" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -843,21 +975,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.31" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libgit2-sys" -version = "0.6.15" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libz-sys 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -866,20 +998,20 @@ name = "libssh2-sys" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "libz-sys 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libz-sys" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -898,7 +1030,7 @@ name = "log_settings" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -906,9 +1038,9 @@ name = "lzma-sys" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -917,26 +1049,9 @@ name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "magenta" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "magenta-sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "markup5ever" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -944,7 +1059,7 @@ dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tendril 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -954,21 +1069,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mdbook" -version = "0.0.25" +version = "0.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "handlebars 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -978,15 +1093,23 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -994,8 +1117,8 @@ name = "miniz-sys" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1020,11 +1143,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nix" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num" version = "0.1.40" @@ -1034,7 +1168,7 @@ dependencies = [ "num-complex 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1045,7 +1179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1077,7 +1211,7 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.1.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-bigint 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1093,16 +1227,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "omit_init_build" -version = "0.1.0" - [[package]] name = "open" version = "1.2.1" @@ -1110,14 +1240,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.9.19" +version = "0.9.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1127,15 +1257,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.19" +version = "0.9.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "os_pipe" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "owning_ref" version = "0.3.3" @@ -1164,7 +1304,7 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1195,7 +1335,7 @@ version = "0.7.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1213,7 +1353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "precomputed-hash" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1227,14 +1367,14 @@ dependencies = [ [[package]] name = "procedural-masquerade" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "profiler_builtins" version = "0.0.0" dependencies = [ - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1249,10 +1389,19 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.0.14" +version = "0.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pulldown-cmark" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1262,8 +1411,8 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "quote" -version = "0.2.3" +name = "quine-mc_cluskey" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1273,38 +1422,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "racer" -version = "2.0.10" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.0.0" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core 0.0.0", + "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand" -version = "0.3.16" +name = "rayon" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.31" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "reformat" @@ -1332,7 +1503,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1356,54 +1527,51 @@ version = "0.1.0" name = "remote-test-server" version = "0.1.0" -[[package]] -name = "rename" -version = "0.1.0" - [[package]] name = "rls" -version = "0.122.0" +version = "0.124.0" dependencies = [ - "cargo 0.23.0", + "cargo 0.24.0", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "languageserver-types 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "languageserver-types 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "racer 2.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-analysis 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-data 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "racer 2.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-analysis 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-data 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-rustc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-vfs 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustfmt-nightly 0.2.7", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustfmt-nightly 0.2.16", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rls-analysis" -version = "0.6.8" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "derive-new 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive-new 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-data 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-data 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rls-data" -version = "0.10.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1417,8 +1585,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1426,7 +1594,7 @@ name = "rls-vfs" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "racer 2.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "racer 2.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1434,8 +1602,8 @@ dependencies = [ name = "rustbook" version = "0.1.0" dependencies = [ - "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mdbook 0.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mdbook 0.0.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1443,18 +1611,19 @@ name = "rustc" version = "0.0.0" dependencies = [ "arena 0.0.0", - "backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "fmt_macros 0.0.0", "graphviz 0.0.0", - "jobserver 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_apfloat 0.0.0", "rustc_back 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", @@ -1496,7 +1665,7 @@ dependencies = [ name = "rustc_apfloat" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", ] @@ -1507,7 +1676,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1516,10 +1685,20 @@ name = "rustc_back" version = "0.0.0" dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", "syntax 0.0.0", ] +[[package]] +name = "rustc_binaryen" +version = "0.0.0" +dependencies = [ + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc_borrowck" version = "0.0.0" @@ -1540,7 +1719,6 @@ dependencies = [ "arena 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_back 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -1561,7 +1739,7 @@ dependencies = [ name = "rustc_cratesio_shim" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1576,7 +1754,7 @@ dependencies = [ name = "rustc_driver" version = "0.0.0" dependencies = [ - "ar 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "arena 0.0.0", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", @@ -1611,6 +1789,7 @@ dependencies = [ name = "rustc_errors" version = "0.0.0" dependencies = [ + "rustc_data_structures 0.0.0", "serialize 0.0.0", "syntax_pos 0.0.0", ] @@ -1621,6 +1800,7 @@ version = "0.0.0" dependencies = [ "graphviz 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_data_structures 0.0.0", "serialize 0.0.0", @@ -1634,7 +1814,6 @@ version = "0.0.0" dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_back 0.0.0", "rustc_const_eval 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", @@ -1644,9 +1823,9 @@ dependencies = [ name = "rustc_llvm" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "build_helper 0.1.0", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", ] @@ -1657,7 +1836,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1683,7 +1862,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", @@ -1691,6 +1870,7 @@ dependencies = [ "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", + "serialize 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] @@ -1702,7 +1882,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1728,7 +1908,6 @@ name = "rustc_plugin" version = "0.0.0" dependencies = [ "rustc 0.0.0", - "rustc_back 0.0.0", "rustc_errors 0.0.0", "rustc_metadata 0.0.0", "syntax 0.0.0", @@ -1752,6 +1931,7 @@ dependencies = [ "arena 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", + "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", @@ -1762,7 +1942,7 @@ name = "rustc_save_analysis" version = "0.0.0" dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-data 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-data 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1776,17 +1956,19 @@ dependencies = [ name = "rustc_trans" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "jobserver 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", + "rustc_apfloat 0.0.0", "rustc_back 0.0.0", + "rustc_binaryen 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -1797,18 +1979,20 @@ dependencies = [ "serialize 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc_trans_utils" version = "0.0.0" dependencies = [ - "ar 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", + "rustc_data_structures 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] @@ -1820,7 +2004,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1832,7 +2016,6 @@ dependencies = [ "fmt_macros 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc_back 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -1846,11 +2029,11 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html-diff 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "html-diff 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1862,19 +2045,21 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "0.2.7" +version = "0.2.16" dependencies = [ - "diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive-new 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "strings 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "strings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1900,6 +2085,11 @@ name = "scopeguard" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "selectors" version = "0.18.0" @@ -1907,21 +2097,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1931,22 +2129,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.15" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.15" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1958,24 +2156,34 @@ name = "serde_ignored" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serialize" version = "0.0.0" +[[package]] +name = "shared_child" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "shell-escape" version = "0.1.3" @@ -1993,12 +2201,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "socket2" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2016,15 +2224,13 @@ dependencies = [ "alloc_jemalloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "collections 0.0.0", "compiler_builtins 0.0.0", "core 0.0.0", "libc 0.0.0", "panic_abort 0.0.0", "panic_unwind 0.0.0", "profiler_builtins 0.0.0", - "rand 0.0.0", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_asan 0.0.0", "rustc_lsan 0.0.0", "rustc_msan 0.0.0", @@ -2046,10 +2252,10 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2072,7 +2278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strings" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2083,15 +2289,6 @@ name = "strsim" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "syn" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "syn" version = "0.11.11" @@ -2114,7 +2311,7 @@ dependencies = [ name = "syntax" version = "0.0.0" dependencies = [ - "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", "rustc_data_structures 0.0.0", @@ -2140,6 +2337,7 @@ version = "0.0.0" dependencies = [ "rustc_data_structures 0.0.0", "serialize 0.0.0", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2147,7 +2345,7 @@ name = "syntex_errors" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_pos 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2169,7 +2367,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2180,11 +2378,12 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "xattr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2193,12 +2392,12 @@ name = "tempdir" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tendril" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futf 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2220,21 +2419,21 @@ dependencies = [ ] [[package]] -name = "term_size" -version = "0.3.0" +name = "termcolor" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "termcolor" -version = "0.3.3" +name = "termion" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2247,10 +2446,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2260,7 +2458,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2276,7 +2474,7 @@ name = "thread_local" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2297,14 +2495,9 @@ name = "toml" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "typed-arena" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-bidi" version = "0.3.4" @@ -2338,10 +2531,6 @@ name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicødë" -version = "0.1.0" - [[package]] name = "unreachable" version = "0.1.1" @@ -2375,12 +2564,12 @@ dependencies = [ [[package]] name = "url" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2388,8 +2577,8 @@ name = "url_serde" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2463,6 +2652,10 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "workspace_symbol" +version = "0.1.0" + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2477,7 +2670,7 @@ name = "xattr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2497,124 +2690,140 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" -"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" -"checksum ar 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b24e4eef8e3fa7e2ca75b157e6039cdf8d9d3a68213ddc19d0fd9d576b9717c9" -"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" -"checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983" -"checksum backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c63ea141ef8fdb10409d0f5daf30ac51f84ef43bff66f16627773d2a292cd189" +"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" +"checksum ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35c7a5669cb64f085739387e1308b74e6d44022464b7f1b63bbd4ceb6379ec31" +"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" +"checksum backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8709cc7ec06f6f0ae6c2c7e12f6ed41540781f72b488d83734978295ceae182e" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" -"checksum cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1" +"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b" +"checksum cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f56ec3e469bca7c276f2eea015aa05c5e381356febdbb0683c2580189604537" +"checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3451e409013178663435d6f15fdb212f14ee4424a3d74f979d081d0a66b6f1f2" -"checksum cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "357c07e7a1fc95732793c1edb5901e1a1f305cfcf63a90eb12dbd22bdb6b789d" -"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" -"checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7" -"checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387" +"checksum clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc34bf7d5d66268b466b9852bca925ec1d2650654dab4da081e63fd230145c2e" +"checksum cmake 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "e14cd15a7cbc2c6a905677e54b831ee91af2ff43b352010f6133236463b65cac" +"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" +"checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" +"checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" +"checksum compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "562bafeec9aef1e3e08f1c5b0c542220bb80ff2894e5373a1f9d17c346412c66" +"checksum core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8047f547cd6856d45b1cdd75ef8d2f21f3d0e4bf1dab0a0041b0ae9a5dda9c0e" +"checksum core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "152195421a2e6497a8179195672e9d4ee8e45ed8c465b626f1606d27a08ebcd5" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum crossbeam 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8837ab96533202c5b610ed44bc7f4183e7957c1c8f56e8cc78bb098593c8ba0a" +"checksum crypto-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34903878eec1694faf53cae8473a088df333181de421d4d3d48061d6559fe602" "checksum cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef6124306e5ebc5ab11891d063aeafdd0cdc308079b708c8b566125f3680292b" "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df" "checksum curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7034c534a1d7d22f7971d6088aa9d281d219ef724026c3428092500f41ae9c2c" "checksum curl-sys 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4bee31aa3a079d5f3ff9579ea4dcfb1b1a17a40886f5f467436d383e78134b55" -"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3" -"checksum derive-new 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41be6ca3b99e0c0483fb2389685448f650459c3ecbe4e18d7705d8010ec4ab8e" -"checksum diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0a515461b6c8c08419850ced27bc29e86166dcdcde8fbe76f8b1f0589bb49472" +"checksum derive-new 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "415f627ab054041c3eb748c2e1da0ef751989f5f0c386b63a098e545854a98ba" +"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" "checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum duct 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e45aa15fe0a8a8f511e6d834626afd55e49b62e5c8802e18328a87e8a8f6065c" +"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" -"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" -"checksum filetime 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "6ab199bf38537c6f38792669e081e0bb278b9b7405bba2642e4e5d15bf732c0e" +"checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" +"checksum filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "aa75ec8f7927063335a9583e7fa87b0110bb888cf766dc01b54c0ff70d760c8e" "checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" -"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" -"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866" +"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" +"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" "checksum futf 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "51f93f3de6ba1794dcd5810b3546d004600a59a98266487c8407bc4b24e398f3" -"checksum futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "05a23db7bd162d4e8265968602930c476f688f0c180b44bdaf55e0cb2c687558" -"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" "checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9" "checksum git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1c0203d653f4140241da0c1375a404f0a397249ec818cd2076c6280c50f6fa" "checksum git2-curl 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68676bc784bf0bef83278898929bf64a251e87c0340723d0b93fa096c9c5bf8e" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum globset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "feeb1b6840809ef5efcf7a4a990bc4e1b7ee3df8cf9e2379a75aeb2ba42ac9c3" +"checksum globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "464627f948c3190ae3d04b1bc6d7dca2f785bda0ac01278e6db129ad383dbeb6" "checksum hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf088f042a467089e9baa4972f57f9247e42a0cc549ba264c7a04fbb8ecb89d4" -"checksum handlebars 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef7567daf271a32e60301e4821fcb5b51a5b535167115d1ce04f46c3f0a15f0b" +"checksum handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04af2006ea09d985fef82b81e0eb25337e51b691c76403332378a53d521edc" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" "checksum home 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f25ae61099d8f3fee8b483df0bd4ecccf4b2731897aad40d50eca1b641fe6db" -"checksum html-diff 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5298d63081a642508fce965740ddb03a386c5d81bf1fef0579a815cf49cb8c68" -"checksum html5ever 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a49d5001dd1bddf042ea41ed4e0a671d50b1bf187e66b349d7ec613bdce4ad90" +"checksum html-diff 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9778743e3b3c3679f471f0ed1833c690f19f4a0919e33b281f12ef5f77ad64c6" +"checksum html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bfb46978eb757a603b7dfe2dafb1c62cb4dee3428d8ac1de734d83d6b022d06" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" "checksum ignore 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3fcaf2365eb14b28ec7603c98c06cc531f19de9eb283d89a3dff8417c8c99f5" +"checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" -"checksum jobserver 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "443ae8bc0af6c106e6e8b77e04684faecc1a5ce94e058f4c2b0a037b0ea1b133" +"checksum jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "931b04e5e57d88cc909528f0d701db36a870b72a052648ded8baf80f9f445e0f" "checksum jsonrpc-core 7.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1acd0f9934da94466d2370f36832b9b19271b4abdfdb5e69f0bcd991ebcd515" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kuchiki 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef2ea4f2f7883cd7c6772b06c14abca01a2cc1f75c426cebffcf6b3b925ef9fc" -"checksum languageserver-types 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d52e477b23bf52cd3ca0f9fc6c5d14be954eec97e3b9cdfbd962d911bd533caf" -"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "d1419b2939a0bc44b77feb34661583c7546b532b192feab36249ab584b86856c" -"checksum libgit2-sys 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "205fc37e829c5b36de63d14c8dc8b62c5a6a2519b16318ed0977079ca97256a9" +"checksum kuchiki 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e03098e8e719c92b7794515dfd5c1724e2b12f5ce1788e61cfa4663f82eba8d8" +"checksum languageserver-types 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7878a264bff274f017fac3dff1fa17afb9e6320738f91331ebfde14ab2bc734" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" +"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" +"checksum libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "6f74b4959cef96898f5123148724fc7dee043b9a6b99f219d948851bfbe53cb2" "checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" -"checksum libz-sys 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "44ebbc760fd2d2f4d93de09a0e13d97e057612052e871da9985cedcb451e6bd5" +"checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum lzma-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c1b93b78f89e8737dac81837fc8f5521ac162abcba902e1a3db949d55346d1da" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -"checksum magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf0336886480e671965f794bc9b6fce88503563013d1bfb7a502c81fe3ac527" -"checksum magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40d014c7011ac470ae28e2f76a02bfea4a8480f73e701353b49ad7a8d75f4699" -"checksum markup5ever 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff834ac7123c6a37826747e5ca09db41fd7a83126792021c2e636ad174bb77d3" +"checksum markup5ever 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "047150a0e03b57e638fc45af33a0b63a0362305d5b9f92ecef81df472a4cceb0" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" -"checksum mdbook 0.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "146eadfc6d141452a364c351f07bb19208d1401e931f40b8532f87bba3ecc40f" +"checksum mdbook 0.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1ac668292d1e5c7b1c6fd64f70d3a85105b8069a89558a0d67bdb2ff298ca1" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" +"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" +"checksum memchr 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" "checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" +"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" "checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525" "checksum num-bigint 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "8fd0f8dbb4c0960998958a796281d88c16fbe68d87b1baa6f31e2979e81fd0bd" "checksum num-complex 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "503e668405c5492d67cf662a81e05be40efe2e6bcf10f7794a07bd9865e704e6" "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" "checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" -"checksum num-rational 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "288629c76fac4b33556f4b7ab57ba21ae202da65ba8b77466e6d598e31990790" +"checksum num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "0c7cb72a95250d8a370105c828f388932373e0e94414919891a0f945222310fe" "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" -"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" +"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" "checksum open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c281318d992e4432cfa799969467003d05921582a7489a8325e37f8a450d5113" -"checksum openssl 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)" = "816914b22eb15671d62c73442a51978f311e911d6a6f6cbdafa6abce1b5038fc" +"checksum openssl 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "419ef26bb651d72b6c5a603bcc4e4856a362460e62352dfffa53de91d2e81181" "checksum openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98df0270d404ccd3c050a41d579c52d1db15375168bb3471e04ec0f5f378daf" -"checksum openssl-sys 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4c63a7d559c1e5afa6d6a9e6fa34bbc5f800ffc9ae08b72c605420b0c4f5e8" +"checksum openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5483bdc56756041ba6aa37c9cb59cc2219f012a2a1377d97ad35556ac6676ee7" +"checksum os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "998bfbb3042e715190fe2a41abfa047d7e8cb81374d2977d7f100eacd8619cb1" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" -"checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150" -"checksum procedural-masquerade 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c93cdc1fb30af9ddf3debc4afbdb0f35126cbd99daa229dd76cdd5349b41d989" +"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +"checksum procedural-masquerade 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1bcafee1590f81acb329ae45ec627b318123f085153913620316ae9a144b2a" "checksum psapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abcd5d1a07d360e29727f757a9decb3ce8bc6e0efa8969cfaad669a8317a2478" -"checksum pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ab1e588ef8efd702c7ed9d2bd774db5e6f4d878bb5a1a9f371828fbdff6973" +"checksum pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "378e941dbd392c101f2cb88097fa4d7167bc421d4b88de3ff7dbee503bc3233b" +"checksum pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a656fdb8b6848f896df5e478a0eb9083681663e37dcb77dd16981ff65329fe8b" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" -"checksum quote 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5cf478fe1006dbcc72567121d23dbdae5f1632386068c5c86ff4f645628504" +"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum racer 2.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "f120c7510ef7aff254aeb06067fb6fac573ec96a1660e194787cf9dced412bf0" -"checksum rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "eb250fd207a4729c976794d03db689c9be1d634ab5a1c9da9492a13d8fecbcdf" -"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" +"checksum racer 2.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "034f1c4528581c40a60e96875467c03315868084e08ff4ceb46a00f7be3b16b4" +"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" +"checksum rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed02d09394c94ffbdfdc755ad62a132e94c3224a8354e78a1200ced34df12edf" +"checksum rayon-core 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b609139d83da75902f88fd6c01820046840a18471e4dfcd5ac7c0f46bea53" +"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" -"checksum rls-analysis 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fa390bdc70b0a90d07d9cd5c6989ba5fca2d59728903919ebda1a1b2037b18d7" -"checksum rls-data 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11d339f1888e33e74d8032de0f83c40b2bdaaaf04a8cfc03b32186c3481fb534" +"checksum rls-analysis 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "65e4806e68b62c0c8e45b0f71431b57889d044cf921d0061fa7d65e7af12048d" +"checksum rls-data 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff85bb3a0daf9f64207a5530d90ae1c10f5515cef064c88b6821090678382b44" "checksum rls-rustc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b21ea952e9bf1569929abf1bb920262cde04b7b1b26d8e0260286302807299d2" "checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a" "checksum rls-vfs 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd34691a510938bb67fe0444fb363103c73ffb31c121d1e16bc92d8945ea8ff" @@ -2623,43 +2832,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59a076157c1e2dc561d8de585151ee6965d910dd4dcb5dabb7ae3e83981a6c57" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum selectors 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3c89b1c6a3c029c82263f7dd2d44d0005ee7374eb09e254ab59dede4353a8c0" +"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "6a7046c9d4c6c522d10b2d098f9bebe2bef227e0e74044d8c1bfcf6b476af799" -"checksum serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1afcaae083fd1c46952a315062326bc9957f182358eb7da03b57ef1c688f7aa9" -"checksum serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd381f6d01a6616cdba8530492d453b7761b456ba974e98768a18cad2cd76f58" +"checksum serde 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fcd35da6125742035aeb2c17ed4b49b83ba4b24156400b8195e5d4f6357ff144" +"checksum serde_derive 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d0a8327f3bcb285ac0f8763a9072067e7600f873d9edbc3bf40da0e2e7e9c870" +"checksum serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32f1926285523b2db55df263d2aa4eb69ddcfa7a7eade6430323637866b513ab" "checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" -"checksum serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d243424e06f9f9c39e3cd36147470fd340db785825e367625f79298a6ac6b7ac" +"checksum serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e4586746d1974a030c48919731ecffd0ed28d0c40749d0d18d43b3a7d6c9b20e" +"checksum shared_child 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "099b38928dbe4a0a01fcd8c233183072f14a7d126a34bed05880869be66e14cc" "checksum shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5cc96481d54583947bfe88bf30c23d53f883c6cd0145368b69989d97b84ef8" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e" -"checksum socket2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e76b159741052c7deaa9fd0b5ca6b5f79cecf525ed665abfe5002086c6b2791" +"checksum socket2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b4896961171cd3317c7e9603d88f379f8c6e45342212235d356496680c68fd" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum string_cache 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "413fc7852aeeb5472f1986ef755f561ddf0c789d3d796e65f0b6fe293ecd4ef8" "checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" -"checksum strings 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da75d8bf2c4d210d63dd09581a041b036001f9f6e03d9b151dbff810fb7ba26a" +"checksum strings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa481ee1bc42fc3df8195f91f7cb43cf8f2b71b48bac40bf5381cfaf7e481f3c" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6ae6fb0dcc9bd85f89a1a4adc0df2fd90c90c98849d61433983dd7a9df6363f7" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e52bffe6202cfb67587784cf23e0ec5bf26d331eef4922a16d5c42e12aa1e9b" "checksum syntex_pos 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "955ef4b16af4c468e4680d1497f873ff288f557d338180649e18f915af5e15ac" "checksum syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76a302e717e348aa372ff577791c3832395650073b8d8432f8b3cb170b34afde" -"checksum tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "281285b717926caa919ad905ef89c63d75805c7d89437fb873100925a53f2b1b" +"checksum tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1605d3388ceb50252952ffebab4b5dc43017ead7e4481b175961c283bb951195" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" -"checksum tendril 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1b72f8e2f5b73b65c315b1a70c730f24b9d7a25f39e98de8acbe2bb795caea" +"checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" "checksum termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9065bced9c3e43453aa3d56f1e98590b8455b341d2fa191a1090c0dd0b242c75" -"checksum textwrap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8e08afc40ae3459e4838f303e465aa50d823df8d7f83ca88108f6d3afe7edd" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" "checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5934776c3ac1bea4a9d56620d6bf2d483b20d394e49581db40f187e1118ff667" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" @@ -2668,7 +2878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb819346883532a271eb626deb43c4a1bb4c4dd47c519bd78137c3e72a4fe27" +"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" "checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" "checksum userenv-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d28ea36bbd9192d75bd9fa9b39f96ddb986eaee824adae5d53b6e51919b2f3" "checksum utf-8 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f923c601c7ac48ef1d66f7d5b5b2d9a7ba9c51333ab75a3ddf8d0309185a56" diff --git a/src/Cargo.toml b/src/Cargo.toml index f4b4189e01f06..abe1fe5a6de01 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -5,6 +5,7 @@ members = [ "libstd", "libtest", "tools/cargotest", + "tools/clippy", "tools/compiletest", "tools/error_index_generator", "tools/linkchecker", @@ -20,25 +21,19 @@ members = [ "tools/rls", "tools/rustfmt", # FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude + "tools/rls/test_data/bin_lib", "tools/rls/test_data/borrow_error", - "tools/rls/test_data/completion", - "tools/rls/test_data/find_all_refs", + "tools/rls/test_data/common", + "tools/rls/test_data/deglob", + "tools/rls/test_data/features", "tools/rls/test_data/find_all_refs_no_cfg_test", - "tools/rls/test_data/goto_def", - "tools/rls/test_data/highlight", - "tools/rls/test_data/hover", - "tools/rls/test_data/rename", - "tools/rls/test_data/reformat", - "tools/rls/test_data/bin_lib_no_cfg_test", - "tools/rls/test_data/multiple_bins", - "tools/rls/test_data/bin_lib", - "tools/rls/test_data/reformat_with_range", "tools/rls/test_data/find_impls", "tools/rls/test_data/infer_bin", "tools/rls/test_data/infer_custom_bin", "tools/rls/test_data/infer_lib", - "tools/rls/test_data/omit_init_build", - "tools/rls/test_data/unicødë", + "tools/rls/test_data/multiple_bins", + "tools/rls/test_data/reformat", + "tools/rls/test_data/reformat_with_range", "tools/rls/test_data/workspace_symbol", ] diff --git a/src/binaryen b/src/binaryen new file mode 160000 index 0000000000000..1c9bf65aa0e37 --- /dev/null +++ b/src/binaryen @@ -0,0 +1 @@ +Subproject commit 1c9bf65aa0e371b84755a8ddd6e79497fac57171 diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 3f1d03b187203..bbbbf0e191555 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -34,7 +34,7 @@ cmake = "0.1.23" filetime = "0.1" num_cpus = "1.0" getopts = "0.2" -cc = "1.0" +cc = "1.0.1" libc = "0.2" serde = "1.0.8" serde_derive = "1.0.8" diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index e543b8c070bcc..9ff681ac68087 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -39,7 +39,7 @@ The script accepts commands, flags, and arguments to determine what to do: ``` If files are dirty that would normally be rebuilt from stage 0, that can be - overidden using `--keep-stage 0`. Using `--keep-stage n` will skip all steps + overridden using `--keep-stage 0`. Using `--keep-stage n` will skip all steps that belong to stage n or earlier: ``` @@ -126,17 +126,17 @@ install a nightly, presumably using `rustup`. You will then want to configure your directory to use this build, like so: ``` -# configure to use local rust instead of downloding a beta. +# configure to use local rust instead of downloading a beta. # `--local-rust-root` is optional here. If elided, we will # use whatever rustc we find on your PATH. -> configure --enable-rustbuild --local-rust-root=~/.cargo/ --enable-local-rebuild +> ./configure --local-rust-root=~/.cargo/ --enable-local-rebuild ``` After that, you can use the `--incremental` flag to actually do incremental builds: ``` -> ../x.py build --incremental +> ./x.py build --incremental ``` The `--incremental` flag will store incremental compilation artifacts diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 848b10d312cea..631c9f72f3500 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -31,8 +31,6 @@ extern crate bootstrap; use std::env; use std::ffi::OsString; -use std::io; -use std::io::prelude::*; use std::str::FromStr; use std::path::PathBuf; use std::process::{Command, ExitStatus}; @@ -122,19 +120,14 @@ fn main() { cmd.arg("-L").arg(&root); } - // Pass down extra flags, commonly used to configure `-Clinker` when - // cross compiling. - if let Ok(s) = env::var("RUSTC_FLAGS") { - cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::>()); + // Override linker if necessary. + if let Ok(target_linker) = env::var("RUSTC_TARGET_LINKER") { + cmd.arg(format!("-Clinker={}", target_linker)); } // Pass down incremental directory, if any. if let Ok(dir) = env::var("RUSTC_INCREMENTAL") { cmd.arg(format!("-Zincremental={}", dir)); - - if verbose > 0 { - cmd.arg("-Zincremental-info"); - } } let crate_name = args.windows(2) @@ -182,12 +175,16 @@ fn main() { if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") { cmd.arg("-C").arg(format!("codegen-units={}", s)); } + if env::var("RUSTC_THINLTO").is_ok() { + cmd.arg("-Ccodegen-units=16").arg("-Zthinlto"); + } // Emit save-analysis info. if env::var("RUSTC_SAVE_ANALYSIS") == Ok("api".to_string()) { cmd.arg("-Zsave-analysis"); cmd.env("RUST_SAVE_ANALYSIS_CONFIG", - "{\"output_file\": null,\"full_docs\": false,\"pub_only\": true,\ + "{\"output_file\": null,\"full_docs\": false,\ + \"pub_only\": true,\"reachable_only\": false,\ \"distro_crate\": true,\"signatures\": false,\"borrow_data\": false}"); } @@ -258,6 +255,11 @@ fn main() { if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() { cmd.arg("-Z").arg("force-unstable-if-unmarked"); } + } else { + // Override linker if necessary. + if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") { + cmd.arg(format!("-Clinker={}", host_linker)); + } } let color = match env::var("RUSTC_COLOR") { @@ -270,7 +272,7 @@ fn main() { } if verbose > 1 { - writeln!(&mut io::stderr(), "rustc command: {:?}", cmd).unwrap(); + eprintln!("rustc command: {:?}", cmd); } // Actually run the compiler! diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index d7d72d5dd56c9..4e975adc9721c 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -47,6 +47,17 @@ fn main() { if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() { cmd.arg("-Z").arg("force-unstable-if-unmarked"); } + if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") { + cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options"); + } + + // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick + // it up so we can make rustdoc print this into the docs + if let Some(version) = env::var_os("RUSTDOC_CRATE_VERSION") { + // This "unstable-options" can be removed when `--crate-version` is stabilized + cmd.arg("-Z").arg("unstable-options") + .arg("--crate-version").arg(version); + } std::process::exit(match cmd.status() { Ok(s) => s.code().unwrap_or(1), diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 4a8c3dcebcb49..707aceebb1eda 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -8,7 +8,7 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. -from __future__ import print_function +from __future__ import absolute_import, division, print_function import argparse import contextlib import datetime @@ -294,7 +294,7 @@ def default_build_triple(): raise ValueError('unknown byteorder: {}'.format(sys.byteorder)) # only the n64 ABI is supported, indicate it ostype += 'abi64' - elif cputype == 'sparcv9': + elif cputype == 'sparcv9' or cputype == 'sparc64': pass else: err = "unknown cpu type: {}".format(cputype) @@ -302,6 +302,7 @@ def default_build_triple(): return "{}-{}".format(cputype, ostype) + class RustBuild(object): """Provide all the methods required to build Rust""" def __init__(self): @@ -498,7 +499,7 @@ def get_toml(self, key): If the key does not exists, the result is None: - >>> rb.get_toml("key3") == None + >>> rb.get_toml("key3") is None True """ for line in self.config_toml.splitlines(): @@ -531,7 +532,7 @@ def program_config(self, program): """ config = self.get_toml(program) if config: - return config + return os.path.expanduser(config) return os.path.join(self.bin_root(), "bin", "{}{}".format( program, self.exe_suffix())) @@ -647,7 +648,8 @@ def update_submodules(self): if not ((module.endswith("llvm") and self.get_toml('llvm-config')) or (module.endswith("jemalloc") and - self.get_toml('jemalloc')))] + (self.get_toml('use-jemalloc') == "false" or + self.get_toml('jemalloc'))))] run(["git", "submodule", "update", "--init", "--recursive"] + submodules, cwd=self.rust_root, verbose=self.verbose) diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 32ea4b4abe638..4db7e2ec016f0 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -10,6 +10,7 @@ """Bootstrap tests""" +from __future__ import absolute_import, division, print_function import os import doctest import unittest diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index ffd959d86f580..dcffc83c4b6f6 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -261,9 +261,10 @@ impl<'a> Builder<'a> { doc::Reference, doc::Rustdoc, doc::CargoBook), Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts, dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo, - dist::Rls, dist::Extended, dist::HashSign, dist::DontDistWithMiriEnabled), + dist::Rls, dist::Rustfmt, dist::Extended, dist::HashSign, + dist::DontDistWithMiriEnabled), Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls, - install::Analysis, install::Src, install::Rustc), + install::Rustfmt, install::Analysis, install::Src, install::Rustc), } } @@ -306,7 +307,7 @@ impl<'a> Builder<'a> { Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), - Subcommand::Clean => panic!(), + Subcommand::Clean { .. } => panic!(), }; let builder = Builder { @@ -413,12 +414,15 @@ impl<'a> Builder<'a> { pub fn rustdoc_cmd(&self, host: Interned) -> Command { let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc")); let compiler = self.compiler(self.top_stage, host); - cmd - .env("RUSTC_STAGE", compiler.stage.to_string()) - .env("RUSTC_SYSROOT", self.sysroot(compiler)) - .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build)) - .env("CFG_RELEASE_CHANNEL", &self.build.config.channel) - .env("RUSTDOC_REAL", self.rustdoc(host)); + cmd.env("RUSTC_STAGE", compiler.stage.to_string()) + .env("RUSTC_SYSROOT", self.sysroot(compiler)) + .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build)) + .env("CFG_RELEASE_CHANNEL", &self.build.config.channel) + .env("RUSTDOC_REAL", self.rustdoc(host)) + .env("RUSTDOC_CRATE_VERSION", self.build.rust_version()); + if let Some(linker) = self.build.linker(host) { + cmd.env("RUSTC_TARGET_LINKER", linker); + } cmd } @@ -468,8 +472,6 @@ impl<'a> Builder<'a> { .env("RUSTC", self.out.join("bootstrap/debug/rustc")) .env("RUSTC_REAL", self.rustc(compiler)) .env("RUSTC_STAGE", stage.to_string()) - .env("RUSTC_CODEGEN_UNITS", - self.config.rust_codegen_units.to_string()) .env("RUSTC_DEBUG_ASSERTIONS", self.config.rust_debug_assertions.to_string()) .env("RUSTC_SYSROOT", self.sysroot(compiler)) @@ -481,15 +483,23 @@ impl<'a> Builder<'a> { } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") }) - .env("TEST_MIRI", self.config.test_miri.to_string()) - .env("RUSTC_FLAGS", self.rustc_flags(target).join(" ")); + .env("TEST_MIRI", self.config.test_miri.to_string()); + + if let Some(n) = self.config.rust_codegen_units { + cargo.env("RUSTC_CODEGEN_UNITS", n.to_string()); + } + + if let Some(host_linker) = self.build.linker(compiler.host) { + cargo.env("RUSTC_HOST_LINKER", host_linker); + } + if let Some(target_linker) = self.build.linker(target) { + cargo.env("RUSTC_TARGET_LINKER", target_linker); + } + cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string()) + .env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string()); if mode != Mode::Tool { - // Tools don't get debuginfo right now, e.g. cargo and rls don't - // get compiled with debuginfo. - cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string()) - .env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string()) - .env("RUSTC_FORCE_UNSTABLE", "1"); + cargo.env("RUSTC_FORCE_UNSTABLE", "1"); // Currently the compiler depends on crates from crates.io, and // then other crates can depend on the compiler (e.g. proc-macro @@ -556,17 +566,35 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity)); - // Specify some various options for build scripts used throughout - // the build. + // Throughout the build Cargo can execute a number of build scripts + // compiling C/C++ code and we need to pass compilers, archivers, flags, etc + // obtained previously to those build scripts. + // Build scripts use either the `cc` crate or `configure/make` so we pass + // the options through environment variables that are fetched and understood by both. // // FIXME: the guard against msvc shouldn't need to be here if !target.contains("msvc") { - cargo.env(format!("CC_{}", target), self.cc(target)) - .env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None - .env(format!("CFLAGS_{}", target), self.cflags(target).join(" ")); + let cc = self.cc(target); + cargo.env(format!("CC_{}", target), cc) + .env("CC", cc); + + let cflags = self.cflags(target).join(" "); + cargo.env(format!("CFLAGS_{}", target), cflags.clone()) + .env("CFLAGS", cflags.clone()); + + if let Some(ar) = self.ar(target) { + let ranlib = format!("{} s", ar.display()); + cargo.env(format!("AR_{}", target), ar) + .env("AR", ar) + .env(format!("RANLIB_{}", target), ranlib.clone()) + .env("RANLIB", ranlib); + } if let Ok(cxx) = self.cxx(target) { - cargo.env(format!("CXX_{}", target), cxx); + cargo.env(format!("CXX_{}", target), cxx) + .env("CXX", cxx) + .env(format!("CXXFLAGS_{}", target), cflags.clone()) + .env("CXXFLAGS", cflags); } } @@ -574,6 +602,9 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_SAVE_ANALYSIS", "api".to_string()); } + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs + cargo.env("RUSTDOC_CRATE_VERSION", self.build.rust_version()); + // Environment variables *required* throughout the build // // FIXME: should update code to not require this env var @@ -582,12 +613,20 @@ impl<'a> Builder<'a> { // Set this for all builds to make sure doc builds also get it. cargo.env("CFG_RELEASE_CHANNEL", &self.build.config.channel); - if self.is_verbose() { + if self.is_very_verbose() { cargo.arg("-v"); } - // FIXME: cargo bench does not accept `--release` - if self.config.rust_optimize && cmd != "bench" { - cargo.arg("--release"); + if self.config.rust_optimize { + // FIXME: cargo bench does not accept `--release` + if cmd != "bench" { + cargo.arg("--release"); + } + + if self.config.rust_codegen_units.is_none() && + self.build.is_rust_llvm(compiler.host) + { + cargo.env("RUSTC_THINLTO", "1"); + } } if self.config.locked_deps { cargo.arg("--locked"); diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index 08df65c761182..e531fdaf2923b 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -31,20 +31,51 @@ //! ever be probed for. Instead the compilers found here will be used for //! everything. +use std::collections::HashSet; +use std::{env, iter}; +use std::path::{Path, PathBuf}; use std::process::Command; -use std::iter; -use build_helper::{cc2ar, output}; +use build_helper::output; use cc; use Build; use config::Target; use cache::Interned; +// The `cc` crate doesn't provide a way to obtain a path to the detected archiver, +// so use some simplified logic here. First we respect the environment variable `AR`, then +// try to infer the archiver path from the C compiler path. +// In the future this logic should be replaced by calling into the `cc` crate. +fn cc2ar(cc: &Path, target: &str) -> Option { + if let Some(ar) = env::var_os("AR") { + Some(PathBuf::from(ar)) + } else if target.contains("msvc") { + None + } else if target.contains("musl") { + Some(PathBuf::from("ar")) + } else if target.contains("openbsd") { + Some(PathBuf::from("ar")) + } else { + let parent = cc.parent().unwrap(); + let file = cc.file_name().unwrap().to_str().unwrap(); + for suffix in &["gcc", "cc", "clang"] { + if let Some(idx) = file.rfind(suffix) { + let mut file = file[..idx].to_owned(); + file.push_str("ar"); + return Some(parent.join(&file)); + } + } + Some(parent.join(file)) + } +} + pub fn find(build: &mut Build) { // For all targets we're going to need a C compiler for building some shims // and such as well as for being a linker for Rust code. - for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) { + let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) + .collect::>(); + for target in targets.into_iter() { let mut cfg = cc::Build::new(); cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false) .target(&target).host(&build.build); @@ -53,20 +84,27 @@ pub fn find(build: &mut Build) { if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { cfg.compiler(cc); } else { - set_compiler(&mut cfg, "gcc", target, config, build); + set_compiler(&mut cfg, Language::C, target, config, build); } let compiler = cfg.get_compiler(); - let ar = cc2ar(compiler.path(), &target); + let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { + ar + } else { + cc2ar(compiler.path(), &target) + }; + build.verbose(&format!("CC_{} = {:?}", &target, compiler.path())); - if let Some(ref ar) = ar { + build.cc.insert(target, compiler); + if let Some(ar) = ar { build.verbose(&format!("AR_{} = {:?}", &target, ar)); + build.ar.insert(target, ar); } - build.cc.insert(target, (compiler, ar)); } // For all host triples we need to find a C++ compiler as well - for host in build.hosts.iter().cloned().chain(iter::once(build.build)) { + let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::>(); + for host in hosts.into_iter() { let mut cfg = cc::Build::new(); cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true) .target(&host).host(&build.build); @@ -74,7 +112,7 @@ pub fn find(build: &mut Build) { if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) { cfg.compiler(cxx); } else { - set_compiler(&mut cfg, "g++", host, config, build); + set_compiler(&mut cfg, Language::CPlusPlus, host, config, build); } let compiler = cfg.get_compiler(); build.verbose(&format!("CXX_{} = {:?}", host, compiler.path())); @@ -83,7 +121,7 @@ pub fn find(build: &mut Build) { } fn set_compiler(cfg: &mut cc::Build, - gnu_compiler: &str, + compiler: Language, target: Interned, config: Option<&Target>, build: &Build) { @@ -94,7 +132,7 @@ fn set_compiler(cfg: &mut cc::Build, t if t.contains("android") => { if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) { let target = target.replace("armv7", "arm"); - let compiler = format!("{}-{}", target, gnu_compiler); + let compiler = format!("{}-{}", target, compiler.clang()); cfg.compiler(ndk.join("bin").join(compiler)); } } @@ -103,6 +141,7 @@ fn set_compiler(cfg: &mut cc::Build, // which is a gcc version from ports, if this is the case. t if t.contains("openbsd") => { let c = cfg.get_compiler(); + let gnu_compiler = compiler.gcc(); if !c.path().ends_with(gnu_compiler) { return } @@ -145,3 +184,29 @@ fn set_compiler(cfg: &mut cc::Build, _ => {} } } + +/// The target programming language for a native compiler. +enum Language { + /// The compiler is targeting C. + C, + /// The compiler is targeting C++. + CPlusPlus, +} + +impl Language { + /// Obtains the name of a compiler in the GCC collection. + fn gcc(self) -> &'static str { + match self { + Language::C => "gcc", + Language::CPlusPlus => "g++", + } + } + + /// Obtains the name of a compiler in the clang suite. + fn clang(self) -> &'static str { + match self { + Language::C => "clang", + Language::CPlusPlus => "clang++", + } + } +} diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 6ed504dfe74a3..0485ebe17fb00 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -24,7 +24,7 @@ use Build; use config::Config; // The version number -pub const CFG_RELEASE_NUM: &str = "1.22.0"; +pub const CFG_RELEASE_NUM: &str = "1.24.0"; // An optional number to put after the label, e.g. '.2' -> '-beta.2' // Be sure to make this starts with a dot to conform to semver pre-release diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 6e276f44668f7..ee780d1245ed8 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -65,19 +65,21 @@ impl fmt::Display for TestKind { } } -fn try_run_expecting(build: &Build, cmd: &mut Command, expect: BuildExpectation) { +fn try_run_expecting(build: &Build, cmd: &mut Command, expect: BuildExpectation) -> bool { if !build.fail_fast { if !build.try_run(cmd, expect) { let mut failures = build.delayed_failures.borrow_mut(); failures.push(format!("{:?}", cmd)); + return false; } } else { build.run_expecting(cmd, expect); } + true } fn try_run(build: &Build, cmd: &mut Command) { - try_run_expecting(build, cmd, BuildExpectation::None) + try_run_expecting(build, cmd, BuildExpectation::None); } fn try_run_quiet(build: &Build, cmd: &mut Command) { @@ -246,19 +248,24 @@ impl Step for Rls { let compiler = builder.compiler(stage, host); builder.ensure(tool::Rls { compiler, target: self.host }); - let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); - cargo.arg("--manifest-path").arg(build.src.join("src/tools/rls/Cargo.toml")); + let mut cargo = tool::prepare_tool_cargo(builder, + compiler, + host, + "test", + "src/tools/rls"); // Don't build tests dynamically, just a pain to work with cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); builder.add_rustc_lib_path(compiler, &mut cargo); - try_run_expecting( + if try_run_expecting( build, &mut cargo, builder.build.config.toolstate.rls.passes(ToolState::Testing), - ); + ) { + build.save_toolstate("rls", ToolState::Testing); + } } } @@ -291,24 +298,30 @@ impl Step for Rustfmt { let compiler = builder.compiler(stage, host); builder.ensure(tool::Rustfmt { compiler, target: self.host }); - let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); - cargo.arg("--manifest-path").arg(build.src.join("src/tools/rustfmt/Cargo.toml")); + let mut cargo = tool::prepare_tool_cargo(builder, + compiler, + host, + "test", + "src/tools/rustfmt"); // Don't build tests dynamically, just a pain to work with cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); builder.add_rustc_lib_path(compiler, &mut cargo); - try_run_expecting( + if try_run_expecting( build, &mut cargo, builder.build.config.toolstate.rustfmt.passes(ToolState::Testing), - ); + ) { + build.save_toolstate("rustfmt", ToolState::Testing); + } } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Miri { + stage: u32, host: Interned, } @@ -324,6 +337,7 @@ impl Step for Miri { fn make_run(run: RunConfig) { run.builder.ensure(Miri { + stage: run.builder.top_stage, host: run.target, }); } @@ -331,33 +345,40 @@ impl Step for Miri { /// Runs `cargo test` for miri. fn run(self, builder: &Builder) { let build = builder.build; + let stage = self.stage; let host = self.host; - let compiler = builder.compiler(1, host); - - let miri = builder.ensure(tool::Miri { compiler, target: self.host }); - let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); - cargo.arg("--manifest-path").arg(build.src.join("src/tools/miri/Cargo.toml")); - - // Don't build tests dynamically, just a pain to work with - cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); - // miri tests need to know about the stage sysroot - cargo.env("MIRI_SYSROOT", builder.sysroot(compiler)); - cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); - cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); - cargo.env("MIRI_PATH", miri); - - builder.add_rustc_lib_path(compiler, &mut cargo); + let compiler = builder.compiler(stage, host); - try_run_expecting( - build, - &mut cargo, - builder.build.config.toolstate.miri.passes(ToolState::Testing), - ); + if let Some(miri) = builder.ensure(tool::Miri { compiler, target: self.host }) { + let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); + cargo.arg("--manifest-path").arg(build.src.join("src/tools/miri/Cargo.toml")); + + // Don't build tests dynamically, just a pain to work with + cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); + // miri tests need to know about the stage sysroot + cargo.env("MIRI_SYSROOT", builder.sysroot(compiler)); + cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); + cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); + cargo.env("MIRI_PATH", miri); + + builder.add_rustc_lib_path(compiler, &mut cargo); + + if try_run_expecting( + build, + &mut cargo, + builder.build.config.toolstate.miri.passes(ToolState::Testing), + ) { + build.save_toolstate("miri", ToolState::Testing); + } + } else { + eprintln!("failed to test miri: could not build"); + } } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Clippy { + stage: u32, host: Interned, } @@ -372,6 +393,7 @@ impl Step for Clippy { fn make_run(run: RunConfig) { run.builder.ensure(Clippy { + stage: run.builder.top_stage, host: run.target, }); } @@ -379,25 +401,37 @@ impl Step for Clippy { /// Runs `cargo test` for clippy. fn run(self, builder: &Builder) { let build = builder.build; + let stage = self.stage; let host = self.host; - let compiler = builder.compiler(1, host); - - let _clippy = builder.ensure(tool::Clippy { compiler, target: self.host }); - let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); - cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml")); - - // Don't build tests dynamically, just a pain to work with - cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); - // clippy tests need to know about the stage sysroot - cargo.env("SYSROOT", builder.sysroot(compiler)); - - builder.add_rustc_lib_path(compiler, &mut cargo); + let compiler = builder.compiler(stage, host); - try_run_expecting( - build, - &mut cargo, - builder.build.config.toolstate.clippy.passes(ToolState::Testing), - ); + if let Some(clippy) = builder.ensure(tool::Clippy { compiler, target: self.host }) { + let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test"); + cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml")); + + // Don't build tests dynamically, just a pain to work with + cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); + // clippy tests need to know about the stage sysroot + cargo.env("SYSROOT", builder.sysroot(compiler)); + cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); + cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); + let host_libs = builder.stage_out(compiler, Mode::Tool).join(builder.cargo_dir()); + cargo.env("HOST_LIBS", host_libs); + // clippy tests need to find the driver + cargo.env("CLIPPY_DRIVER_PATH", clippy); + + builder.add_rustc_lib_path(compiler, &mut cargo); + + if try_run_expecting( + build, + &mut cargo, + builder.build.config.toolstate.clippy.passes(ToolState::Testing), + ) { + build.save_toolstate("clippy-driver", ToolState::Testing); + } + } else { + eprintln!("failed to test clippy: could not build"); + } } } @@ -736,12 +770,14 @@ impl Step for Compiletest { flags.push("-g".to_string()); } - let mut hostflags = build.rustc_flags(compiler.host); - hostflags.extend(flags.clone()); + if let Some(linker) = build.linker(target) { + cmd.arg("--linker").arg(linker); + } + + let hostflags = flags.clone(); cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); - let mut targetflags = build.rustc_flags(target); - targetflags.extend(flags); + let mut targetflags = flags.clone(); targetflags.push(format!("-Lnative={}", build.test_helpers_out(target).display())); cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); @@ -795,6 +831,9 @@ impl Step for Compiletest { .arg("--cflags").arg(build.cflags(target).join(" ")) .arg("--llvm-components").arg(llvm_components.trim()) .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim()); + if let Some(ar) = build.ar(target) { + cmd.arg("--ar").arg(ar); + } } } if suite == "run-make" && !build.config.llvm_enabled { @@ -820,7 +859,7 @@ impl Step for Compiletest { // Note that if we encounter `PATH` we make sure to append to our own `PATH` // rather than stomp over it. if target.contains("msvc") { - for &(ref k, ref v) in build.cc[&target].0.env() { + for &(ref k, ref v) in build.cc[&target].env() { if k != "PATH" { cmd.env(k, v); } @@ -1185,7 +1224,8 @@ impl Step for Crate { // ends up messing with various mtime calculations and such. if !name.contains("jemalloc") && *name != *"build_helper" && - !(name.starts_with("rustc_") && name.ends_with("san")) { + !(name.starts_with("rustc_") && name.ends_with("san")) && + name != "dlmalloc" { cargo.arg("-p").arg(&format!("{}:0.0.0", name)); } for dep in build.crates[&name].deps.iter() { @@ -1218,6 +1258,17 @@ impl Step for Crate { if target.contains("emscripten") { cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)), build.config.nodejs.as_ref().expect("nodejs not configured")); + } else if target.starts_with("wasm32") { + // On the wasm32-unknown-unknown target we're using LTO which is + // incompatible with `-C prefer-dynamic`, so disable that here + cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); + + let node = build.config.nodejs.as_ref() + .expect("nodejs not configured"); + let runner = format!("{} {}/src/etc/wasm32-shim.js", + node.display(), + build.src.display()); + cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)), &runner); } else if build.remote_tested(target) { cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)), format!("{} run", diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 119340a0190c4..87f194fb7d2f8 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -13,7 +13,7 @@ //! Responsible for cleaning out a build directory of all old and stale //! artifacts to prepare for a fresh build. Currently doesn't remove the //! `build/cache` directory (download cache) or the `build/$target/llvm` -//! directory as we want that cached between builds. +//! directory unless the --all flag is present. use std::fs; use std::io::{self, ErrorKind}; @@ -21,24 +21,29 @@ use std::path::Path; use Build; -pub fn clean(build: &Build) { +pub fn clean(build: &Build, all: bool) { rm_rf("tmp".as_ref()); - rm_rf(&build.out.join("tmp")); - rm_rf(&build.out.join("dist")); - for host in &build.hosts { - let entries = match build.out.join(host).read_dir() { - Ok(iter) => iter, - Err(_) => continue, - }; + if all { + rm_rf(&build.out); + } else { + rm_rf(&build.out.join("tmp")); + rm_rf(&build.out.join("dist")); - for entry in entries { - let entry = t!(entry); - if entry.file_name().to_str() == Some("llvm") { - continue + for host in &build.hosts { + let entries = match build.out.join(host).read_dir() { + Ok(iter) => iter, + Err(_) => continue, + }; + + for entry in entries { + let entry = t!(entry); + if entry.file_name().to_str() == Some("llvm") { + continue + } + let path = t!(entry.path().canonicalize()); + rm_rf(&path); } - let path = t!(entry.path().canonicalize()); - rm_rf(&path); } } } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 335e1690a2ea0..db013691bb1b8 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -29,7 +29,7 @@ use build_helper::{output, mtime, up_to_date}; use filetime::FileTime; use serde_json; -use util::{exe, libdir, is_dylib, copy}; +use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv}; use {Build, Compiler, Mode}; use native; use tool; @@ -102,13 +102,13 @@ impl Step for Std { copy_musl_third_party_objects(build, target, &libdir); } - let out_dir = build.cargo_out(compiler, Mode::Libstd, target); + let out_dir = build.stage_out(compiler, Mode::Libstd); build.clear_if_dirty(&out_dir, &builder.rustc(compiler)); let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build"); std_cargo(build, &compiler, target, &mut cargo); run_cargo(build, - &mut cargo, - &libstd_stamp(build, compiler, target)); + &mut cargo, + &libstd_stamp(build, compiler, target)); builder.ensure(StdLink { compiler: builder.compiler(compiler.stage, build.build), @@ -354,13 +354,13 @@ impl Step for Test { let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage)); println!("Building stage{} test artifacts ({} -> {})", compiler.stage, &compiler.host, target); - let out_dir = build.cargo_out(compiler, Mode::Libtest, target); + let out_dir = build.stage_out(compiler, Mode::Libtest); build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target)); let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build"); test_cargo(build, &compiler, target, &mut cargo); run_cargo(build, - &mut cargo, - &libtest_stamp(build, compiler, target)); + &mut cargo, + &libtest_stamp(build, compiler, target)); builder.ensure(TestLink { compiler: builder.compiler(compiler.stage, build.build), @@ -480,8 +480,9 @@ impl Step for Rustc { println!("Building stage{} compiler artifacts ({} -> {})", compiler.stage, &compiler.host, target); - let out_dir = build.cargo_out(compiler, Mode::Librustc, target); - build.clear_if_dirty(&out_dir, &libtest_stamp(build, compiler, target)); + let stage_out = builder.stage_out(compiler, Mode::Librustc); + build.clear_if_dirty(&stage_out, &libstd_stamp(build, compiler, target)); + build.clear_if_dirty(&stage_out, &libtest_stamp(build, compiler, target)); let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build"); rustc_cargo(build, &compiler, target, &mut cargo); @@ -560,9 +561,6 @@ pub fn rustc_cargo(build: &Build, if let Some(ref s) = build.config.rustc_default_linker { cargo.env("CFG_DEFAULT_LINKER", s); } - if let Some(ref s) = build.config.rustc_default_ar { - cargo.env("CFG_DEFAULT_AR", s); - } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -760,15 +758,7 @@ impl Step for Assemble { /// `sysroot_dst` provided. fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) { t!(fs::create_dir_all(&sysroot_dst)); - let mut contents = Vec::new(); - t!(t!(File::open(stamp)).read_to_end(&mut contents)); - // This is the method we use for extracting paths from the stamp file passed to us. See - // run_cargo for more information (in this file). - for part in contents.split(|b| *b == 0) { - if part.is_empty() { - continue - } - let path = Path::new(t!(str::from_utf8(part))); + for path in read_stamp_file(stamp) { copy(&path, &sysroot_dst.join(path.file_name().unwrap())); } } @@ -802,7 +792,7 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) { cargo.arg("--message-format").arg("json") .stdout(Stdio::piped()); - if stderr_isatty() { + if stderr_isatty() && build.ci_env == CiEnv::None { // since we pass message-format=json to cargo, we need to tell the rustc // wrapper to give us colored output if necessary. This is because we // only want Cargo's JSON output, not rustcs. @@ -870,10 +860,19 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) { // have a hash in the name, but there's a version of this file in // the `deps` folder which *does* have a hash in the name. That's // the one we'll want to we'll probe for it later. - toplevel.push((filename.file_stem().unwrap() - .to_str().unwrap().to_string(), - filename.extension().unwrap().to_owned() - .to_str().unwrap().to_string())); + // + // We do not use `Path::file_stem` or `Path::extension` here, + // because some generated files may have multiple extensions e.g. + // `std-.dll.lib` on Windows. The aforementioned methods only + // split the file name by the last extension (`.lib`) while we need + // to split by all extensions (`.dll.lib`). + let expected_len = t!(filename.metadata()).len(); + let filename = filename.file_name().unwrap().to_str().unwrap(); + let mut parts = filename.splitn(2, '.'); + let file_stem = parts.next().unwrap().to_owned(); + let extension = parts.next().unwrap().to_owned(); + + toplevel.push((file_stem, extension, expected_len)); } } @@ -893,11 +892,12 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) { .map(|e| t!(e)) .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata()))) .collect::>(); - for (prefix, extension) in toplevel { - let candidates = contents.iter().filter(|&&(_, ref filename, _)| { + for (prefix, extension, expected_len) in toplevel { + let candidates = contents.iter().filter(|&&(_, ref filename, ref meta)| { filename.starts_with(&prefix[..]) && filename[prefix.len()..].starts_with("-") && - filename.ends_with(&extension[..]) + filename.ends_with(&extension[..]) && + meta.len() == expected_len }); let max = candidates.max_by_key(|&&(_, _, ref metadata)| { FileTime::from_last_modification_time(metadata) @@ -941,6 +941,8 @@ fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) { let max = max.unwrap(); let max_path = max_path.unwrap(); if stamp_contents == new_contents && max <= stamp_mtime { + build.verbose(&format!("not updating {:?}; contents equal and {} <= {}", + stamp, max, stamp_mtime)); return } if max > stamp_mtime { diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index c8b2ed042c119..9dd37d8e4560c 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -76,19 +76,18 @@ pub struct Config { pub llvm_static_stdcpp: bool, pub llvm_link_shared: bool, pub llvm_targets: Option, - pub llvm_experimental_targets: Option, + pub llvm_experimental_targets: String, pub llvm_link_jobs: Option, // rust codegen options pub rust_optimize: bool, - pub rust_codegen_units: u32, + pub rust_codegen_units: Option, 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, pub rust_optimize_tests: bool, pub rust_debuginfo_tests: bool, pub rust_dist_src: bool, @@ -113,6 +112,8 @@ pub struct Config { pub channel: String, pub quiet_tests: bool, pub test_miri: bool, + pub save_toolstates: Option, + // Fallback musl-root for all targets pub musl_root: Option, pub prefix: Option, @@ -144,6 +145,8 @@ pub struct Target { pub jemalloc: Option, pub cc: Option, pub cxx: Option, + pub ar: Option, + pub linker: Option, pub ndk: Option, pub crt_static: Option, pub musl_root: Option, @@ -206,6 +209,11 @@ struct Install { bindir: Option, libdir: Option, mandir: Option, + + // standard paths, currently unused + datadir: Option, + infodir: Option, + localstatedir: Option, } /// TOML representation of how the LLVM build is configured. @@ -262,7 +270,6 @@ struct Rust { use_jemalloc: Option, backtrace: Option, default_linker: Option, - default_ar: Option, channel: Option, musl_root: Option, rpath: Option, @@ -274,6 +281,7 @@ struct Rust { dist_src: Option, quiet_tests: Option, test_miri: Option, + save_toolstates: Option, } /// TOML representation of how each build target is configured. @@ -284,6 +292,8 @@ struct TomlTarget { jemalloc: Option, cc: Option, cxx: Option, + ar: Option, + linker: Option, android_ndk: Option, crt_static: Option, musl_root: Option, @@ -297,6 +307,7 @@ impl Config { let mut config = Config::default(); config.llvm_enabled = true; config.llvm_optimize = true; + config.llvm_version_check = true; config.use_jemalloc = true; config.backtrace = true; config.rust_optimize = true; @@ -304,7 +315,6 @@ impl Config { config.submodules = true; config.docs = true; config.rust_rpath = true; - config.rust_codegen_units = 1; config.channel = "dev".to_string(); config.codegen_tests = true; config.ignore_git = false; @@ -440,7 +450,8 @@ impl Config { set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp); set(&mut config.llvm_link_shared, llvm.link_shared); config.llvm_targets = llvm.targets.clone(); - config.llvm_experimental_targets = llvm.experimental_targets.clone(); + config.llvm_experimental_targets = llvm.experimental_targets.clone() + .unwrap_or("WebAssembly".to_string()); config.llvm_link_jobs = llvm.link_jobs; } @@ -464,12 +475,12 @@ impl Config { set(&mut config.quiet_tests, rust.quiet_tests); set(&mut config.test_miri, rust.test_miri); config.rustc_default_linker = rust.default_linker.clone(); - config.rustc_default_ar = rust.default_ar.clone(); config.musl_root = rust.musl_root.clone().map(PathBuf::from); + config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from); match rust.codegen_units { - Some(0) => config.rust_codegen_units = num_cpus::get() as u32, - Some(n) => config.rust_codegen_units = n, + Some(0) => config.rust_codegen_units = Some(num_cpus::get() as u32), + Some(n) => config.rust_codegen_units = Some(n), None => {} } } @@ -487,8 +498,10 @@ impl Config { if let Some(ref s) = cfg.android_ndk { target.ndk = Some(env::current_dir().unwrap().join(s)); } - target.cxx = cfg.cxx.clone().map(PathBuf::from); target.cc = cfg.cc.clone().map(PathBuf::from); + target.cxx = cfg.cxx.clone().map(PathBuf::from); + target.ar = cfg.ar.clone().map(PathBuf::from); + target.linker = cfg.linker.clone().map(PathBuf::from); target.crt_static = cfg.crt_static.clone(); target.musl_root = cfg.musl_root.clone().map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); @@ -520,7 +533,7 @@ impl Config { // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. - let default = config.channel == "nightly"; + let default = false; config.llvm_assertions = llvm_assertions.unwrap_or(default); let default = match &config.channel[..] { diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 67337bf44214e..48ca2838e4feb 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -11,6 +11,7 @@ # ignore-tidy-linelength +from __future__ import absolute_import, division, print_function import sys import os rust_dir = os.path.dirname(os.path.abspath(__file__)) @@ -19,21 +20,26 @@ sys.path.append(os.path.join(rust_dir, "src", "bootstrap")) import bootstrap -class Option: + +class Option(object): def __init__(self, name, rustbuild, desc, value): self.name = name self.rustbuild = rustbuild self.desc = desc self.value = value + options = [] + def o(*args): options.append(Option(*args, value=False)) + def v(*args): options.append(Option(*args, value=True)) + o("debug", "rust.debug", "debug mode; disables optimization unless `--enable-optimize` given") o("docs", "build.docs", "build standard library documentation") o("compiler-docs", "build.compiler-docs", "build compiler documentation") @@ -71,6 +77,7 @@ def v(*args): o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata") o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information") o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill") +v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file") v("prefix", "install.prefix", "set installation prefix") v("localstatedir", "install.localstatedir", "local state directory") @@ -119,9 +126,8 @@ def v(*args): "experimental LLVM targets to build") v("release-channel", "rust.channel", "the name of the release channel to build") -# Used on systems where "cc" and "ar" are unavailable +# Used on systems where "cc" is unavailable v("default-linker", "rust.default-linker", "the default linker") -v("default-ar", "rust.default-ar", "the default ar") # Many of these are saved below during the "writing configuration" step # (others are conditionally saved). @@ -136,13 +142,16 @@ def v(*args): v("set", None, "set arbitrary key/value pairs in TOML configuration") + def p(msg): print("configure: " + msg) + def err(msg): print("configure: error: " + msg) sys.exit(1) + if '--help' in sys.argv or '-h' in sys.argv: print('Usage: ./configure [options]') print('') @@ -208,7 +217,7 @@ def err(msg): continue found = True - if not option.name in known_args: + if option.name not in known_args: known_args[option.name] = [] known_args[option.name].append((option, value)) break @@ -217,7 +226,12 @@ def err(msg): unknown_args.append(arg) p("") -if 'option-checking' not in known_args or known_args['option-checking'][1]: +# Note: here and a few other places, we use [-1] to apply the *last* value +# passed. But if option-checking is enabled, then the known_args loop will +# also assert that options are only passed once. +option_checking = ('option-checking' not in known_args + or known_args['option-checking'][-1][1]) +if option_checking: if len(unknown_args) > 0: err("Option '" + unknown_args[0] + "' is not recognized") if len(need_value_args) > 0: @@ -227,27 +241,30 @@ def err(msg): # TOML we're going to write out config = {} + def build(): if 'build' in known_args: - return known_args['build'][0][1] + return known_args['build'][-1][1] return bootstrap.default_build_triple() + def set(key, value): - s = "{:20} := {}".format(key, value) - if len(s) < 70: - p(s) - else: - p(s[:70] + " ...") - - arr = config - parts = key.split('.') - for i, part in enumerate(parts): - if i == len(parts) - 1: - arr[part] = value - else: - if not part in arr: - arr[part] = {} - arr = arr[part] + s = "{:20} := {}".format(key, value) + if len(s) < 70: + p(s) + else: + p(s[:70] + " ...") + + arr = config + parts = key.split('.') + for i, part in enumerate(parts): + if i == len(parts) - 1: + arr[part] = value + else: + if part not in arr: + arr[part] = {} + arr = arr[part] + for key in known_args: # The `set` option is special and can be passed a bunch of times @@ -265,9 +282,9 @@ def set(key, value): # Ensure each option is only passed once arr = known_args[key] - if len(arr) > 1: + if option_checking and len(arr) > 1: err("Option '{}' provided more than once".format(key)) - option, value = arr[0] + option, value = arr[-1] # If we have a clear avenue to set our value in rustbuild, do so if option.rustbuild is not None: @@ -345,8 +362,9 @@ def set(key, value): targets[target] = sections['target'][:] targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target) + # Here we walk through the constructed configuration we have from the parsed -# command line arguemnts. We then apply each piece of configuration by +# command line arguments. We then apply each piece of configuration by # basically just doing a `sed` to change the various configuration line to what # we've got configure. def to_toml(value): @@ -360,7 +378,8 @@ def to_toml(value): elif isinstance(value, str): return "'" + value + "'" else: - raise 'no toml' + raise RuntimeError('no toml') + def configure_section(lines, config): for key in config: @@ -375,10 +394,11 @@ def configure_section(lines, config): if not found: raise RuntimeError("failed to find config line for {}".format(key)) + for section_key in config: section_config = config[section_key] - if not section_key in sections: - raise RuntimeError("config key {} not in sections".format(key)) + if section_key not in sections: + raise RuntimeError("config key {} not in sections".format(section_key)) if section_key == 'target': for target in section_config: @@ -407,11 +427,6 @@ def configure_section(lines, config): contents = contents.replace("$(CFG_PYTHON)", sys.executable) f.write(contents) -# Finally, clean up with a bit of a help message -relpath = os.path.dirname(__file__) -if relpath == '': - relpath = '.' - p("") -p("run `python {}/x.py --help`".format(relpath)) +p("run `python {}/x.py --help`".format(rust_dir)) p("") diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 3d4aa0413db61..bfe2a64f5ba48 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -39,6 +39,8 @@ pub fn pkgname(build: &Build, component: &str) -> String { format!("{}-{}", component, build.cargo_package_vers()) } else if component == "rls" { format!("{}-{}", component, build.rls_package_vers()) + } else if component == "rustfmt" { + format!("{}-{}", component, build.rustfmt_package_vers()) } else { assert!(component.starts_with("rust")); format!("{}-{}", component, build.rust_package_vers()) @@ -176,7 +178,7 @@ fn make_win_dist( } } - let target_tools = ["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe", "libwinpthread-1.dll"]; + let target_tools = ["gcc.exe", "ld.exe", "dlltool.exe", "libwinpthread-1.dll"]; let mut rustc_dlls = vec!["libstdc++-6.dll", "libwinpthread-1.dll"]; if target_triple.starts_with("i686-") { rustc_dlls.push("libgcc_s_dw2-1.dll"); @@ -630,7 +632,7 @@ impl Step for Analysis { let image = tmpdir(build).join(format!("{}-{}-image", name, target)); let src = build.stage_out(compiler, Mode::Libstd) - .join(target).join("release").join("deps"); + .join(target).join(build.cargo_dir()).join("deps"); let image_src = src.join("save-analysis"); let dst = image.join("lib/rustlib").join(target).join("analysis"); @@ -670,6 +672,9 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di spath.ends_with(".s")) { return false } + if spath.contains("test/emscripten") || spath.contains("test\\emscripten") { + return false + } let full_path = Path::new(dir).join(path); if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) { @@ -734,17 +739,16 @@ impl Step for Src { // (essentially libstd and all of its path dependencies) let std_src_dirs = [ "src/build_helper", + "src/dlmalloc", "src/liballoc", "src/liballoc_jemalloc", "src/liballoc_system", "src/libbacktrace", - "src/libcollections", "src/libcompiler_builtins", "src/libcore", "src/liblibc", "src/libpanic_abort", "src/libpanic_unwind", - "src/librand", "src/librustc_asan", "src/librustc_lsan", "src/librustc_msan", @@ -754,6 +758,7 @@ impl Step for Src { "src/libunwind", "src/rustc/compiler_builtins_shim", "src/rustc/libc_shim", + "src/rustc/dlmalloc_shim", "src/libtest", "src/libterm", "src/jemalloc", @@ -1035,7 +1040,7 @@ pub struct Rls { } impl Step for Rls { - type Output = PathBuf; + type Output = Option; const ONLY_BUILD_TARGETS: bool = true; const ONLY_HOSTS: bool = true; @@ -1050,12 +1055,17 @@ impl Step for Rls { }); } - fn run(self, builder: &Builder) -> PathBuf { + fn run(self, builder: &Builder) -> Option { let build = builder.build; let stage = self.stage; let target = self.target; assert!(build.config.extended); + if !builder.config.toolstate.rls.testing() { + println!("skipping Dist RLS stage{} ({})", stage, target); + return None + } + println!("Dist RLS stage{} ({})", stage, target); let src = build.src.join("src/tools/rls"); let release_num = build.release_num("rls"); @@ -1068,10 +1078,13 @@ impl Step for Rls { t!(fs::create_dir_all(&image)); // Prepare the image directory + // We expect RLS to build, because we've exited this step above if tool + // state for RLS isn't testing. let rls = builder.ensure(tool::Rls { compiler: builder.compiler(stage, build.build), target - }); + }).or_else(|| { println!("Unable to build RLS, skipping dist"); None })?; + install(&rls, &image.join("bin"), 0o755); let doc = image.join("share/doc/rls"); install(&src.join("README.md"), &doc, 0o644); @@ -1102,7 +1115,97 @@ impl Step for Rls { .arg("--component-name=rls-preview"); build.run(&mut cmd); - distdir(build).join(format!("{}-{}.tar.gz", name, target)) + Some(distdir(build).join(format!("{}-{}.tar.gz", name, target))) + } +} + + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Rustfmt { + pub stage: u32, + pub target: Interned, +} + +impl Step for Rustfmt { + type Output = Option; + const ONLY_BUILD_TARGETS: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun) -> ShouldRun { + run.path("rustfmt") + } + + fn make_run(run: RunConfig) { + run.builder.ensure(Rustfmt { + stage: run.builder.top_stage, + target: run.target, + }); + } + + fn run(self, builder: &Builder) -> Option { + let build = builder.build; + let stage = self.stage; + let target = self.target; + assert!(build.config.extended); + + if !builder.config.toolstate.rustfmt.testing() { + println!("skipping Dist Rustfmt stage{} ({})", stage, target); + return None + } + + println!("Dist Rustfmt stage{} ({})", stage, target); + let src = build.src.join("src/tools/rustfmt"); + let release_num = build.release_num("rustfmt"); + let name = pkgname(build, "rustfmt"); + let version = build.rustfmt_info.version(build, &release_num); + + let tmp = tmpdir(build); + let image = tmp.join("rustfmt-image"); + drop(fs::remove_dir_all(&image)); + t!(fs::create_dir_all(&image)); + + // Prepare the image directory + let rustfmt = builder.ensure(tool::Rustfmt { + compiler: builder.compiler(stage, build.build), + target + }).or_else(|| { println!("Unable to build Rustfmt, skipping dist"); None })?; + let cargofmt = builder.ensure(tool::Cargofmt { + compiler: builder.compiler(stage, build.build), + target + }).or_else(|| { println!("Unable to build Cargofmt, skipping dist"); None })?; + + install(&rustfmt, &image.join("bin"), 0o755); + install(&cargofmt, &image.join("bin"), 0o755); + let doc = image.join("share/doc/rustfmt"); + install(&src.join("README.md"), &doc, 0o644); + install(&src.join("LICENSE-MIT"), &doc, 0o644); + install(&src.join("LICENSE-APACHE"), &doc, 0o644); + + // Prepare the overlay + let overlay = tmp.join("rustfmt-overlay"); + drop(fs::remove_dir_all(&overlay)); + t!(fs::create_dir_all(&overlay)); + install(&src.join("README.md"), &overlay, 0o644); + install(&src.join("LICENSE-MIT"), &overlay, 0o644); + install(&src.join("LICENSE-APACHE"), &overlay, 0o644); + t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); + + // Generate the installer tarball + let mut cmd = rust_installer(builder); + cmd.arg("generate") + .arg("--product-name=Rust") + .arg("--rel-manifest-dir=rustlib") + .arg("--success-message=rustfmt-ready-to-fmt.") + .arg("--image-dir").arg(&image) + .arg("--work-dir").arg(&tmpdir(build)) + .arg("--output-dir").arg(&distdir(build)) + .arg("--non-installed-overlay").arg(&overlay) + .arg(format!("--package-name={}-{}", name, target)) + .arg("--legacy-manifest-dirs=rustlib,cargo") + .arg("--component-name=rustfmt-preview"); + + build.run(&mut cmd); + Some(distdir(build).join(format!("{}-{}.tar.gz", name, target))) } } @@ -1169,6 +1272,7 @@ impl Step for Extended { compiler: builder.compiler(stage, target), }); let cargo_installer = builder.ensure(Cargo { stage, target }); + let rustfmt_installer = builder.ensure(Rustfmt { stage, target }); let rls_installer = builder.ensure(Rls { stage, target }); let mingw_installer = builder.ensure(Mingw { host: target }); let analysis_installer = builder.ensure(Analysis { @@ -1202,8 +1306,13 @@ impl Step for Extended { // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering // the std files during uninstall. To do this ensure that rustc comes // before rust-std in the list below. - let mut tarballs = vec![rustc_installer, cargo_installer, rls_installer, - analysis_installer, std_installer]; + let mut tarballs = Vec::new(); + tarballs.push(rustc_installer); + tarballs.push(cargo_installer); + tarballs.extend(rls_installer.clone()); + tarballs.extend(rustfmt_installer.clone()); + tarballs.push(analysis_installer); + tarballs.push(std_installer); if build.config.docs { tarballs.push(docs_installer); } @@ -1245,35 +1354,41 @@ impl Step for Extended { } rtf.push_str("}"); + fn filter(contents: &str, marker: &str) -> String { + let start = format!("tool-{}-start", marker); + let end = format!("tool-{}-end", marker); + let mut lines = Vec::new(); + let mut omitted = false; + for line in contents.lines() { + if line.contains(&start) { + omitted = true; + } else if line.contains(&end) { + omitted = false; + } else if !omitted { + lines.push(line); + } + } + + lines.join("\n") + } + + let xform = |p: &Path| { + let mut contents = String::new(); + t!(t!(File::open(p)).read_to_string(&mut contents)); + if rls_installer.is_none() { + contents = filter(&contents, "rls"); + } + if rustfmt_installer.is_none() { + contents = filter(&contents, "rustfmt"); + } + let ret = tmp.join(p.file_name().unwrap()); + t!(t!(File::create(&ret)).write_all(contents.as_bytes())); + return ret + }; + if target.contains("apple-darwin") { let pkg = tmp.join("pkg"); let _ = fs::remove_dir_all(&pkg); - t!(fs::create_dir_all(pkg.join("rustc"))); - t!(fs::create_dir_all(pkg.join("cargo"))); - t!(fs::create_dir_all(pkg.join("rust-docs"))); - t!(fs::create_dir_all(pkg.join("rust-std"))); - t!(fs::create_dir_all(pkg.join("rls"))); - t!(fs::create_dir_all(pkg.join("rust-analysis"))); - - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target)), - &pkg.join("rustc")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "cargo"), target)), - &pkg.join("cargo")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target)), - &pkg.join("rust-docs")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target)), - &pkg.join("rust-std")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rls"), target)), - &pkg.join("rls")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-analysis"), target)), - &pkg.join("rust-analysis")); - - install(&etc.join("pkg/postinstall"), &pkg.join("rustc"), 0o755); - install(&etc.join("pkg/postinstall"), &pkg.join("cargo"), 0o755); - install(&etc.join("pkg/postinstall"), &pkg.join("rust-docs"), 0o755); - install(&etc.join("pkg/postinstall"), &pkg.join("rust-std"), 0o755); - install(&etc.join("pkg/postinstall"), &pkg.join("rls"), 0o755); - install(&etc.join("pkg/postinstall"), &pkg.join("rust-analysis"), 0o755); let pkgbuild = |component: &str| { let mut cmd = Command::new("pkgbuild"); @@ -1283,12 +1398,23 @@ impl Step for Extended { .arg(pkg.join(component).with_extension("pkg")); build.run(&mut cmd); }; - pkgbuild("rustc"); - pkgbuild("cargo"); - pkgbuild("rust-docs"); - pkgbuild("rust-std"); - pkgbuild("rls"); - pkgbuild("rust-analysis"); + + let prepare = |name: &str| { + t!(fs::create_dir_all(pkg.join(name))); + cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)), + &pkg.join(name)); + install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); + pkgbuild(name); + }; + prepare("rustc"); + prepare("cargo"); + prepare("rust-docs"); + prepare("rust-std"); + prepare("rust-analysis"); + + if rls_installer.is_some() { + prepare("rls"); + } // create an 'uninstall' package install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); @@ -1298,7 +1424,7 @@ impl Step for Extended { t!(t!(File::create(pkg.join("res/LICENSE.txt"))).write_all(license.as_bytes())); install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); let mut cmd = Command::new("productbuild"); - cmd.arg("--distribution").arg(etc.join("pkg/Distribution.xml")) + cmd.arg("--distribution").arg(xform(&etc.join("pkg/Distribution.xml"))) .arg("--resources").arg(pkg.join("res")) .arg(distdir(build).join(format!("{}-{}.pkg", pkgname(build, "rust"), @@ -1310,46 +1436,34 @@ impl Step for Extended { if target.contains("windows") { let exe = tmp.join("exe"); let _ = fs::remove_dir_all(&exe); - t!(fs::create_dir_all(exe.join("rustc"))); - t!(fs::create_dir_all(exe.join("cargo"))); - t!(fs::create_dir_all(exe.join("rls"))); - t!(fs::create_dir_all(exe.join("rust-analysis"))); - t!(fs::create_dir_all(exe.join("rust-docs"))); - t!(fs::create_dir_all(exe.join("rust-std"))); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target)) - .join("rustc"), - &exe.join("rustc")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "cargo"), target)) - .join("cargo"), - &exe.join("cargo")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target)) - .join("rust-docs"), - &exe.join("rust-docs")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target)) - .join(format!("rust-std-{}", target)), - &exe.join("rust-std")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview"), - &exe.join("rls")); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-analysis"), target)) - .join(format!("rust-analysis-{}", target)), - &exe.join("rust-analysis")); - - t!(fs::remove_file(exe.join("rustc/manifest.in"))); - t!(fs::remove_file(exe.join("cargo/manifest.in"))); - t!(fs::remove_file(exe.join("rust-docs/manifest.in"))); - t!(fs::remove_file(exe.join("rust-std/manifest.in"))); - t!(fs::remove_file(exe.join("rls/manifest.in"))); - t!(fs::remove_file(exe.join("rust-analysis/manifest.in"))); + let prepare = |name: &str| { + t!(fs::create_dir_all(exe.join(name))); + let dir = if name == "rust-std" || name == "rust-analysis" { + format!("{}-{}", name, target) + } else if name == "rls" { + "rls-preview".to_string() + } else { + name.to_string() + }; + cp_r(&work.join(&format!("{}-{}", pkgname(build, name), target)) + .join(dir), + &exe.join(name)); + t!(fs::remove_file(exe.join(name).join("manifest.in"))); + }; + prepare("rustc"); + prepare("cargo"); + prepare("rust-analysis"); + prepare("rust-docs"); + prepare("rust-std"); + if rls_installer.is_some() { + prepare("rls"); + } if target.contains("windows-gnu") { - t!(fs::create_dir_all(exe.join("rust-mingw"))); - cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-mingw"), target)) - .join("rust-mingw"), - &exe.join("rust-mingw")); - t!(fs::remove_file(exe.join("rust-mingw/manifest.in"))); + prepare("rust-mingw"); } - install(&etc.join("exe/rust.iss"), &exe, 0o644); + install(&xform(&etc.join("exe/rust.iss")), &exe, 0o644); install(&etc.join("exe/modpath.iss"), &exe, 0o644); install(&etc.join("exe/upgrade.iss"), &exe, 0o644); install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); @@ -1413,16 +1527,18 @@ impl Step for Extended { .arg("-dr").arg("Std") .arg("-var").arg("var.StdDir") .arg("-out").arg(exe.join("StdGroup.wxs"))); - build.run(Command::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rls") - .args(&heat_flags) - .arg("-cg").arg("RlsGroup") - .arg("-dr").arg("Rls") - .arg("-var").arg("var.RlsDir") - .arg("-out").arg(exe.join("RlsGroup.wxs")) - .arg("-t").arg(etc.join("msi/remove-duplicates.xsl"))); + if rls_installer.is_some() { + build.run(Command::new(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rls") + .args(&heat_flags) + .arg("-cg").arg("RlsGroup") + .arg("-dr").arg("Rls") + .arg("-var").arg("var.RlsDir") + .arg("-out").arg(exe.join("RlsGroup.wxs")) + .arg("-t").arg(etc.join("msi/remove-duplicates.xsl"))); + } build.run(Command::new(&heat) .current_dir(&exe) .arg("dir") @@ -1456,26 +1572,30 @@ impl Step for Extended { .arg("-dDocsDir=rust-docs") .arg("-dCargoDir=cargo") .arg("-dStdDir=rust-std") - .arg("-dRlsDir=rls") .arg("-dAnalysisDir=rust-analysis") .arg("-arch").arg(&arch) .arg("-out").arg(&output) .arg(&input); add_env(build, &mut cmd, target); + if rls_installer.is_some() { + cmd.arg("-dRlsDir=rls"); + } if target.contains("windows-gnu") { cmd.arg("-dGccDir=rust-mingw"); } build.run(&mut cmd); }; - candle(&etc.join("msi/rust.wxs")); + candle(&xform(&etc.join("msi/rust.wxs"))); candle(&etc.join("msi/ui.wxs")); candle(&etc.join("msi/rustwelcomedlg.wxs")); candle("RustcGroup.wxs".as_ref()); candle("DocsGroup.wxs".as_ref()); candle("CargoGroup.wxs".as_ref()); candle("StdGroup.wxs".as_ref()); - candle("RlsGroup.wxs".as_ref()); + if rls_installer.is_some() { + candle("RlsGroup.wxs".as_ref()); + } candle("AnalysisGroup.wxs".as_ref()); if target.contains("windows-gnu") { @@ -1499,10 +1619,13 @@ impl Step for Extended { .arg("DocsGroup.wixobj") .arg("CargoGroup.wixobj") .arg("StdGroup.wixobj") - .arg("RlsGroup.wixobj") .arg("AnalysisGroup.wixobj") .current_dir(&exe); + if rls_installer.is_some() { + cmd.arg("RlsGroup.wixobj"); + } + if target.contains("windows-gnu") { cmd.arg("GccGroup.wixobj"); } @@ -1586,6 +1709,7 @@ impl Step for HashSign { cmd.arg(build.rust_package_vers()); cmd.arg(build.package_vers(&build.release_num("cargo"))); cmd.arg(build.package_vers(&build.release_num("rls"))); + cmd.arg(build.package_vers(&build.release_num("rustfmt"))); cmd.arg(addr); t!(fs::create_dir_all(distdir(build))); diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index b9a52a66793df..3c12cfc4c7ffd 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -66,7 +66,7 @@ macro_rules! book { } book!( - Nomicon, "src/doc/book", "nomicon"; + Nomicon, "src/doc/nomicon", "nomicon"; Reference, "src/doc/reference", "reference"; Rustdoc, "src/doc/rustdoc", "rustdoc"; ); @@ -132,6 +132,52 @@ impl Step for UnstableBook { } } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct CargoBook { + target: Interned, + name: Interned, +} + +impl Step for CargoBook { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun) -> ShouldRun { + let builder = run.builder; + run.path("src/tools/cargo/src/doc/book").default_condition(builder.build.config.docs) + } + + fn make_run(run: RunConfig) { + run.builder.ensure(CargoBook { + target: run.target, + name: INTERNER.intern_str("cargo"), + }); + } + + fn run(self, builder: &Builder) { + let build = builder.build; + + let target = self.target; + let name = self.name; + let src = build.src.join("src/tools/cargo/src/doc/book"); + + let out = build.doc_out(target); + t!(fs::create_dir_all(&out)); + + let out = out.join(name); + + println!("Cargo Book ({}) - {}", target, name); + + let _ = fs::remove_dir_all(&out); + + build.run(builder.tool_cmd(Tool::Rustbook) + .arg("build") + .arg(&src) + .arg("-d") + .arg(out)); + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] struct RustbookSrc { target: Interned, @@ -205,10 +251,12 @@ impl Step for TheBook { /// /// * Book (first edition) /// * Book (second edition) + /// * Version info and CSS /// * Index page /// * Redirect pages fn run(self, builder: &Builder) { let build = builder.build; + let compiler = self.compiler; let target = self.target; let name = self.name; // build book first edition @@ -223,10 +271,16 @@ impl Step for TheBook { name: INTERNER.intern_string(format!("{}/second-edition", name)), }); + // build the version info page and CSS + builder.ensure(Standalone { + compiler, + target, + }); + // build the index page let index = format!("{}/index.md", name); println!("Documenting book index ({})", target); - invoke_rustdoc(builder, self.compiler, target, &index); + invoke_rustdoc(builder, compiler, target, &index); // build the redirect pages println!("Documenting book redirect pages ({})", target); @@ -235,56 +289,11 @@ impl Step for TheBook { let path = file.path(); let path = path.to_str().unwrap(); - invoke_rustdoc(builder, self.compiler, target, path); + invoke_rustdoc(builder, compiler, target, path); } } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct CargoBook { - target: Interned, -} - -impl Step for CargoBook { - type Output = (); - const DEFAULT: bool = true; - - fn should_run(run: ShouldRun) -> ShouldRun { - let builder = run.builder; - run.path("src/doc/cargo").default_condition(builder.build.config.docs) - } - - fn make_run(run: RunConfig) { - run.builder.ensure(CargoBook { - target: run.target, - }); - } - - /// Create a placeholder for the cargo documentation so that doc.rust-lang.org/cargo will - /// redirect to doc.crates.io. We want to publish doc.rust-lang.org/cargo in the paper - /// version of the book, but we don't want to rush the process of switching cargo's docs - /// over to mdbook and deploying them. When the cargo book is ready, this implementation - /// should build the mdbook instead of this redirect page. - fn run(self, builder: &Builder) { - let build = builder.build; - let out = build.doc_out(self.target); - - let cargo_dir = out.join("cargo"); - t!(fs::create_dir_all(&cargo_dir)); - - let index = cargo_dir.join("index.html"); - let redirect_html = r#" - - - - - "#; - - println!("Creating cargo book redirect page"); - t!(t!(File::create(&index)).write_all(redirect_html.as_bytes())); - } -} - fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned, markdown: &str) { let build = builder.build; let out = build.doc_out(target); @@ -293,25 +302,12 @@ fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned, test_args: Vec, }, - Clean, + Clean { + all: bool, + }, Dist { paths: Vec, }, @@ -134,9 +136,12 @@ To learn more about a subcommand, run `./x.py -h`"); let subcommand = match subcommand { Some(s) => s, None => { - // No subcommand -- show the general usage and subcommand help + // No or an invalid subcommand -- show the general usage and subcommand help + // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid + // subcommand. println!("{}\n", subcommand_help); - process::exit(1); + let exit_code = if args.is_empty() { 0 } else { 1 }; + process::exit(exit_code); } }; @@ -147,6 +152,7 @@ To learn more about a subcommand, run `./x.py -h`"); opts.optmulti("", "test-args", "extra arguments", "ARGS"); }, "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); }, + "clean" => { opts.optflag("", "all", "clean all build artifacts"); }, _ => { }, }; @@ -250,7 +256,7 @@ Arguments: } }); - // All subcommands can have an optional "Available paths" section + // All subcommands except `clean` can have an optional "Available paths" section if matches.opt_present("verbose") { let config = Config::parse(&["build".to_string()]); let mut build = Build::new(config); @@ -258,9 +264,10 @@ Arguments: let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); - } else { - extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.", - subcommand).as_str()); + } else if subcommand.as_str() != "clean" { + extra_help.push_str(format!( + "Run `./x.py {} -h -v` to see a list of available paths.", + subcommand).as_str()); } // User passed in -h/--help? @@ -290,10 +297,13 @@ Arguments: } "clean" => { if paths.len() > 0 { - println!("\nclean takes no arguments\n"); + println!("\nclean does not take a path argument\n"); usage(1, &opts, &subcommand_help, &extra_help); } - Subcommand::Clean + + Subcommand::Clean { + all: matches.opt_present("all"), + } } "dist" => { Subcommand::Dist { diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 608924c9c28d1..743f32ece99c6 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -27,10 +27,8 @@ pub fn install_docs(builder: &Builder, stage: u32, host: Interned) { install_sh(builder, "docs", "rust-docs", stage, Some(host)); } -pub fn install_std(builder: &Builder, stage: u32) { - for target in &builder.build.targets { - install_sh(builder, "std", "rust-std", stage, Some(*target)); - } +pub fn install_std(builder: &Builder, stage: u32, target: Interned) { + install_sh(builder, "std", "rust-std", stage, Some(target)); } pub fn install_cargo(builder: &Builder, stage: u32, host: Interned) { @@ -41,6 +39,10 @@ pub fn install_rls(builder: &Builder, stage: u32, host: Interned) { install_sh(builder, "rls", "rls", stage, Some(host)); } +pub fn install_rustfmt(builder: &Builder, stage: u32, host: Interned) { + install_sh(builder, "rustfmt", "rustfmt", stage, Some(host)); +} + pub fn install_analysis(builder: &Builder, stage: u32, host: Interned) { install_sh(builder, "analysis", "rust-analysis", stage, Some(host)); } @@ -175,19 +177,31 @@ install!((self, builder, _config), install_docs(builder, self.stage, self.target); }; Std, "src/libstd", true, only_hosts: true, { - builder.ensure(dist::Std { - compiler: builder.compiler(self.stage, self.host), - target: self.target - }); - install_std(builder, self.stage); + for target in &builder.build.targets { + builder.ensure(dist::Std { + compiler: builder.compiler(self.stage, self.host), + target: *target + }); + install_std(builder, self.stage, *target); + } }; Cargo, "cargo", _config.extended, only_hosts: true, { builder.ensure(dist::Cargo { stage: self.stage, target: self.target }); install_cargo(builder, self.stage, self.target); }; Rls, "rls", _config.extended, only_hosts: true, { - builder.ensure(dist::Rls { stage: self.stage, target: self.target }); - install_rls(builder, self.stage, self.target); + if builder.ensure(dist::Rls { stage: self.stage, target: self.target }).is_some() { + install_rls(builder, self.stage, self.target); + } else { + println!("skipping Install RLS stage{} ({})", self.stage, self.target); + } + }; + Rustfmt, "rustfmt", _config.extended, only_hosts: true, { + if builder.ensure(dist::Rustfmt { stage: self.stage, target: self.target }).is_some() { + install_rustfmt(builder, self.stage, self.target); + } else { + println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target); + } }; Analysis, "analysis", _config.extended, only_hosts: false, { builder.ensure(dist::Analysis { diff --git a/src/bootstrap/job.rs b/src/bootstrap/job.rs index 72a5d1338b8d0..fa3ba02482f56 100644 --- a/src/bootstrap/job.rs +++ b/src/bootstrap/job.rs @@ -185,7 +185,7 @@ pub unsafe fn setup(build: &mut Build) { 0, FALSE, DUPLICATE_SAME_ACCESS); // If this failed, well at least we tried! An example of DuplicateHandle - // failing in the past has been when the wrong python2 package spawed this + // failing in the past has been when the wrong python2 package spawned this // build system (e.g. the `python2` package in MSYS instead of // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure // mode" here is that we only clean everything up when the build system diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 83aa08366df7d..2f00c313a0c30 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -190,6 +190,7 @@ mod job { pub use config::Config; use flags::Subcommand; use cache::{Interned, INTERNER}; +use toolstate::ToolState; /// A structure representing a Rust compiler. /// @@ -222,6 +223,7 @@ pub struct Build { rust_info: channel::GitInfo, cargo_info: channel::GitInfo, rls_info: channel::GitInfo, + rustfmt_info: channel::GitInfo, local_rebuild: bool, fail_fast: bool, verbosity: usize, @@ -240,10 +242,11 @@ pub struct Build { lldb_python_dir: Option, // Runtime state filled in later on - // target -> (cc, ar) - cc: HashMap, (cc::Tool, Option)>, - // host -> (cc, ar) + // C/C++ compilers and archiver for all targets + cc: HashMap, cc::Tool>, cxx: HashMap, cc::Tool>, + ar: HashMap, PathBuf>, + // Misc crates: HashMap, Crate>, is_sudo: bool, ci_env: CiEnv, @@ -303,6 +306,7 @@ impl Build { let rust_info = channel::GitInfo::new(&config, &src); let cargo_info = channel::GitInfo::new(&config, &src.join("src/tools/cargo")); let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls")); + let rustfmt_info = channel::GitInfo::new(&config, &src.join("src/tools/rustfmt")); Build { initial_rustc: config.initial_rustc.clone(), @@ -322,8 +326,10 @@ impl Build { rust_info, cargo_info, rls_info, + rustfmt_info, cc: HashMap::new(), cxx: HashMap::new(), + ar: HashMap::new(), crates: HashMap::new(), lldb_version: None, lldb_python_dir: None, @@ -345,8 +351,8 @@ impl Build { job::setup(self); } - if let Subcommand::Clean = self.config.cmd { - return clean::clean(self); + if let Subcommand::Clean { all } = self.config.cmd { + return clean::clean(self, all); } self.verbose("finding compilers"); @@ -383,16 +389,19 @@ impl Build { /// Clear out `dir` if `input` is newer. /// /// After this executes, it will also ensure that `dir` exists. - fn clear_if_dirty(&self, dir: &Path, input: &Path) { + fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { let stamp = dir.join(".stamp"); + let mut cleared = false; if mtime(&stamp) < mtime(input) { self.verbose(&format!("Dirty - {}", dir.display())); let _ = fs::remove_dir_all(dir); + cleared = true; } else if stamp.exists() { - return + return cleared; } t!(fs::create_dir_all(dir)); t!(File::create(stamp)); + cleared } /// Get the space-separated set of activated features for the standard @@ -433,6 +442,12 @@ impl Build { if self.config.rust_optimize {"release"} else {"debug"} } + fn tools_dir(&self, compiler: Compiler) -> PathBuf { + let out = self.out.join(&*compiler.host).join(format!("stage{}-tools-bin", compiler.stage)); + t!(fs::create_dir_all(&out)); + out + } + /// Get the directory for incremental by-products when using the /// given compiler. fn incremental_dir(&self, compiler: Compiler) -> PathBuf { @@ -612,7 +627,7 @@ impl Build { /// Returns the path to the C compiler for the target specified. fn cc(&self, target: Interned) -> &Path { - self.cc[&target].0.path() + self.cc[&target].path() } /// Returns a list of flags to pass to the C compiler for the target @@ -620,7 +635,7 @@ impl Build { fn cflags(&self, target: Interned) -> Vec { // Filter out -O and /O (the optimization flags) that we picked up from // cc-rs because the build scripts will determine that for themselves. - let mut base = self.cc[&target].0.args().iter() + let mut base = self.cc[&target].args().iter() .map(|s| s.to_string_lossy().into_owned()) .filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) .collect::>(); @@ -644,7 +659,7 @@ impl Build { /// Returns the path to the `ar` archive utility for the target specified. fn ar(&self, target: Interned) -> Option<&Path> { - self.cc[&target].1.as_ref().map(|p| &**p) + self.ar.get(&target).map(|p| &**p) } /// Returns the path to the C++ compiler for the target specified. @@ -657,21 +672,17 @@ impl Build { } } - /// Returns flags to pass to the compiler to generate code for `target`. - fn rustc_flags(&self, target: Interned) -> Vec { - // New flags should be added here with great caution! - // - // It's quite unfortunate to **require** flags to generate code for a - // target, so it should only be passed here if absolutely necessary! - // Most default configuration should be done through target specs rather - // than an entry here. - - let mut base = Vec::new(); - if target != self.config.build && !target.contains("msvc") && - !target.contains("emscripten") { - base.push(format!("-Clinker={}", self.cc(target).display())); + /// Returns the path to the linker for the given target if it needs to be overriden. + fn linker(&self, target: Interned) -> Option<&Path> { + if let Some(linker) = self.config.target_config.get(&target) + .and_then(|c| c.linker.as_ref()) { + Some(linker) + } else if target != self.config.build && + !target.contains("msvc") && !target.contains("emscripten") { + Some(self.cc(target)) + } else { + None } - base } /// Returns if this target should statically link the C runtime, if specified @@ -807,6 +818,11 @@ impl Build { self.package_vers(&self.release_num("rls")) } + /// Returns the value of `package_vers` above for rustfmt + fn rustfmt_package_vers(&self) -> String { + self.package_vers(&self.release_num("rustfmt")) + } + /// Returns the `version` string associated with this compiler for Rust /// itself. /// @@ -859,6 +875,30 @@ impl Build { } } + /// Updates the actual toolstate of a tool. + /// + /// The toolstates are saved to the file specified by the key + /// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be + /// done. The file is updated immediately after this function completes. + pub fn save_toolstate(&self, tool: &str, state: ToolState) { + use std::io::{Seek, SeekFrom}; + + if let Some(ref path) = self.config.save_toolstates { + let mut file = t!(fs::OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(path)); + + let mut current_toolstates: HashMap, ToolState> = + serde_json::from_reader(&mut file).unwrap_or_default(); + current_toolstates.insert(tool.into(), state); + t!(file.seek(SeekFrom::Start(0))); + t!(file.set_len(0)); + t!(serde_json::to_writer(file, ¤t_toolstates)); + } + } + /// Get a list of crates from a root crate. /// /// Returns Vec<(crate, path to crate, is_root_crate)> diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 004f0c31024cd..925a361f0b22e 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -52,11 +52,8 @@ check: $(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS) check-aux: $(Q)$(BOOTSTRAP) test \ - src/tools/cargotest \ src/tools/cargo \ - src/tools/rls \ - src/tools/rustfmt \ - src/tools/miri \ + src/tools/cargotest \ src/test/pretty \ src/test/run-pass/pretty \ src/test/run-fail/pretty \ diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 00852d6930546..a5408ee381bbb 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -110,10 +110,7 @@ impl Step for Llvm { None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon", }; - let llvm_exp_targets = match build.config.llvm_experimental_targets { - Some(ref s) => s, - None => "", - }; + let llvm_exp_targets = &build.config.llvm_experimental_targets; let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"}; @@ -227,6 +224,13 @@ impl Step for Llvm { cfg.build_arg("-j").build_arg(build.jobs().to_string()); cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" ")); cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" ")); + if let Some(ar) = build.ar(target) { + if ar.is_absolute() { + // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it + // tries to resolve this path in the LLVM build directory. + cfg.define("CMAKE_AR", sanitize_cc(ar)); + } + } }; configure_compilers(&mut cfg); @@ -252,11 +256,14 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) { let mut cmd = Command::new(llvm_config); let version = output(cmd.arg("--version")); - if version.starts_with("3.5") || version.starts_with("3.6") || - version.starts_with("3.7") { - return + let mut parts = version.split('.').take(2) + .filter_map(|s| s.parse::().ok()); + if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { + if major > 3 || (major == 3 && minor >= 9) { + return + } } - panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version) + panic!("\n\nbad LLVM version: {}, need >=3.9\n\n", version) } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -309,13 +316,13 @@ impl Step for TestHelpers { .warnings(false) .debug(false) .file(build.src.join("src/rt/rust_test_helpers.c")) - .compile("librust_test_helpers.a"); + .compile("rust_test_helpers"); } } -const OPENSSL_VERS: &'static str = "1.0.2k"; +const OPENSSL_VERS: &'static str = "1.0.2m"; const OPENSSL_SHA256: &'static str = - "6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0"; + "8c6ff15ec6b319b50788f42c7abc2890c08ba5a1cdcd3810eb9092deada37b0f"; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Openssl { @@ -352,34 +359,51 @@ impl Step for Openssl { // originally from https://www.openssl.org/source/... let url = format!("https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/{}", name); - let mut ok = false; + let mut last_error = None; for _ in 0..3 { let status = Command::new("curl") .arg("-o").arg(&tmp) + .arg("-f") // make curl fail if the URL does not return HTTP 200 .arg(&url) .status() .expect("failed to spawn curl"); - if status.success() { - ok = true; - break + + // Retry if download failed. + if !status.success() { + last_error = Some(status.to_string()); + continue; } + + // Ensure the hash is correct. + let mut shasum = if target.contains("apple") || build.build.contains("netbsd") { + let mut cmd = Command::new("shasum"); + cmd.arg("-a").arg("256"); + cmd + } else { + Command::new("sha256sum") + }; + let output = output(&mut shasum.arg(&tmp)); + let found = output.split_whitespace().next().unwrap(); + + // If the hash is wrong, probably the download is incomplete or S3 served an error + // page. In any case, retry. + if found != OPENSSL_SHA256 { + last_error = Some(format!( + "downloaded openssl sha256 different\n\ + expected: {}\n\ + found: {}\n", + OPENSSL_SHA256, + found + )); + continue; + } + + // Everything is fine, so exit the retry loop. + last_error = None; + break; } - if !ok { - panic!("failed to download openssl source") - } - let mut shasum = if target.contains("apple") { - let mut cmd = Command::new("shasum"); - cmd.arg("-a").arg("256"); - cmd - } else { - Command::new("sha256sum") - }; - let output = output(&mut shasum.arg(&tmp)); - let found = output.split_whitespace().next().unwrap(); - if found != OPENSSL_SHA256 { - panic!("downloaded openssl sha256 different\n\ - expected: {}\n\ - found: {}\n", OPENSSL_SHA256, found); + if let Some(error) = last_error { + panic!("failed to download openssl source: {}", error); } t!(fs::rename(&tmp, &tarball)); } @@ -387,7 +411,7 @@ impl Step for Openssl { let dst = build.openssl_install_dir(target).unwrap(); drop(fs::remove_dir_all(&obj)); drop(fs::remove_dir_all(&dst)); - build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out)); + build.run(Command::new("tar").arg("zxf").arg(&tarball).current_dir(&out)); let mut configure = Command::new("perl"); configure.arg(obj.join("Configure")); @@ -419,10 +443,12 @@ impl Step for Openssl { "powerpc64-unknown-linux-gnu" => "linux-ppc64", "powerpc64le-unknown-linux-gnu" => "linux-ppc64le", "s390x-unknown-linux-gnu" => "linux64-s390x", + "sparc64-unknown-linux-gnu" => "linux64-sparcv9", "sparc64-unknown-netbsd" => "BSD-sparc64", "x86_64-apple-darwin" => "darwin64-x86_64-cc", "x86_64-linux-android" => "linux-x86_64", "x86_64-unknown-freebsd" => "BSD-x86_64", + "x86_64-unknown-dragonfly" => "BSD-x86_64", "x86_64-unknown-linux-gnu" => "linux-x86_64", "x86_64-unknown-linux-musl" => "linux-x86_64", "x86_64-unknown-netbsd" => "BSD-x86_64", diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 8b23be69a85cf..bc275b7fc745c 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -78,7 +78,7 @@ pub fn check(build: &mut Build) { } let mut cmd_finder = Finder::new(); - // If we've got a git directory we're gona need git to update + // If we've got a git directory we're gonna need git to update // submodules and learn about various other aspects. if build.rust_info.is_git() { cmd_finder.must_have("git"); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index a05e58e6a2270..fa9bdc43c378c 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -11,7 +11,7 @@ use std::fs; use std::env; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, exit}; use Mode; use Compiler; @@ -38,24 +38,40 @@ impl Step for CleanTools { run.never() } - /// Build a tool in `src/tools` - /// - /// This will build the specified tool with the specified `host` compiler in - /// `stage` into the normal cargo output directory. fn run(self, builder: &Builder) { let build = builder.build; let compiler = self.compiler; let target = self.target; let mode = self.mode; - let stamp = match mode { - Mode::Libstd => libstd_stamp(build, compiler, target), - Mode::Libtest => libtest_stamp(build, compiler, target), - Mode::Librustc => librustc_stamp(build, compiler, target), - _ => panic!(), + // This is for the original compiler, but if we're forced to use stage 1, then + // std/test/rustc stamps won't exist in stage 2, so we need to get those from stage 1, since + // we copy the libs forward. + let tools_dir = build.stage_out(compiler, Mode::Tool); + let compiler = if builder.force_use_stage1(compiler, target) { + builder.compiler(1, compiler.host) + } else { + compiler }; - let out_dir = build.cargo_out(compiler, Mode::Tool, target); - build.clear_if_dirty(&out_dir, &stamp); + + for &cur_mode in &[Mode::Libstd, Mode::Libtest, Mode::Librustc] { + let stamp = match cur_mode { + Mode::Libstd => libstd_stamp(build, compiler, target), + Mode::Libtest => libtest_stamp(build, compiler, target), + Mode::Librustc => librustc_stamp(build, compiler, target), + _ => panic!(), + }; + + if build.clear_if_dirty(&tools_dir, &stamp) { + break; + } + + // If we are a rustc tool, and std changed, we also need to clear ourselves out -- our + // dependencies depend on std. Therefore, we iterate up until our own mode. + if mode == cur_mode { + break; + } + } } } @@ -70,7 +86,7 @@ struct ToolBuild { } impl Step for ToolBuild { - type Output = PathBuf; + type Output = Option; fn should_run(run: ShouldRun) -> ShouldRun { run.never() @@ -80,7 +96,7 @@ impl Step for ToolBuild { /// /// This will build the specified tool with the specified `host` compiler in /// `stage` into the normal cargo output directory. - fn run(self, builder: &Builder) -> PathBuf { + fn run(self, builder: &Builder) -> Option { let build = builder.build; let compiler = self.compiler; let target = self.target; @@ -99,8 +115,35 @@ impl Step for ToolBuild { println!("Building stage{} tool {} ({})", compiler.stage, tool, target); let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path); - build.run_expecting(&mut cargo, expectation); - build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host)) + let is_expected = build.try_run(&mut cargo, expectation); + // If the expectation is "Failing", `try_run` returning true actually + // means a build-failure is successfully observed, i.e. the tool is + // broken. Thus the XOR here. + // Sorry for the complicated logic, but we can remove this expectation + // logic after #45861 is fully fixed. + build.save_toolstate(tool, if is_expected ^ (expectation == BuildExpectation::Failing) { + ToolState::Compiling + } else { + ToolState::Broken + }); + + if !is_expected { + if expectation == BuildExpectation::None { + exit(1); + } else { + return None; + } + } + + if expectation == BuildExpectation::Succeeding || expectation == BuildExpectation::None { + let cargo_out = build.cargo_out(compiler, Mode::Tool, target) + .join(exe(tool, &compiler.host)); + let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host)); + copy(&cargo_out, &bin); + Some(bin) + } else { + None + } } } @@ -169,12 +212,12 @@ macro_rules! tool { } pub fn tool_default_stage(&self, tool: Tool) -> u32 { - // Compile the error-index in the top stage as it depends on - // rustdoc, so we want to avoid recompiling rustdoc twice if we - // can. Otherwise compile everything else in stage0 as there's - // no need to rebootstrap everything + // Compile the error-index in the same stage as rustdoc to avoid + // recompiling rustdoc twice if we can. Otherwise compile + // everything else in stage0 as there's no need to rebootstrap + // everything. match tool { - Tool::ErrorIndex => self.top_stage, + Tool::ErrorIndex if self.top_stage >= 2 => self.top_stage, _ => 0, } } @@ -209,7 +252,7 @@ macro_rules! tool { mode: $mode, path: $path, expectation: BuildExpectation::None, - }) + }).expect("expected to build -- BuildExpectation::None") } } )+ @@ -257,7 +300,7 @@ impl Step for RemoteTestServer { mode: Mode::Libstd, path: "src/tools/remote-test-server", expectation: BuildExpectation::None, - }) + }).expect("expected to build -- BuildExpectation::None") } } @@ -375,74 +418,70 @@ impl Step for Cargo { mode: Mode::Librustc, path: "src/tools/cargo", expectation: BuildExpectation::None, - }) + }).expect("BuildExpectation::None - expected to build") } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct Clippy { - pub compiler: Compiler, - pub target: Interned, -} +macro_rules! tool_extended { + (($sel:ident, $builder:ident), + $($name:ident, + $toolstate:ident, + $path:expr, + $tool_name:expr, + $extra_deps:block;)+) => { + $( + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] + pub struct $name { + pub compiler: Compiler, + pub target: Interned, + } -impl Step for Clippy { - type Output = PathBuf; - const DEFAULT: bool = false; - const ONLY_HOSTS: bool = true; + impl Step for $name { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; - fn should_run(run: ShouldRun) -> ShouldRun { - run.path("src/tools/clippy") - } + fn should_run(run: ShouldRun) -> ShouldRun { + let builder = run.builder; + run.path($path).default_condition(builder.build.config.extended) + } - fn make_run(run: RunConfig) { - run.builder.ensure(Clippy { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build), - target: run.target, - }); + fn make_run(run: RunConfig) { + run.builder.ensure($name { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build), + target: run.target, + }); + } + + fn run($sel, $builder: &Builder) -> Option { + $extra_deps + let toolstate = $builder.build.config.toolstate.$toolstate; + $builder.ensure(ToolBuild { + compiler: $sel.compiler, + target: $sel.target, + tool: $tool_name, + mode: Mode::Librustc, + path: $path, + expectation: toolstate.passes(ToolState::Compiling), + }) + } + } + )+ } +} - fn run(self, builder: &Builder) -> PathBuf { +tool_extended!((self, builder), + Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {}; + Clippy, clippy, "src/tools/clippy", "clippy-driver", { // Clippy depends on procedural macros (serde), which requires a full host // compiler to be available, so we need to depend on that. builder.ensure(compile::Rustc { compiler: self.compiler, target: builder.build.build, }); - builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: "clippy", - mode: Mode::Librustc, - path: "src/tools/clippy", - expectation: builder.build.config.toolstate.clippy.passes(ToolState::Compiling), - }) - } -} - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct Rls { - pub compiler: Compiler, - pub target: Interned, -} - -impl Step for Rls { - type Output = PathBuf; - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun) -> ShouldRun { - let builder = run.builder; - run.path("src/tools/rls").default_condition(builder.build.config.extended) - } - - fn make_run(run: RunConfig) { - run.builder.ensure(Rls { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build), - target: run.target, - }); - } - - fn run(self, builder: &Builder) -> PathBuf { + }; + Miri, miri, "src/tools/miri", "miri", {}; + Rls, rls, "src/tools/rls", "rls", { builder.ensure(native::Openssl { target: self.target, }); @@ -452,87 +491,9 @@ impl Step for Rls { compiler: self.compiler, target: builder.build.build, }); - builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: "rls", - mode: Mode::Librustc, - path: "src/tools/rls", - expectation: builder.build.config.toolstate.rls.passes(ToolState::Compiling), - }) - } -} - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct Rustfmt { - pub compiler: Compiler, - pub target: Interned, -} - -impl Step for Rustfmt { - type Output = PathBuf; - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun) -> ShouldRun { - let builder = run.builder; - run.path("src/tools/rustfmt").default_condition(builder.build.config.extended) - } - - fn make_run(run: RunConfig) { - run.builder.ensure(Rustfmt { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build), - target: run.target, - }); - } - - fn run(self, builder: &Builder) -> PathBuf { - builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: "rustfmt", - mode: Mode::Librustc, - path: "src/tools/rustfmt", - expectation: builder.build.config.toolstate.rustfmt.passes(ToolState::Compiling), - }) - } -} - - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct Miri { - pub compiler: Compiler, - pub target: Interned, -} - -impl Step for Miri { - type Output = PathBuf; - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun) -> ShouldRun { - let build_miri = run.builder.build.config.test_miri; - run.path("src/tools/miri").default_condition(build_miri) - } - - fn make_run(run: RunConfig) { - run.builder.ensure(Miri { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build), - target: run.target, - }); - } - - fn run(self, builder: &Builder) -> PathBuf { - builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: "miri", - mode: Mode::Librustc, - path: "src/tools/miri", - expectation: builder.build.config.toolstate.miri.passes(ToolState::Compiling), - }) - } -} + }; + Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {}; +); impl<'a> Builder<'a> { /// Get a `Command` which is ready to run `tool` in `stage` built for @@ -561,7 +522,7 @@ impl<'a> Builder<'a> { if compiler.host.contains("msvc") { let curpaths = env::var_os("PATH").unwrap_or_default(); let curpaths = env::split_paths(&curpaths).collect::>(); - for &(ref k, ref v) in self.cc[&compiler.host].0.env() { + for &(ref k, ref v) in self.cc[&compiler.host].env() { if k != "PATH" { continue } diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 8a113f6b4d2df..00dbcc86af4d1 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -10,7 +10,7 @@ use build_helper::BuildExpectation; -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] /// Whether a tool can be compiled, tested or neither pub enum ToolState { /// The tool compiles successfully, but the test suite fails @@ -31,6 +31,13 @@ impl ToolState { BuildExpectation::Failing } } + + pub fn testing(&self) -> bool { + match *self { + ToolState::Testing => true, + _ => false, + } + } } impl Default for ToolState { diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index a521dd0945391..2506048858f2b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -14,8 +14,9 @@ //! not a lot of interesting happenings here unfortunately. use std::env; -use std::fs; -use std::io::{self, Write}; +use std::str; +use std::fs::{self, File}; +use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{SystemTime, Instant}; @@ -50,6 +51,22 @@ pub fn copy(src: &Path, dst: &Path) { t!(filetime::set_file_times(dst, atime, mtime)); } +pub fn read_stamp_file(stamp: &Path) -> Vec { + let mut paths = Vec::new(); + let mut contents = Vec::new(); + t!(t!(File::open(stamp)).read_to_end(&mut contents)); + // This is the method we use for extracting paths from the stamp file passed to us. See + // run_cargo for more information (in compile.rs). + for part in contents.split(|b| *b == 0) { + if part.is_empty() { + continue + } + let path = PathBuf::from(t!(str::from_utf8(part))); + paths.push(path); + } + paths +} + /// Copies the `src` directory recursively to `dst`. Both are assumed to exist /// when this function is called. pub fn cp_r(src: &Path, dst: &Path) { diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index e81dab70b43e7..2b6e2828cfb4b 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -138,27 +138,6 @@ pub fn gnu_target(target: &str) -> String { } } -pub fn cc2ar(cc: &Path, target: &str) -> Option { - if target.contains("msvc") { - None - } else if target.contains("musl") { - Some(PathBuf::from("ar")) - } else if target.contains("openbsd") { - Some(PathBuf::from("ar")) - } else { - let parent = cc.parent().unwrap(); - let file = cc.file_name().unwrap().to_str().unwrap(); - for suffix in &["gcc", "cc", "clang"] { - if let Some(idx) = file.rfind(suffix) { - let mut file = file[..idx].to_owned(); - file.push_str("ar"); - return Some(parent.join(&file)); - } - } - Some(parent.join(file)) - } -} - pub fn make(host: &str) -> PathBuf { if host.contains("bitrig") || host.contains("dragonfly") || host.contains("freebsd") || host.contains("netbsd") || @@ -211,6 +190,9 @@ pub fn mtime(path: &Path) -> FileTime { /// /// Uses last-modified time checks to verify this. pub fn up_to_date(src: &Path, dst: &Path) -> bool { + if !dst.exists() { + return false; + } let threshold = mtime(dst); let meta = match fs::metadata(src) { Ok(meta) => meta, diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index adce6a00d4623..8d4dbc399986f 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -22,6 +22,48 @@ Images will output artifacts in an `obj` dir at the root of a repository. - `scripts` contains files shared by docker images - `disabled` contains images that are not built on travis +## Docker Toolbox on Windows + +For Windows before Windows 10, the docker images can be run on Windows via +[Docker Toolbox]. There are several preparation needs to be made before running +a Docker image. + +1. Stop the virtual machine from the terminal with `docker-machine stop` + +2. If your Rust source is placed outside of `C:\Users\**`, e.g. if you place the + repository in the `E:\rust` folder, please add a shared folder from + VirtualBox by: + + 1. Select the "default" virtual machine inside VirtualBox, then click + "Settings" + 2. Go to "Shared Folders", click "Add shared folder" (the folder icon with + a plus sign), fill in the following information, then click "OK": + + * Folder path: `E:\rust` + * Folder name: `e/rust` + * Read-only: ☐ *unchecked* + * Auto-mount: ☑ *checked* + * Make Permanent: ☑ *checked* + +3. VirtualBox might not support creating symbolic links inside a shared folder + by default. You can enable it manually by running these from `cmd.exe`: + + ```bat + cd "C:\Program Files\Oracle\VirtualBox" + VBoxManage setextradata default VBoxInternal2/SharedFoldersEnableSymlinksCreate/e/rust 1 + :: ^~~~~~ + :: folder name + ``` + +4. Restart the virtual machine from terminal with `docker-machine start`. + +To run the image, + +1. Launch the "Docker Quickstart Terminal". +2. Execute `./src/ci/docker/run.sh $image_name` as explained at the beginning. + +[Docker Toolbox]: https://www.docker.com/products/docker-toolbox + ## Cross toolchains A number of these images take quite a long time to compile as they're building @@ -137,7 +179,7 @@ For targets: `armv7-unknown-linux-gnueabihf` libraries like jemalloc. See the mk/cfg/arm(v7)-uknown-linux-gnueabi{,hf}.mk file in Rust's source code. -## `aarch64-linux-gnu.config` +### `aarch64-linux-gnu.config` For targets: `aarch64-unknown-linux-gnu` @@ -150,7 +192,7 @@ For targets: `aarch64-unknown-linux-gnu` - C compiler > gcc version = 5.2.0 - C compiler > C++ = ENABLE -- to cross compile LLVM -## `powerpc-linux-gnu.config` +### `powerpc-linux-gnu.config` For targets: `powerpc-unknown-linux-gnu` @@ -165,7 +207,7 @@ For targets: `powerpc-unknown-linux-gnu` - C compiler > gcc version = 4.9.3 - C compiler > C++ = ENABLE -- to cross compile LLVM -## `powerpc64-linux-gnu.config` +### `powerpc64-linux-gnu.config` For targets: `powerpc64-unknown-linux-gnu` @@ -184,7 +226,7 @@ For targets: `powerpc64-unknown-linux-gnu` (+) These CPU options match the configuration of the toolchains in RHEL6. -## `s390x-linux-gnu.config` +### `s390x-linux-gnu.config` For targets: `s390x-unknown-linux-gnu` diff --git a/src/ci/docker/arm-android/Dockerfile b/src/ci/docker/arm-android/Dockerfile index 49d07d28d3c8e..f2773a720cfbc 100644 --- a/src/ci/docker/arm-android/Dockerfile +++ b/src/ci/docker/arm-android/Dockerfile @@ -5,21 +5,27 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm 9 + download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm 14 +# Note: +# Do not upgrade to `openjdk-9-jre-headless`, as it will cause certificate error +# when installing the Android SDK (see PR #45193). This is unfortunate, but +# every search result suggested either disabling HTTPS or replacing JDK 9 by +# JDK 8 as the solution (e.g. https://stackoverflow.com/q/41421340). :| RUN dpkg --add-architecture i386 && \ apt-get update && \ apt-get install -y --no-install-recommends \ libgl1-mesa-glx \ libpulse0 \ libstdc++6:i386 \ - openjdk-9-jre-headless \ + openjdk-8-jre-headless \ tzdata COPY scripts/android-sdk.sh /scripts/ RUN . /scripts/android-sdk.sh && \ - download_and_create_avd tools_r25.2.5-linux.zip armeabi-v7a 18 + download_and_create_avd 4333796 armeabi-v7a 18 +ENV PATH=$PATH:/android/sdk/emulator ENV PATH=$PATH:/android/sdk/tools ENV PATH=$PATH:/android/sdk/platform-tools @@ -27,7 +33,7 @@ ENV TARGETS=arm-linux-androideabi ENV RUST_CONFIGURE_ARGS \ --target=$TARGETS \ - --arm-linux-androideabi-ndk=/android/ndk/arm-9 + --arm-linux-androideabi-ndk=/android/ndk/arm-14 ENV SCRIPT python2.7 ../x.py test --target $TARGETS diff --git a/src/ci/docker/asmjs/Dockerfile b/src/ci/docker/asmjs/Dockerfile index 28caf1fb57a48..07849a20d0045 100644 --- a/src/ci/docker/asmjs/Dockerfile +++ b/src/ci/docker/asmjs/Dockerfile @@ -16,6 +16,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/emscripten.sh /scripts/ RUN bash /scripts/emscripten.sh +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + ENV PATH=$PATH:/emsdk-portable ENV PATH=$PATH:/emsdk-portable/clang/e1.37.13_64bit/ ENV PATH=$PATH:/emsdk-portable/emscripten/1.37.13/ @@ -29,6 +32,3 @@ ENV TARGETS=asmjs-unknown-emscripten ENV RUST_CONFIGURE_ARGS --target=$TARGETS ENV SCRIPT python2.7 ../x.py test --target $TARGETS - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/disabled/aarch64-gnu/Dockerfile b/src/ci/docker/disabled/aarch64-gnu/Dockerfile index 9a0e45312235e..fedb4094c8aaa 100644 --- a/src/ci/docker/disabled/aarch64-gnu/Dockerfile +++ b/src/ci/docker/disabled/aarch64-gnu/Dockerfile @@ -31,7 +31,7 @@ WORKDIR /build # The `config` config file was a previously generated config file for # the kernel. This file was generated by running `make defconfig` # followed by `make menuconfig` and then enabling the IPv6 protocol page. -COPY disabled/aarch64-gnu/config /build/.config +COPY aarch64-gnu/config /build/.config RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.42.tar.xz | \ tar xJf - && \ cd /build/linux-4.4.42 && \ diff --git a/src/ci/docker/disabled/dist-aarch64-android/Dockerfile b/src/ci/docker/disabled/dist-aarch64-android/Dockerfile index 20d823a3d7338..ce5e8cfaf0958 100644 --- a/src/ci/docker/disabled/dist-aarch64-android/Dockerfile +++ b/src/ci/docker/disabled/dist-aarch64-android/Dockerfile @@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm64 21 + download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm64 21 ENV PATH=$PATH:/android/ndk/arm64-21/bin diff --git a/src/ci/docker/disabled/dist-armv7-android/Dockerfile b/src/ci/docker/disabled/dist-armv7-android/Dockerfile index 3435d641a13c5..3177fa2147fa1 100644 --- a/src/ci/docker/disabled/dist-armv7-android/Dockerfile +++ b/src/ci/docker/disabled/dist-armv7-android/Dockerfile @@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_ndk android-ndk-r13b-linux-x86_64.zip && \ - make_standalone_toolchain arm 9 && \ + download_ndk android-ndk-r15c-linux-x86_64.zip && \ + make_standalone_toolchain arm 14 && \ make_standalone_toolchain arm 21 && \ remove_ndk RUN chmod 777 /android/ndk && \ ln -s /android/ndk/arm-21 /android/ndk/arm -ENV PATH=$PATH:/android/ndk/arm-9/bin +ENV PATH=$PATH:/android/ndk/arm-14/bin -ENV DEP_Z_ROOT=/android/ndk/arm-9/sysroot/usr/ +ENV DEP_Z_ROOT=/android/ndk/arm-14/sysroot/usr/ ENV HOSTS=armv7-linux-androideabi @@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-cargo-openssl-static -# We support api level 9, but api level 21 is required to build llvm. To +# We support api level 14, but api level 21 is required to build llvm. To # overcome this problem we use a ndk with api level 21 to build llvm and then -# switch to a ndk with api level 9 to complete the build. When the linker is +# switch to a ndk with api level 14 to complete the build. When the linker is # invoked there are missing symbols (like sigsetempty, not available with api -# level 9), the default linker behavior is to generate an error, to allow the +# level 14), the default linker behavior is to generate an error, to allow the # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/arm && \ - ln -s /android/ndk/arm-9 /android/ndk/arm && \ + ln -s /android/ndk/arm-14 /android/ndk/arm && \ python2.7 ../x.py dist --host $HOSTS --target $HOSTS) COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/disabled/dist-i686-android/Dockerfile b/src/ci/docker/disabled/dist-i686-android/Dockerfile index 4bb7053760f9c..ace9c4feb4f3b 100644 --- a/src/ci/docker/disabled/dist-i686-android/Dockerfile +++ b/src/ci/docker/disabled/dist-i686-android/Dockerfile @@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_ndk android-ndk-r13b-linux-x86_64.zip && \ - make_standalone_toolchain x86 9 && \ + download_ndk android-ndk-r15c-linux-x86_64.zip && \ + make_standalone_toolchain x86 14 && \ make_standalone_toolchain x86 21 && \ remove_ndk RUN chmod 777 /android/ndk && \ ln -s /android/ndk/x86-21 /android/ndk/x86 -ENV PATH=$PATH:/android/ndk/x86-9/bin +ENV PATH=$PATH:/android/ndk/x86-14/bin -ENV DEP_Z_ROOT=/android/ndk/x86-9/sysroot/usr/ +ENV DEP_Z_ROOT=/android/ndk/x86-14/sysroot/usr/ ENV HOSTS=i686-linux-android @@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \ --enable-extended \ --enable-cargo-openssl-static -# We support api level 9, but api level 21 is required to build llvm. To +# We support api level 14, but api level 21 is required to build llvm. To # overcome this problem we use a ndk with api level 21 to build llvm and then -# switch to a ndk with api level 9 to complete the build. When the linker is +# switch to a ndk with api level 14 to complete the build. When the linker is # invoked there are missing symbols (like sigsetempty, not available with api -# level 9), the default linker behavior is to generate an error, to allow the +# level 14), the default linker behavior is to generate an error, to allow the # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/x86 && \ - ln -s /android/ndk/x86-9 /android/ndk/x86 && \ + ln -s /android/ndk/x86-14 /android/ndk/x86 && \ python2.7 ../x.py dist --host $HOSTS --target $HOSTS) COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/disabled/dist-x86_64-android/Dockerfile b/src/ci/docker/disabled/dist-x86_64-android/Dockerfile index 525b218417b67..322d26f0adc4c 100644 --- a/src/ci/docker/disabled/dist-x86_64-android/Dockerfile +++ b/src/ci/docker/disabled/dist-x86_64-android/Dockerfile @@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip x86_64 21 + download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip x86_64 21 ENV PATH=$PATH:/android/ndk/x86_64-21/bin diff --git a/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile b/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile new file mode 100644 index 0000000000000..f3509efdb988b --- /dev/null +++ b/src/ci/docker/disabled/dist-x86_64-dragonfly/Dockerfile @@ -0,0 +1,36 @@ +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 \ + bzip2 \ + xz-utils \ + wget \ + libssl-dev \ + bsdtar \ + pkg-config + + +COPY dist-x86_64-dragonfly/build-toolchain.sh /tmp/ +COPY dist-x86_64-dragonfly/patch-toolchain /tmp/ +RUN /tmp/build-toolchain.sh /tmp/patch-toolchain + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV \ + AR_x86_64_unknown_dragonfly=x86_64-unknown-dragonfly-ar \ + CC_x86_64_unknown_dragonfly=x86_64-unknown-dragonfly-gcc \ + CXX_x86_64_unknown_dragonfly=x86_64-unknown-dragonfly-g++ + +ENV HOSTS=x86_64-unknown-dragonfly + +ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended +ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/disabled/dist-x86_64-dragonfly/build-toolchain.sh b/src/ci/docker/disabled/dist-x86_64-dragonfly/build-toolchain.sh new file mode 100755 index 0000000000000..2ebbe0cdee9b8 --- /dev/null +++ b/src/ci/docker/disabled/dist-x86_64-dragonfly/build-toolchain.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# 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. + +set -ex + +ARCH=x86_64 +PATCH_TOOLCHAIN=$1 +BINUTILS=2.25.1 +GCC=6.4.0 + +hide_output() { + set +x + on_err=" +echo ERROR: An error was encountered with the build. +cat /tmp/build.log +exit 1 +" + trap "$on_err" ERR + bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & + PING_LOOP_PID=$! + $@ &> /tmp/build.log + trap - ERR + kill $PING_LOOP_PID + set -x +} + +mkdir binutils +cd binutils + +# First up, build binutils +curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.bz2 | tar xjf - +mkdir binutils-build +cd binutils-build +hide_output ../binutils-$BINUTILS/configure \ + --target=$ARCH-unknown-dragonfly +hide_output make -j10 +hide_output make install +cd ../.. +rm -rf binutils + +# Next, download the DragonFly libc and relevant header files + +URL=http://mirror-master.dragonflybsd.org/iso-images/dfly-x86_64-5.0.0_REL.iso.bz2 +mkdir dragonfly +curl $URL | bzcat | bsdtar xf - -C dragonfly ./usr/include ./usr/lib ./lib + +dst=/usr/local/$ARCH-unknown-dragonfly + +mkdir -p $dst/lib +cp -r dragonfly/usr/include $dst/ +cp dragonfly/usr/lib/crt1.o $dst/lib +cp dragonfly/usr/lib/Scrt1.o $dst/lib +cp dragonfly/usr/lib/crti.o $dst/lib +cp dragonfly/usr/lib/crtn.o $dst/lib +cp dragonfly/usr/lib/libc.a $dst/lib +cp dragonfly/usr/lib/libutil.a $dst/lib +cp dragonfly/usr/lib/libm.a $dst/lib +cp dragonfly/usr/lib/librt.so.0 $dst/lib +cp dragonfly/usr/lib/libexecinfo.so.1 $dst/lib +cp dragonfly/lib/libc.so.8 $dst/lib +cp dragonfly/lib/libm.so.4 $dst/lib +cp dragonfly/lib/libutil.so.4 $dst/lib +cp dragonfly/usr/lib/libpthread.so $dst/lib/libpthread.so +cp dragonfly/usr/lib/thread/libthread_xu.so.2 $dst/lib/libpthread.so.0 + +ln -s libc.so.8 $dst/lib/libc.so +ln -s libm.so.4 $dst/lib/libm.so +ln -s librt.so.0 $dst/lib/librt.so +ln -s libutil.so.4 $dst/lib/libutil.so +ln -s libexecinfo.so.1 $dst/lib/libexecinfo.so +rm -rf dragonfly + +# Finally, download and build gcc to target DragonFly +mkdir gcc +cd gcc +curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.gz | tar xzf - +cd gcc-$GCC + +# The following three patches are taken from DragonFly's dports collection: +# https://github.com/DragonFlyBSD/DPorts/tree/master/lang/gcc5 +# The dports specification for gcc5 contains a few more patches, but they are +# not relevant in this situation, as they are for a language we don't need +# (e.g. java), or a platform which is not supported by DragonFly (e.g. i386, +# powerpc64, ia64, arm). +# +# These patches probably only need to be updated in case the gcc version is +# updated. + +patch -p0 < $PATCH_TOOLCHAIN + +./contrib/download_prerequisites + +mkdir ../gcc-build +cd ../gcc-build +hide_output ../gcc-$GCC/configure \ + --enable-languages=c,c++ \ + --target=$ARCH-unknown-dragonfly \ + --disable-multilib \ + --disable-nls \ + --disable-libgomp \ + --disable-libquadmath \ + --disable-libssp \ + --disable-libvtv \ + --disable-libcilkrts \ + --disable-libada \ + --disable-libsanitizer \ + --disable-libquadmath-support \ + --disable-lto +hide_output make -j10 +hide_output make install +cd ../.. +rm -rf gcc diff --git a/src/ci/docker/disabled/dist-x86_64-dragonfly/patch-toolchain b/src/ci/docker/disabled/dist-x86_64-dragonfly/patch-toolchain new file mode 100644 index 0000000000000..98424309ee23d --- /dev/null +++ b/src/ci/docker/disabled/dist-x86_64-dragonfly/patch-toolchain @@ -0,0 +1,23 @@ +--- libstdc++-v3/config/os/bsd/dragonfly/os_defines.h.orig 2015-07-09 16:08:54 UTC ++++ libstdc++-v3/config/os/bsd/dragonfly/os_defines.h +@@ -29,4 +29,9 @@ + // System-specific #define, typedefs, corrections, etc, go here. This + // file will come before all others. + ++#define _GLIBCXX_USE_C99_CHECK 1 ++#define _GLIBCXX_USE_C99_DYNAMIC (!(__ISO_C_VISIBLE >= 1999)) ++#define _GLIBCXX_USE_C99_LONG_LONG_CHECK 1 ++#define _GLIBCXX_USE_C99_LONG_LONG_DYNAMIC (_GLIBCXX_USE_C99_DYNAMIC || !defined __LONG_LONG_SUPPORTED) ++ + #endif +--- libstdc++-v3/configure.orig 2016-05-26 18:34:47.163132921 +0200 ++++ libstdc++-v3/configure 2016-05-26 18:35:29.594590648 +0200 +@@ -52013,7 +52013,7 @@ + + ;; + +- *-freebsd*) ++ *-freebsd* | *-dragonfly*) + SECTION_FLAGS='-ffunction-sections -fdata-sections' + + diff --git a/src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh b/src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh index 0776d448984ee..a1115e254b5b2 100755 --- a/src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh +++ b/src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh b/src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh index 0f6034cdb8620..a37532e203aa4 100755 --- a/src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh +++ b/src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/disabled/wasm32-exp/Dockerfile b/src/ci/docker/disabled/wasm32-exp/Dockerfile index 6323369421bb4..8653b0e8b465e 100644 --- a/src/ci/docker/disabled/wasm32-exp/Dockerfile +++ b/src/ci/docker/disabled/wasm32-exp/Dockerfile @@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # emscripten COPY scripts/emscripten-wasm.sh /scripts/ -COPY disabled/wasm32-exp/node.sh /usr/local/bin/node +COPY wasm32-exp/node.sh /usr/local/bin/node RUN bash /scripts/emscripten-wasm.sh # cache diff --git a/src/ci/docker/disabled/wasm32-exp/node.sh b/src/ci/docker/disabled/wasm32-exp/node.sh index dfa7f221ffa20..2bfddb0de99b0 100755 --- a/src/ci/docker/disabled/wasm32-exp/node.sh +++ b/src/ci/docker/disabled/wasm32-exp/node.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-aarch64-linux/build-toolchains.sh b/src/ci/docker/dist-aarch64-linux/build-toolchains.sh index 94f785c96f815..22b719bb30755 100755 --- a/src/ci/docker/dist-aarch64-linux/build-toolchains.sh +++ b/src/ci/docker/dist-aarch64-linux/build-toolchains.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-android/Dockerfile b/src/ci/docker/dist-android/Dockerfile index a36f7fc1ac528..5d7545a3c2a95 100644 --- a/src/ci/docker/dist-android/Dockerfile +++ b/src/ci/docker/dist-android/Dockerfile @@ -6,9 +6,9 @@ RUN sh /scripts/android-base-apt-get.sh # ndk COPY scripts/android-ndk.sh /scripts/ RUN . /scripts/android-ndk.sh && \ - download_ndk android-ndk-r13b-linux-x86_64.zip && \ - make_standalone_toolchain arm 9 && \ - make_standalone_toolchain x86 9 && \ + download_ndk android-ndk-r15c-linux-x86_64.zip && \ + make_standalone_toolchain arm 14 && \ + make_standalone_toolchain x86 14 && \ make_standalone_toolchain arm64 21 && \ make_standalone_toolchain x86_64 21 && \ remove_ndk @@ -23,9 +23,9 @@ ENV TARGETS=$TARGETS,x86_64-linux-android ENV RUST_CONFIGURE_ARGS \ --target=$TARGETS \ --enable-extended \ - --arm-linux-androideabi-ndk=/android/ndk/arm-9 \ - --armv7-linux-androideabi-ndk=/android/ndk/arm-9 \ - --i686-linux-android-ndk=/android/ndk/x86-9 \ + --arm-linux-androideabi-ndk=/android/ndk/arm-14 \ + --armv7-linux-androideabi-ndk=/android/ndk/arm-14 \ + --i686-linux-android-ndk=/android/ndk/x86-14 \ --aarch64-linux-android-ndk=/android/ndk/arm64-21 \ --x86_64-linux-android-ndk=/android/ndk/x86_64-21 diff --git a/src/ci/docker/dist-arm-linux/build-toolchains.sh b/src/ci/docker/dist-arm-linux/build-toolchains.sh index f78ecf9381a1f..c53cca0bb982c 100755 --- a/src/ci/docker/dist-arm-linux/build-toolchains.sh +++ b/src/ci/docker/dist-arm-linux/build-toolchains.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-armhf-linux/build-toolchains.sh b/src/ci/docker/dist-armhf-linux/build-toolchains.sh index df1134d5483c2..964182a5ad544 100755 --- a/src/ci/docker/dist-armhf-linux/build-toolchains.sh +++ b/src/ci/docker/dist-armhf-linux/build-toolchains.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-armv7-linux/build-toolchains.sh b/src/ci/docker/dist-armv7-linux/build-toolchains.sh index 2d395fee792ec..40adfe5d53e0b 100755 --- a/src/ci/docker/dist-armv7-linux/build-toolchains.sh +++ b/src/ci/docker/dist-armv7-linux/build-toolchains.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-fuchsia/Dockerfile b/src/ci/docker/dist-fuchsia/Dockerfile deleted file mode 100644 index bcd95924b427f..0000000000000 --- a/src/ci/docker/dist-fuchsia/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y \ - build-essential \ - bzip2 \ - ca-certificates \ - cmake \ - curl \ - file \ - g++ \ - gdb \ - git \ - libedit-dev \ - make \ - ninja-build \ - nodejs \ - python2.7-dev \ - sudo \ - xz-utils \ - unzip - -WORKDIR /tmp -COPY dist-fuchsia/shared.sh dist-fuchsia/build-toolchain.sh /tmp/ -RUN /tmp/build-toolchain.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV \ - AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \ - CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \ - CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \ - AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \ - CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \ - CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++ - -ENV TARGETS=x86_64-unknown-fuchsia -ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia - -ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended -ENV SCRIPT python2.7 ../x.py dist --target $TARGETS \ No newline at end of file diff --git a/src/ci/docker/dist-i586-gnu-i686-musl/Dockerfile b/src/ci/docker/dist-i586-gnu-i686-musl/Dockerfile index efde3ff52962c..2fb1219681108 100644 --- a/src/ci/docker/dist-i586-gnu-i686-musl/Dockerfile +++ b/src/ci/docker/dist-i586-gnu-i686-musl/Dockerfile @@ -34,6 +34,7 @@ ENV RUST_CONFIGURE_ARGS \ # # See: https://github.com/rust-lang/rust/issues/34978 ENV CFLAGS_i686_unknown_linux_musl=-Wa,-mrelax-relocations=no +ENV CFLAGS_i586_unknown_linux_gnu=-Wa,-mrelax-relocations=no ENV SCRIPT \ python2.7 ../x.py test \ diff --git a/src/ci/docker/dist-i586-gnu-i686-musl/build-musl.sh b/src/ci/docker/dist-i586-gnu-i686-musl/build-musl.sh index ad285a57a84a3..883859d1fa64e 100644 --- a/src/ci/docker/dist-i586-gnu-i686-musl/build-musl.sh +++ b/src/ci/docker/dist-i586-gnu-i686-musl/build-musl.sh @@ -15,7 +15,7 @@ set -ex export CFLAGS="-fPIC -Wa,-mrelax-relocations=no" export CXXFLAGS="-Wa,-mrelax-relocations=no" -MUSL=musl-1.1.16 +MUSL=musl-1.1.17 curl https://www.musl-libc.org/releases/$MUSL.tar.gz | tar xzf - cd $MUSL CC=gcc \ diff --git a/src/ci/docker/dist-i686-freebsd/build-toolchain.sh b/src/ci/docker/dist-i686-freebsd/build-toolchain.sh index 8343327c33bf2..3c86a8e38175e 100755 --- a/src/ci/docker/dist-i686-freebsd/build-toolchain.sh +++ b/src/ci/docker/dist-i686-freebsd/build-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. diff --git a/src/ci/docker/dist-i686-linux/build-binutils.sh b/src/ci/docker/dist-i686-linux/build-binutils.sh index 80aa1f2a01613..f4bdbd80d0edb 100755 --- a/src/ci/docker/dist-i686-linux/build-binutils.sh +++ b/src/ci/docker/dist-i686-linux/build-binutils.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-cmake.sh b/src/ci/docker/dist-i686-linux/build-cmake.sh index 82e46455cb0f0..9a3763d421ad2 100755 --- a/src/ci/docker/dist-i686-linux/build-cmake.sh +++ b/src/ci/docker/dist-i686-linux/build-cmake.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-curl.sh b/src/ci/docker/dist-i686-linux/build-curl.sh index b7d22755a571b..edf3175b81c43 100755 --- a/src/ci/docker/dist-i686-linux/build-curl.sh +++ b/src/ci/docker/dist-i686-linux/build-curl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-gcc.sh b/src/ci/docker/dist-i686-linux/build-gcc.sh index ab2562538d6d7..6b991bb59e4b0 100755 --- a/src/ci/docker/dist-i686-linux/build-gcc.sh +++ b/src/ci/docker/dist-i686-linux/build-gcc.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-git.sh b/src/ci/docker/dist-i686-linux/build-git.sh index 92fa66b496d93..ff62a68629a8b 100755 --- a/src/ci/docker/dist-i686-linux/build-git.sh +++ b/src/ci/docker/dist-i686-linux/build-git.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-headers.sh b/src/ci/docker/dist-i686-linux/build-headers.sh index 4ce38fd9205e2..2f15114d6f980 100755 --- a/src/ci/docker/dist-i686-linux/build-headers.sh +++ b/src/ci/docker/dist-i686-linux/build-headers.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-openssl.sh b/src/ci/docker/dist-i686-linux/build-openssl.sh index 34da0ed631093..e7226ace020bd 100755 --- a/src/ci/docker/dist-i686-linux/build-openssl.sh +++ b/src/ci/docker/dist-i686-linux/build-openssl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-i686-linux/build-python.sh b/src/ci/docker/dist-i686-linux/build-python.sh index a7a450f3c8de7..c6b8cdde4b9af 100755 --- a/src/ci/docker/dist-i686-linux/build-python.sh +++ b/src/ci/docker/dist-i686-linux/build-python.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-powerpc-linux/build-powerpc-toolchain.sh b/src/ci/docker/dist-powerpc-linux/build-powerpc-toolchain.sh index 90a4df0c19583..15211acb4459b 100755 --- a/src/ci/docker/dist-powerpc-linux/build-powerpc-toolchain.sh +++ b/src/ci/docker/dist-powerpc-linux/build-powerpc-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-powerpc64-linux/build-powerpc64-toolchain.sh b/src/ci/docker/dist-powerpc64-linux/build-powerpc64-toolchain.sh index c477cd61f98de..ac6460a472993 100755 --- a/src/ci/docker/dist-powerpc64-linux/build-powerpc64-toolchain.sh +++ b/src/ci/docker/dist-powerpc64-linux/build-powerpc64-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh b/src/ci/docker/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh index 4d3e638916dbf..2f6937afff032 100755 --- a/src/ci/docker/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh +++ b/src/ci/docker/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. @@ -23,7 +23,7 @@ SYSROOT=/usr/local/$TARGET/sysroot mkdir -p $SYSROOT pushd $SYSROOT -centos_base=http://mirror.centos.org/altarch/7.3.1611/os/ppc64le/Packages +centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages/ glibc_v=2.17-157.el7 kernel_v=3.10.0-514.el7 for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do diff --git a/src/ci/docker/dist-s390x-linux/build-s390x-toolchain.sh b/src/ci/docker/dist-s390x-linux/build-s390x-toolchain.sh index b4995e20dc69b..306204dd0e1f6 100755 --- a/src/ci/docker/dist-s390x-linux/build-s390x-toolchain.sh +++ b/src/ci/docker/dist-s390x-linux/build-s390x-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/cross/Dockerfile b/src/ci/docker/dist-various-1/Dockerfile similarity index 88% rename from src/ci/docker/cross/Dockerfile rename to src/ci/docker/dist-various-1/Dockerfile index 05745709a07cb..a616693311a2d 100644 --- a/src/ci/docker/cross/Dockerfile +++ b/src/ci/docker/dist-various-1/Dockerfile @@ -24,19 +24,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /tmp -COPY cross/build-rumprun.sh /tmp/ +COPY dist-various-1/build-rumprun.sh /tmp/ RUN ./build-rumprun.sh -COPY cross/build-arm-musl.sh /tmp/ +COPY dist-various-1/build-arm-musl.sh /tmp/ RUN ./build-arm-musl.sh -COPY cross/install-mips-musl.sh /tmp/ +COPY dist-various-1/install-mips-musl.sh /tmp/ RUN ./install-mips-musl.sh -COPY cross/install-mipsel-musl.sh /tmp/ +COPY dist-various-1/install-mipsel-musl.sh /tmp/ RUN ./install-mipsel-musl.sh -COPY cross/install-x86_64-redox.sh /tmp/ +COPY dist-various-1/install-x86_64-redox.sh /tmp/ RUN ./install-x86_64-redox.sh ENV TARGETS=asmjs-unknown-emscripten diff --git a/src/ci/docker/cross/build-arm-musl.sh b/src/ci/docker/dist-various-1/build-arm-musl.sh similarity index 99% rename from src/ci/docker/cross/build-arm-musl.sh rename to src/ci/docker/dist-various-1/build-arm-musl.sh index 780099e2ec176..f9444a35a8b79 100755 --- a/src/ci/docker/cross/build-arm-musl.sh +++ b/src/ci/docker/dist-various-1/build-arm-musl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. @@ -11,7 +11,7 @@ set -ex -MUSL=1.1.16 +MUSL=1.1.17 hide_output() { set +x diff --git a/src/ci/docker/cross/build-rumprun.sh b/src/ci/docker/dist-various-1/build-rumprun.sh similarity index 98% rename from src/ci/docker/cross/build-rumprun.sh rename to src/ci/docker/dist-various-1/build-rumprun.sh index 59b1c9b641535..ad38cf872ad07 100755 --- a/src/ci/docker/cross/build-rumprun.sh +++ b/src/ci/docker/dist-various-1/build-rumprun.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/cross/install-mips-musl.sh b/src/ci/docker/dist-various-1/install-mips-musl.sh similarity index 100% rename from src/ci/docker/cross/install-mips-musl.sh rename to src/ci/docker/dist-various-1/install-mips-musl.sh diff --git a/src/ci/docker/cross/install-mipsel-musl.sh b/src/ci/docker/dist-various-1/install-mipsel-musl.sh similarity index 100% rename from src/ci/docker/cross/install-mipsel-musl.sh rename to src/ci/docker/dist-various-1/install-mipsel-musl.sh diff --git a/src/ci/docker/cross/install-x86_64-redox.sh b/src/ci/docker/dist-various-1/install-x86_64-redox.sh similarity index 97% rename from src/ci/docker/cross/install-x86_64-redox.sh rename to src/ci/docker/dist-various-1/install-x86_64-redox.sh index 8e052c4acd28a..9bfb57f5741c5 100755 --- a/src/ci/docker/cross/install-x86_64-redox.sh +++ b/src/ci/docker/dist-various-1/install-x86_64-redox.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-various-2/Dockerfile b/src/ci/docker/dist-various-2/Dockerfile new file mode 100644 index 0000000000000..c7885db559a64 --- /dev/null +++ b/src/ci/docker/dist-various-2/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:16.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +RUN apt-get build-dep -y clang llvm && apt-get install -y --no-install-recommends \ + build-essential \ + gcc-multilib \ + libedit-dev \ + libgmp-dev \ + libisl-dev \ + libmpc-dev \ + libmpfr-dev \ + ninja-build \ + nodejs \ + python2.7-dev \ + software-properties-common \ + unzip + +RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486 +RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2-testing main' + +WORKDIR /tmp +COPY dist-various-2/shared.sh dist-various-2/build-fuchsia-toolchain.sh /tmp/ +COPY dist-various-2/build-solaris-toolchain.sh /tmp/ +RUN /tmp/build-fuchsia-toolchain.sh +RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 +RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV \ + AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \ + CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \ + CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \ + AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \ + CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \ + CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++ \ + AR_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-ar \ + CC_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-gcc \ + CXX_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-g++ \ + AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \ + CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \ + CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ + +ENV TARGETS=x86_64-unknown-fuchsia +ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia +ENV TARGETS=$TARGETS,sparcv9-sun-solaris +ENV TARGETS=$TARGETS,wasm32-unknown-unknown +ENV TARGETS=$TARGETS,x86_64-sun-solaris +ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32 + +ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended +ENV SCRIPT python2.7 ../x.py dist --target $TARGETS diff --git a/src/ci/docker/dist-fuchsia/build-toolchain.sh b/src/ci/docker/dist-various-2/build-fuchsia-toolchain.sh similarity index 98% rename from src/ci/docker/dist-fuchsia/build-toolchain.sh rename to src/ci/docker/dist-various-2/build-fuchsia-toolchain.sh index 756013a235cc1..ef8f0c37f8c37 100755 --- a/src/ci/docker/dist-fuchsia/build-toolchain.sh +++ b/src/ci/docker/dist-various-2/build-fuchsia-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-various-2/build-solaris-toolchain.sh b/src/ci/docker/dist-various-2/build-solaris-toolchain.sh new file mode 100755 index 0000000000000..c04c8b7194c71 --- /dev/null +++ b/src/ci/docker/dist-various-2/build-solaris-toolchain.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# 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. + +set -ex +source shared.sh + +ARCH=$1 +LIB_ARCH=$2 +APT_ARCH=$3 +BINUTILS=2.28.1 +GCC=6.4.0 + +# First up, build binutils +mkdir binutils +cd binutils + +curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.xz | tar xJf - +mkdir binutils-build +cd binutils-build +hide_output ../binutils-$BINUTILS/configure --target=$ARCH-sun-solaris2.10 +hide_output make -j10 +hide_output make install + +cd ../.. +rm -rf binutils + +# Next, download and install the relevant solaris packages +mkdir solaris +cd solaris + +dpkg --add-architecture $APT_ARCH +apt-get update +apt-get download $(apt-cache depends --recurse --no-replaces \ + libc-dev:$APT_ARCH \ + libm-dev:$APT_ARCH \ + libpthread-dev:$APT_ARCH \ + libresolv-dev:$APT_ARCH \ + librt-dev:$APT_ARCH \ + libsocket-dev:$APT_ARCH \ + system-crt:$APT_ARCH \ + system-header:$APT_ARCH \ + | grep "^\w") + +for deb in *$APT_ARCH.deb; do + dpkg -x $deb . +done + +# Remove Solaris 11 functions that are optionally used by libbacktrace. +# This is for Solaris 10 compatibility. +rm usr/include/link.h +patch -p0 << 'EOF' +--- usr/include/string.h ++++ usr/include/string10.h +@@ -93 +92,0 @@ +-extern size_t strnlen(const char *, size_t); +EOF + +mkdir /usr/local/$ARCH-sun-solaris2.10/usr +mv usr/include /usr/local/$ARCH-sun-solaris2.10/usr/include +mv usr/lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.10/lib +mv lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.10/lib + +ln -s usr/include /usr/local/$ARCH-sun-solaris2.10/sys-include +ln -s usr/include /usr/local/$ARCH-sun-solaris2.10/include + +cd .. +rm -rf solaris + +# Finally, download and build gcc to target solaris +mkdir gcc +cd gcc + +curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | tar xJf - +cd gcc-$GCC + +mkdir ../gcc-build +cd ../gcc-build +hide_output ../gcc-$GCC/configure \ + --enable-languages=c,c++ \ + --target=$ARCH-sun-solaris2.10 \ + --with-gnu-as \ + --with-gnu-ld \ + --disable-multilib \ + --disable-nls \ + --disable-libgomp \ + --disable-libquadmath \ + --disable-libssp \ + --disable-libvtv \ + --disable-libcilkrts \ + --disable-libada \ + --disable-libsanitizer \ + --disable-libquadmath-support \ + --disable-lto + +hide_output make -j10 +hide_output make install + +cd ../.. +rm -rf gcc diff --git a/src/ci/docker/dist-fuchsia/shared.sh b/src/ci/docker/dist-various-2/shared.sh similarity index 100% rename from src/ci/docker/dist-fuchsia/shared.sh rename to src/ci/docker/dist-various-2/shared.sh diff --git a/src/ci/docker/dist-x86_64-freebsd/build-toolchain.sh b/src/ci/docker/dist-x86_64-freebsd/build-toolchain.sh index 8343327c33bf2..3c86a8e38175e 100755 --- a/src/ci/docker/dist-x86_64-freebsd/build-toolchain.sh +++ b/src/ci/docker/dist-x86_64-freebsd/build-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. diff --git a/src/ci/docker/dist-x86_64-linux/build-binutils.sh b/src/ci/docker/dist-x86_64-linux/build-binutils.sh index 80aa1f2a01613..f4bdbd80d0edb 100755 --- a/src/ci/docker/dist-x86_64-linux/build-binutils.sh +++ b/src/ci/docker/dist-x86_64-linux/build-binutils.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-cmake.sh b/src/ci/docker/dist-x86_64-linux/build-cmake.sh index 82e46455cb0f0..9a3763d421ad2 100755 --- a/src/ci/docker/dist-x86_64-linux/build-cmake.sh +++ b/src/ci/docker/dist-x86_64-linux/build-cmake.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-curl.sh b/src/ci/docker/dist-x86_64-linux/build-curl.sh index b7d22755a571b..edf3175b81c43 100755 --- a/src/ci/docker/dist-x86_64-linux/build-curl.sh +++ b/src/ci/docker/dist-x86_64-linux/build-curl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/dist-x86_64-linux/build-gcc.sh index ab2562538d6d7..6b991bb59e4b0 100755 --- a/src/ci/docker/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/dist-x86_64-linux/build-gcc.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-git.sh b/src/ci/docker/dist-x86_64-linux/build-git.sh index 92fa66b496d93..ff62a68629a8b 100755 --- a/src/ci/docker/dist-x86_64-linux/build-git.sh +++ b/src/ci/docker/dist-x86_64-linux/build-git.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-headers.sh b/src/ci/docker/dist-x86_64-linux/build-headers.sh index 4ce38fd9205e2..2f15114d6f980 100755 --- a/src/ci/docker/dist-x86_64-linux/build-headers.sh +++ b/src/ci/docker/dist-x86_64-linux/build-headers.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-openssl.sh b/src/ci/docker/dist-x86_64-linux/build-openssl.sh index 34da0ed631093..e7226ace020bd 100755 --- a/src/ci/docker/dist-x86_64-linux/build-openssl.sh +++ b/src/ci/docker/dist-x86_64-linux/build-openssl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-linux/build-python.sh b/src/ci/docker/dist-x86_64-linux/build-python.sh index a7a450f3c8de7..c6b8cdde4b9af 100755 --- a/src/ci/docker/dist-x86_64-linux/build-python.sh +++ b/src/ci/docker/dist-x86_64-linux/build-python.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. diff --git a/src/ci/docker/dist-x86_64-musl/build-musl.sh b/src/ci/docker/dist-x86_64-musl/build-musl.sh index 776da0093974c..9be8d001149e9 100644 --- a/src/ci/docker/dist-x86_64-musl/build-musl.sh +++ b/src/ci/docker/dist-x86_64-musl/build-musl.sh @@ -15,7 +15,7 @@ set -ex export CFLAGS="-fPIC -Wa,-mrelax-relocations=no" export CXXFLAGS="-Wa,-mrelax-relocations=no" -MUSL=musl-1.1.16 +MUSL=musl-1.1.17 curl https://www.musl-libc.org/releases/$MUSL.tar.gz | tar xzf - cd $MUSL ./configure --prefix=/musl-x86_64 --disable-shared diff --git a/src/ci/docker/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/dist-x86_64-netbsd/build-netbsd-toolchain.sh index 54100b49cb9f5..5b4314d57e6cc 100755 --- a/src/ci/docker/dist-x86_64-netbsd/build-netbsd-toolchain.sh +++ b/src/ci/docker/dist-x86_64-netbsd/build-netbsd-toolchain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. @@ -52,7 +52,7 @@ curl $URL/2017-03-17-netbsd-comp.tgz | \ cd usr/src # The options, in order, do the following -# * this is an unpriviledged build +# * this is an unprivileged build # * output to a predictable location # * disable various uneeded stuff MKUNPRIVED=yes TOOLDIR=/x-tools/x86_64-unknown-netbsd \ @@ -64,12 +64,12 @@ cd ../.. rm -rf usr cat > /x-tools/x86_64-unknown-netbsd/bin/x86_64--netbsd-gcc-sysroot <<'EOF' -#!/bin/bash +#!/usr/bin/env bash exec /x-tools/x86_64-unknown-netbsd/bin/x86_64--netbsd-gcc --sysroot=/x-tools/x86_64-unknown-netbsd/sysroot "$@" EOF cat > /x-tools/x86_64-unknown-netbsd/bin/x86_64--netbsd-g++-sysroot <<'EOF' -#!/bin/bash +#!/usr/bin/env bash exec /x-tools/x86_64-unknown-netbsd/bin/x86_64--netbsd-g++ --sysroot=/x-tools/x86_64-unknown-netbsd/sysroot "$@" EOF diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 7087033e117a2..a863e1a2d5dc0 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. @@ -11,6 +11,8 @@ set -e +export MSYS_NO_PATHCONV=1 + script=`cd $(dirname $0) && pwd`/`basename $0` image=$1 @@ -25,23 +27,32 @@ travis_fold start build_docker travis_time_start if [ -f "$docker_dir/$image/Dockerfile" ]; then + dockerfile="$docker_dir/$image/Dockerfile" + if [ -x /usr/bin/cygpath ]; then + context="`cygpath -w $docker_dir`" + dockerfile="`cygpath -w $dockerfile`" + else + context="$docker_dir" + fi retry docker \ build \ --rm \ -t rust-ci \ - -f "$docker_dir/$image/Dockerfile" \ - "$docker_dir" + -f "$dockerfile" \ + "$context" elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then if [ -n "$TRAVIS_OS_NAME" ]; then echo Cannot run disabled images on travis! exit 1 fi - retry docker \ + # retry messes with the pipe from tar to docker. Not needed on non-travis + # Transform changes the context of disabled Dockerfiles to match the enabled ones + tar --transform 's#^./disabled/#./#' -C $docker_dir -c . | docker \ build \ --rm \ -t rust-ci \ - -f "$docker_dir/disabled/$image/Dockerfile" \ - "$docker_dir" + -f "$image/Dockerfile" \ + - else echo Invalid image: $image exit 1 diff --git a/src/ci/docker/scripts/android-sdk.sh b/src/ci/docker/scripts/android-sdk.sh index d343aae9dfb68..99c5776c2e849 100644 --- a/src/ci/docker/scripts/android-sdk.sh +++ b/src/ci/docker/scripts/android-sdk.sh @@ -10,40 +10,40 @@ set -ex -URL=https://dl.google.com/android/repository +export ANDROID_HOME=/android/sdk +PATH=$PATH:"${ANDROID_HOME}/tools/bin" download_sdk() { - mkdir -p /android/sdk - cd /android/sdk - curl -fO $URL/$1 - unzip -q $1 - rm -rf $1 + mkdir -p /android + curl -fo sdk.zip "https://dl.google.com/android/repository/sdk-tools-linux-$1.zip" + unzip -q sdk.zip -d "$ANDROID_HOME" + rm -f sdk.zip } download_sysimage() { - # See https://developer.android.com/studio/tools/help/android.html abi=$1 api=$2 - filter="platform-tools,android-$api" - filter="$filter,sys-img-$abi-android-$api" - - # Keep printing yes to accept the licenses - while true; do echo yes; sleep 10; done | \ - /android/sdk/tools/android update sdk -a --no-ui \ - --filter "$filter" + # See https://developer.android.com/studio/command-line/sdkmanager.html for + # usage of `sdkmanager`. + # + # The output from sdkmanager is so noisy that it will occupy all of the 4 MB + # log extremely quickly. Thus we must silence all output. + yes | sdkmanager --licenses > /dev/null + sdkmanager platform-tools emulator \ + "platforms;android-$api" \ + "system-images;android-$api;default;$abi" > /dev/null } create_avd() { - # See https://developer.android.com/studio/tools/help/android.html abi=$1 api=$2 - echo no | \ - /android/sdk/tools/android create avd \ - --name $abi-$api \ - --target android-$api \ - --abi $abi + # See https://developer.android.com/studio/command-line/avdmanager.html for + # usage of `avdmanager`. + echo no | avdmanager create avd \ + -n "$abi-$api" \ + -k "system-images;android-$api;default;$abi" } download_and_create_avd() { @@ -51,3 +51,15 @@ download_and_create_avd() { download_sysimage $2 $3 create_avd $2 $3 } + +# Usage: +# +# setup_android_sdk 4333796 armeabi-v7a 18 +# +# 4333796 => +# SDK tool version. +# Copy from https://developer.android.com/studio/index.html#command-tools +# armeabi-v7a => +# System image ABI +# 18 => +# Android API Level (18 = Android 4.3 = Jelly Bean MR2) diff --git a/src/ci/docker/wasm32-unknown/Dockerfile b/src/ci/docker/wasm32-unknown/Dockerfile new file mode 100644 index 0000000000000..dc1727b7014c3 --- /dev/null +++ b/src/ci/docker/wasm32-unknown/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils + +RUN curl -sL https://nodejs.org/dist/v9.2.0/node-v9.2.0-linux-x64.tar.xz | \ + tar -xJ + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV TARGETS=wasm32-unknown-unknown + +ENV RUST_CONFIGURE_ARGS \ + --target=$TARGETS \ + --set build.nodejs=/node-v9.2.0-linux-x64/bin/node + +ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \ + src/test/ui \ + src/test/run-pass \ + src/test/compile-fail \ + src/test/parse-fail \ + src/test/mir-opt \ + src/test/codegen-units \ + src/libcore \ + src/libstd_unicode/ \ diff --git a/src/ci/docker/x86_64-gnu-aux/Dockerfile b/src/ci/docker/x86_64-gnu-aux/Dockerfile index a453c62cc9e83..62c55f4806734 100644 --- a/src/ci/docker/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/x86_64-gnu-aux/Dockerfile @@ -12,7 +12,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libssl-dev \ sudo \ xz-utils \ - pkg-config + pkg-config \ + libgl1-mesa-dev \ + llvm-dev \ + libfreetype6-dev \ + libexpat1-dev COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile b/src/ci/docker/x86_64-gnu-llvm-3.9/Dockerfile similarity index 72% rename from src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile rename to src/ci/docker/x86_64-gnu-llvm-3.9/Dockerfile index e832a2445ba14..6b8186048988d 100644 --- a/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile +++ b/src/ci/docker/x86_64-gnu-llvm-3.9/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-3.7-tools \ + llvm-3.9-tools \ libedit-dev \ zlib1g-dev \ xz-utils @@ -19,7 +19,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +# using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-3.7 + --llvm-root=/usr/lib/llvm-3.9 \ + --enable-llvm-link-shared ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/x86_64-gnu-tools/Dockerfile b/src/ci/docker/x86_64-gnu-tools/Dockerfile new file mode 100644 index 0000000000000..fffad1c42dfd8 --- /dev/null +++ b/src/ci/docker/x86_64-gnu-tools/Dockerfile @@ -0,0 +1,23 @@ +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 \ + libssl-dev \ + sudo \ + xz-utils \ + pkg-config + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +COPY x86_64-gnu-tools/checktools.sh /tmp/ + +ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --save-toolstates=/tmp/toolstates.json +ENV SCRIPT /tmp/checktools.sh ../x.py /tmp/toolstates.json diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh new file mode 100755 index 0000000000000..bf39bc28a67ea --- /dev/null +++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# Copyright 2017 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +set -eu + +X_PY="$1" +TOOLSTATE_FILE="$2" + +touch "$TOOLSTATE_FILE" + +set +e +python2.7 "$X_PY" test --no-fail-fast \ + src/tools/rls \ + src/tools/rustfmt \ + src/tools/miri \ + src/tools/clippy +TEST_RESULT=$? +set -e + +# FIXME: Upload this file to the repository. +cat "$TOOLSTATE_FILE" + +# FIXME: After we can properly inform dev-tool maintainers about failure, +# comment out the `exit 0` below. +if [ "$RUST_RELEASE_CHANNEL" = nightly ]; then + # exit 0 + true +fi + +exit $TEST_RESULT diff --git a/src/ci/init_repo.sh b/src/ci/init_repo.sh index 7ffe00a807b34..e073a3d99c157 100755 --- a/src/ci/init_repo.sh +++ b/src/ci/init_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. diff --git a/src/ci/run.sh b/src/ci/run.sh index b4fa033c4a668..dab385c09649c 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 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. @@ -37,19 +37,20 @@ if [ "$DIST_SRC" = "" ]; then fi # If we're deploying artifacts then we set the release channel, otherwise if -# we're not deploying then we want to be sure to enable all assertions becauase +# we're not deploying then we want to be sure to enable all assertions because # we'll be running tests # # FIXME: need a scheme for changing this `nightly` value to `beta` and `stable` # either automatically or manually. +export RUST_RELEASE_CHANNEL=nightly if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=nightly" + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" elif [ "$DEPLOY_ALT" != "" ]; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-assertions" fi else # We almost always want debug assertions enabled, but sometimes this takes too diff --git a/src/dlmalloc b/src/dlmalloc new file mode 160000 index 0000000000000..d3812c3accaee --- /dev/null +++ b/src/dlmalloc @@ -0,0 +1 @@ +Subproject commit d3812c3accaee7ad23068ed4fc089cc05c7a538f diff --git a/src/doc/book b/src/doc/book index d09c9e8144ed3..3944d61149fa2 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit d09c9e8144ed32170b7596abb145ade8b097acaf +Subproject commit 3944d61149fa234ea991b498d4dac4fcec68a80e diff --git a/src/doc/index.md b/src/doc/index.md index bfd09145baafa..3784cc3c4b497 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -28,6 +28,7 @@ Rust provides a number of book-length sets of documentation, collectively nicknamed 'The Rust Bookshelf.' * [The Rust Programming Language][book] teaches you how to program in Rust. +* [The Cargo Book][cargo-book] is a guide to Cargo, Rust's build tool and dependency manager. * [The Unstable Book][unstable-book] has documentation for unstable features. * [The Rustonomicon][nomicon] is your guidebook to the dark arts of unsafe Rust. * [The Reference][ref] is not a formal spec, but is more detailed and comprehensive than the book. @@ -53,4 +54,5 @@ before this policy was put into place. That work is being tracked [nomicon]: nomicon/index.html [unstable-book]: unstable-book/index.html [rustdoc-book]: rustdoc/index.html +[cargo-book]: cargo/index.html diff --git a/src/doc/man/rustc.1 b/src/doc/man/rustc.1 index 6c80f11fa7205..0bb41cee2c518 100644 --- a/src/doc/man/rustc.1 +++ b/src/doc/man/rustc.1 @@ -152,9 +152,6 @@ never colorize output. .SH CODEGEN OPTIONS -.TP -\fBar\fR=\fI/path/to/ar\fR -Path to the archive utility to use when assembling archives. .TP \fBlinker\fR=\fI/path/to/cc\fR Path to the linker utility to use when linking libraries, executables, and diff --git a/src/doc/nomicon b/src/doc/nomicon index a4322ccb289a4..cfb1f2d7e5eb6 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit a4322ccb289a43cc238d4536982f184a3eec9ba7 +Subproject commit cfb1f2d7e5eb6143915d5a63afe4cf58e8531c27 diff --git a/src/doc/not_found.md b/src/doc/not_found.md index 5d632ebc68f7a..ebe7c59313fa5 100644 --- a/src/doc/not_found.md +++ b/src/doc/not_found.md @@ -22,7 +22,7 @@ Some things that might be helpful to you though: # Reference * [The Rust official site](https://www.rust-lang.org) -* [The Rust reference](https://doc.rust-lang.org/reference.html) +* [The Rust reference](https://doc.rust-lang.org/reference/index.html) # Docs diff --git a/src/doc/reference b/src/doc/reference index 266d429a48468..857f2c97c8075 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 266d429a48468371d2d90669f6a30dd659bb4bdb +Subproject commit 857f2c97c8075d39fa07eb4a404980519faa600a diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 0f0bda65ce379..e51c63cf00898 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -96,11 +96,11 @@ Using this flag looks like this: $ rustdoc src/lib.rs --crate-name mycrate ``` -By default, `rustodc` assumes that the name of your crate is the same name +By default, `rustdoc` assumes that the name of your crate is the same name as the `.rs` file. `--crate-name` lets you override this assumption with whatever name you choose. -## `-L`/`--library-path`: +## `-L`/`--library-path`: where to look for dependencies Using this flag looks like this: @@ -186,7 +186,7 @@ on documentation tests](documentation-tests.html). See also `--test-args`. -## `--test-args`: +## `--test-args`: pass options to test runner Using this flag looks like this: @@ -199,7 +199,7 @@ For more, see [the chapter on documentation tests](documentation-tests.html). See also `--test`. -## `--target`: +## `--target`: generate documentation for the specified target triple Using this flag looks like this: @@ -253,7 +253,7 @@ $ rustdoc README.md --html-before-content extra.html ``` This flag takes a list of files, and inserts them inside the `` tag but -before the other content `rustodc` would normally produce in the rendered +before the other content `rustdoc` would normally produce in the rendered documentation. ## `--html-after-content`: include more HTML after the content @@ -266,7 +266,7 @@ $ rustdoc README.md --html-after-content extra.html ``` This flag takes a list of files, and inserts them before the `` tag but -after the other content `rustodc` would normally produce in the rendered +after the other content `rustdoc` would normally produce in the rendered documentation. @@ -279,7 +279,7 @@ $ rustdoc README.md --markdown-playground-url https://play.rust-lang.org/ ``` When rendering a Markdown file, this flag gives the base URL of the Rust -Playround, to use for generating `Run` buttons. +Playground, to use for generating `Run` buttons. ## `--markdown-no-toc`: don't generate a table of contents @@ -291,7 +291,7 @@ $ rustdoc README.md --markdown-no-toc ``` When generating documentation from a Markdown file, by default, `rustdoc` will -generate a table of contents. This flag supresses that, and no TOC will be +generate a table of contents. This flag suppresses that, and no TOC will be generated. diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index eb3e6a9dd5067..e5a603a3709f6 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -38,17 +38,19 @@ function! Forcing you to write `main` for every example, no matter how small, adds friction. So `rustdoc` processes your examples slightly before running them. Here's the full algorithm rustdoc uses to preprocess examples: -1. Any leading `#![foo]` attributes are left intact as crate attributes. -2. Some common `allow` attributes are inserted, including +1. Some common `allow` attributes are inserted, including `unused_variables`, `unused_assignments`, `unused_mut`, `unused_attributes`, and `dead_code`. Small examples often trigger these lints. -3. If the example does not contain `extern crate`, then `extern crate +2. Any attributes specified with `#![doc(test(attr(...)))]` are added. +3. Any leading `#![foo]` attributes are left intact as crate attributes. +4. If the example does not contain `extern crate`, and + `#![doc(test(no_crate_inject))]` was not specified, then `extern crate ;` is inserted (note the lack of `#[macro_use]`). -4. Finally, if the example does not contain `fn main`, the remainder of the +5. Finally, if the example does not contain `fn main`, the remainder of the text is wrapped in `fn main() { your_code }`. -For more about that caveat in rule 3, see "Documeting Macros" below. +For more about that caveat in rule 4, see "Documenting Macros" below. ## Hiding portions of the example @@ -261,4 +263,4 @@ are added. The `no_run` attribute will compile your code, but not run it. This is important for examples such as "Here's how to retrieve a web page," which you would want to ensure compiles, but might be run in a test -environment that has no network access. \ No newline at end of file +environment that has no network access. diff --git a/src/doc/rustdoc/src/the-doc-attribute.md b/src/doc/rustdoc/src/the-doc-attribute.md index 978d7656bdd71..aadd72d1c902d 100644 --- a/src/doc/rustdoc/src/the-doc-attribute.md +++ b/src/doc/rustdoc/src/the-doc-attribute.md @@ -103,6 +103,26 @@ to it in the docs. But if you include this: it will not. +### `test(no_crate_inject)` + +By default, `rustdoc` will automatically add a line with `extern crate my_crate;` into each doctest. +But if you include this: + +```rust,ignore +#![doc(test(no_crate_inject))] +``` + +it will not. + +### `test(attr(...))` + +This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For +example, if you want your doctests to fail if they produce any warnings, you could add this: + +```rust,ignore +#![doc(test(attr(deny(warnings))))] +``` + ## At the item level These forms of the `#[doc]` attribute are used on individual items, to control how diff --git a/src/doc/unstable-book/src/language-features/attr-literals.md b/src/doc/unstable-book/src/language-features/attr-literals.md index 60741a74400d2..6606f3c4e5c54 100644 --- a/src/doc/unstable-book/src/language-features/attr-literals.md +++ b/src/doc/unstable-book/src/language-features/attr-literals.md @@ -15,16 +15,16 @@ The `attr_literals` unstable feature allows other types of literals to be used in attributes. Here are some examples of attributes that can now be used with this feature enabled: -+```rust,ignore -+#[attr] -+#[attr(true)] -+#[attr(ident)] -+#[attr(ident, 100, true, "true", ident = 100, ident = "hello", ident(100))] -+#[attr(100)] -+#[attr(enabled = true)] -+#[enabled(true)] -+#[attr("hello")] -+#[repr(C, align = 4)] -+#[repr(C, align(4))] -+``` +```rust,ignore +#[attr] +#[attr(true)] +#[attr(ident)] +#[attr(ident, 100, true, "true", ident = 100, ident = "hello", ident(100))] +#[attr(100)] +#[attr(enabled = true)] +#[enabled(true)] +#[attr("hello")] +#[repr(C, align = 4)] +#[repr(C, align(4))] +``` diff --git a/src/doc/unstable-book/src/language-features/crate-visibility-modifier.md b/src/doc/unstable-book/src/language-features/crate-visibility-modifier.md new file mode 100644 index 0000000000000..11b3ee8edf0b1 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/crate-visibility-modifier.md @@ -0,0 +1,20 @@ +# `crate_visibility_modifier` + +The tracking issue for this feature is: [#45388] + +[#45388]: https://github.com/rust-lang/rust/issues/45388 + +----- + +The `crate_visibility_modifier` feature allows the `crate` keyword to be used +as a visibility modifier synonymous to `pub(crate)`, indicating that a type +(function, _&c._) is to be visible to the entire enclosing crate, but not to +other crates. + +```rust +#![feature(crate_visibility_modifier)] + +crate struct Foo { + bar: usize, +} +``` diff --git a/src/doc/unstable-book/src/language-features/doc-spotlight.md b/src/doc/unstable-book/src/language-features/doc-spotlight.md new file mode 100644 index 0000000000000..8117755fef1c8 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/doc-spotlight.md @@ -0,0 +1,30 @@ +# `doc_spotlight` + +The tracking issue for this feature is: [#45040] + +The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute, +to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]` +attribute to a trait definition will make rustdoc print extra information for functions which return +a type that implements that trait. This attribute is applied to the `Iterator`, `io::Read`, and +`io::Write` traits in the standard library. + +You can do this on your own traits, like this: + +``` +#![feature(doc_spotlight)] + +#[doc(spotlight)] +pub trait MyTrait {} + +pub struct MyStruct; +impl MyTrait for MyStruct {} + +/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`, +/// without having to write that yourself! +pub fn my_fn() -> MyStruct { MyStruct } +``` + +This feature was originally implemented in PR [#45039]. + +[#45040]: https://github.com/rust-lang/rust/issues/45040 +[#45039]: https://github.com/rust-lang/rust/pull/45039 diff --git a/src/doc/unstable-book/src/language-features/external-doc.md b/src/doc/unstable-book/src/language-features/external-doc.md new file mode 100644 index 0000000000000..effae5d299949 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/external-doc.md @@ -0,0 +1,40 @@ +# `external_doc` + +The tracking issue for this feature is: [#44732] + +The `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to +include external files in documentation. Use the attribute in place of, or in addition to, regular +doc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders +documentation for your crate. + +With the following files in the same directory: + +`external-doc.md`: + +```markdown +# My Awesome Type + +This is the documentation for this spectacular type. +``` + +`lib.rs`: + +```no_run (needs-external-files) +#![feature(external_doc)] + +#[doc(include = "external-doc.md")] +pub struct MyAwesomeType; +``` + +`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType` +struct. + +When locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the +`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory, +start your paths with `../docs/` for `rustdoc` to properly find the file. + +This feature was proposed in [RFC #1990] and initially implemented in PR [#44781]. + +[#44732]: https://github.com/rust-lang/rust/issues/44732 +[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990 +[#44781]: https://github.com/rust-lang/rust/pull/44781 diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index ecbc860e25c03..0137a052a62d8 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -227,3 +227,95 @@ A third function, `rust_eh_unwind_resume`, is also needed if the `custom_unwind_ flag is set in the options of the compilation target. It allows customizing the process of resuming unwind at the end of the landing pads. The language item's name is `eh_unwind_resume`. + +## List of all language items + +This is a list of all language items in Rust along with where they are located in +the source code. + +- Primitives + - `i8`: `libcore/num/mod.rs` + - `i16`: `libcore/num/mod.rs` + - `i32`: `libcore/num/mod.rs` + - `i64`: `libcore/num/mod.rs` + - `i128`: `libcore/num/mod.rs` + - `isize`: `libcore/num/mod.rs` + - `u8`: `libcore/num/mod.rs` + - `u16`: `libcore/num/mod.rs` + - `u32`: `libcore/num/mod.rs` + - `u64`: `libcore/num/mod.rs` + - `u128`: `libcore/num/mod.rs` + - `usize`: `libcore/num/mod.rs` + - `f32`: `libstd/f32.rs` + - `f64`: `libstd/f64.rs` + - `char`: `libstd_unicode/char.rs` + - `slice`: `liballoc/slice.rs` + - `str`: `liballoc/str.rs` + - `const_ptr`: `libcore/ptr.rs` + - `mut_ptr`: `libcore/ptr.rs` + - `unsafe_cell`: `libcore/cell.rs` +- Runtime + - `start`: `libstd/rt.rs` + - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) + - `eh_personality`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) + - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) + - `eh_unwind_resume`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) + - `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC) + - `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH) + - `panic`: `libcore/panicking.rs` + - `panic_bounds_check`: `libcore/panicking.rs` + - `panic_fmt`: `libcore/panicking.rs` + - `panic_fmt`: `libstd/panicking.rs` +- Allocations + - `owned_box`: `liballoc/boxed.rs` + - `exchange_malloc`: `liballoc/heap.rs` + - `box_free`: `liballoc/heap.rs` +- Operands + - `not`: `libcore/ops/bit.rs` + - `bitand`: `libcore/ops/bit.rs` + - `bitor`: `libcore/ops/bit.rs` + - `bitxor`: `libcore/ops/bit.rs` + - `shl`: `libcore/ops/bit.rs` + - `shr`: `libcore/ops/bit.rs` + - `bitand_assign`: `libcore/ops/bit.rs` + - `bitor_assign`: `libcore/ops/bit.rs` + - `bitxor_assign`: `libcore/ops/bit.rs` + - `shl_assign`: `libcore/ops/bit.rs` + - `shr_assign`: `libcore/ops/bit.rs` + - `deref`: `libcore/ops/deref.rs` + - `deref_mut`: `libcore/ops/deref.rs` + - `index`: `libcore/ops/index.rs` + - `index_mut`: `libcore/ops/index.rs` + - `add`: `libcore/ops/arith.rs` + - `sub`: `libcore/ops/arith.rs` + - `mul`: `libcore/ops/arith.rs` + - `div`: `libcore/ops/arith.rs` + - `rem`: `libcore/ops/arith.rs` + - `neg`: `libcore/ops/arith.rs` + - `add_assign`: `libcore/ops/arith.rs` + - `sub_assign`: `libcore/ops/arith.rs` + - `mul_assign`: `libcore/ops/arith.rs` + - `div_assign`: `libcore/ops/arith.rs` + - `rem_assign`: `libcore/ops/arith.rs` + - `eq`: `libcore/cmp.rs` + - `ord`: `libcore/cmp.rs` +- Functions + - `fn`: `libcore/ops/function.rs` + - `fn_mut`: `libcore/ops/function.rs` + - `fn_once`: `libcore/ops/function.rs` + - `generator_state`: `libcore/ops/generator.rs` + - `generator`: `libcore/ops/generator.rs` +- Other + - `coerce_unsized`: `libcore/ops/unsize.rs` + - `drop`: `libcore/ops/drop.rs` + - `drop_in_place`: `libcore/ptr.rs` + - `clone`: `libcore/clone.rs` + - `copy`: `libcore/marker.rs` + - `send`: `libcore/marker.rs` + - `sized`: `libcore/marker.rs` + - `unsize`: `libcore/marker.rs` + - `sync`: `libcore/marker.rs` + - `phantom_data`: `libcore/marker.rs` + - `freeze`: `libcore/marker.rs` + - `debug_trait`: `libcore/fmt/mod.rs` + - `non_zero`: `libcore/nonzero.rs` \ No newline at end of file diff --git a/src/doc/unstable-book/src/language-features/match_default_bindings.md b/src/doc/unstable-book/src/language-features/match_default_bindings.md new file mode 100644 index 0000000000000..cc542931cbe1f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/match_default_bindings.md @@ -0,0 +1,58 @@ +# `match_default_bindings` + +The tracking issue for this feature is: [#42640] + +[#42640]: https://github.com/rust-lang/rust/issues/42640 + +------------------------ + +Match default bindings (also called "default binding modes in match") improves ergonomics for +pattern-matching on references by introducing automatic dereferencing (and a corresponding shift +in binding modes) for large classes of patterns that would otherwise not compile. + +For example, under match default bindings, + +```rust +#![feature(match_default_bindings)] + +fn main() { + let x: &Option<_> = &Some(0); + + match x { + Some(y) => { + println!("y={}", *y); + }, + None => {}, + } +} +``` + +compiles and is equivalent to either of the below: + +```rust +fn main() { + let x: &Option<_> = &Some(0); + + match *x { + Some(ref y) => { + println!("y={}", *y); + }, + None => {}, + } +} +``` + +or + +```rust +fn main() { + let x: &Option<_> = &Some(0); + + match x { + &Some(ref y) => { + println!("y={}", *y); + }, + &None => {}, + } +} +``` diff --git a/src/doc/unstable-book/src/language-features/non-ascii-idents.md b/src/doc/unstable-book/src/language-features/non-ascii-idents.md index d5600c58fd9a6..efb5495fe26ac 100644 --- a/src/doc/unstable-book/src/language-features/non-ascii-idents.md +++ b/src/doc/unstable-book/src/language-features/non-ascii-idents.md @@ -15,4 +15,34 @@ The `non_ascii_idents` feature adds support for non-ASCII identifiers. const ε: f64 = 0.00001f64; const Π: f64 = 3.14f64; -``` \ No newline at end of file +``` + +## Changes to the language reference + +> **Lexer:** +> IDENTIFIER : +>       XID_start XID_continue\* +>    | `_` XID_continue+ + +An identifier is any nonempty Unicode string of the following form: + +Either + + * The first character has property [`XID_start`] + * The remaining characters have property [`XID_continue`] + +Or + + * The first character is `_` + * The identifier is more than one character, `_` alone is not an identifier + * The remaining characters have property [`XID_continue`] + +that does _not_ occur in the set of [strict keywords]. + +> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the +> character ranges used to form the more familiar C and Java language-family +> identifiers. + +[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i= +[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i= +[strict keywords]: ../reference/keywords.html#strict-keywords diff --git a/src/doc/unstable-book/src/language-features/non-exhaustive.md b/src/doc/unstable-book/src/language-features/non-exhaustive.md new file mode 100644 index 0000000000000..f9840e1b83f2b --- /dev/null +++ b/src/doc/unstable-book/src/language-features/non-exhaustive.md @@ -0,0 +1,75 @@ +# `non_exhaustive` + +The tracking issue for this feature is: [#44109] + +[#44109]: https://github.com/rust-lang/rust/issues/44109 + +------------------------ + +The `non_exhaustive` gate allows you to use the `#[non_exhaustive]` attribute +on structs and enums. When applied within a crate, users of the crate will need +to use the `_` pattern when matching enums and use the `..` pattern when +matching structs. Structs marked as `non_exhaustive` will not be able to be +created normally outside of the defining crate. This is demonstrated below: + +```rust,ignore (pseudo-Rust) +use std::error::Error as StdError; + +#[non_exhaustive] +pub enum Error { + Message(String), + Other, +} +impl StdError for Error { + fn description(&self) -> &str { + // This will not error, despite being marked as non_exhaustive, as this + // enum is defined within the current crate, it can be matched + // exhaustively. + match *self { + Message(ref s) => s, + Other => "other or unknown error", + } + } +} +``` + +```rust,ignore (pseudo-Rust) +use mycrate::Error; + +// This will not error as the non_exhaustive Error enum has been matched with +// a wildcard. +match error { + Message(ref s) => ..., + Other => ..., + _ => ..., +} +``` + +```rust,ignore (pseudo-Rust) +#[non_exhaustive] +pub struct Config { + pub window_width: u16, + pub window_height: u16, +} + +// We can create structs as normal within the defining crate when marked as +// non_exhaustive. +let config = Config { window_width: 640, window_height: 480 }; + +// We can match structs exhaustively when within the defining crate. +if let Ok(Config { window_width, window_height }) = load_config() { + // ... +} +``` + +```rust,ignore (pseudo-Rust) +use mycrate::Config; + +// We cannot create a struct like normal if it has been marked as +// non_exhaustive. +let config = Config { window_width: 640, window_height: 480 }; +// By adding the `..` we can match the config as below outside of the crate +// when marked non_exhaustive. +let &Config { window_width, window_height, .. } = config; +``` + diff --git a/src/doc/unstable-book/src/language-features/on-unimplemented.md b/src/doc/unstable-book/src/language-features/on-unimplemented.md index 9eea3fccbbc17..70c7c110b786a 100644 --- a/src/doc/unstable-book/src/language-features/on-unimplemented.md +++ b/src/doc/unstable-book/src/language-features/on-unimplemented.md @@ -15,8 +15,8 @@ For example: ```rust,compile_fail #![feature(on_unimplemented)] -#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an \ - iterator over elements of type `{A}`"] +#[rustc_on_unimplemented="an iterator over elements of type `{A}` \ + cannot be built from a collection of type `{Self}`"] trait MyIterator { fn next(&mut self) -> A; } @@ -37,9 +37,9 @@ error[E0277]: the trait bound `&[{integer}]: MyIterator` is not satisfied --> :14:5 | 14 | iterate_chars(&[1, 2, 3][..]); - | ^^^^^^^^^^^^^ the trait `MyIterator` is not implemented for `&[{integer}]` + | ^^^^^^^^^^^^^ an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]` | - = note: a collection of type `&[{integer}]` cannot be built from an iterator over elements of type `char` + = help: the trait `MyIterator` is not implemented for `&[{integer}]` = note: required by `iterate_chars` error: aborting due to previous error diff --git a/src/doc/unstable-book/src/language-features/optin-builtin-traits.md b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md new file mode 100644 index 0000000000000..5c8124c9c6b7d --- /dev/null +++ b/src/doc/unstable-book/src/language-features/optin-builtin-traits.md @@ -0,0 +1,45 @@ +# `optin_builtin_traits` + +The tracking issue for this feature is [#13231] + +[#13231]: https://github.com/rust-lang/rust/issues/13231 + +---- + +The `optin_builtin_traits` feature gate allows you to define auto traits. + +Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits +that are automatically implemented for every type, unless the type, or a type it contains, +has explicitly opted out via a negative impl. + +[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html +[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html + +```rust,ignore +impl !Type for Trait +``` + +Example: + +```rust +#![feature(optin_builtin_traits)] + +auto trait Valid {} + +struct True; +struct False; + +impl !Valid for False {} + +struct MaybeValid(T); + +fn must_be_valid(_t: T) { } + +fn main() { + // works + must_be_valid( MaybeValid(True) ); + + // compiler error - trait bound not satisfied + // must_be_valid( MaybeValid(False) ); +} +``` diff --git a/src/doc/unstable-book/src/language-features/plugin.md b/src/doc/unstable-book/src/language-features/plugin.md index 4b8603e3c4450..1cece930eeaa5 100644 --- a/src/doc/unstable-book/src/language-features/plugin.md +++ b/src/doc/unstable-book/src/language-features/plugin.md @@ -177,7 +177,7 @@ quasiquote as an ordinary plugin library. Plugins can extend [Rust's lint infrastructure](../reference/attributes.html#lint-check-attributes) with additional checks for code style, safety, etc. Now let's write a plugin -[`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/lint_plugin_test.rs) +[`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs) that warns about any item named `lintme`. ```rust,ignore diff --git a/src/doc/unstable-book/src/language-features/trace-macros.md b/src/doc/unstable-book/src/language-features/trace-macros.md new file mode 100644 index 0000000000000..41aa286e69bfb --- /dev/null +++ b/src/doc/unstable-book/src/language-features/trace-macros.md @@ -0,0 +1,39 @@ +# `trace_macros` + +The tracking issue for this feature is [#29598]. + +[#29598]: https://github.com/rust-lang/rust/issues/29598 + +------------------------ + +With `trace_macros` you can trace the expansion of macros in your code. + +## Examples + +```rust +#![feature(trace_macros)] + +fn main() { + trace_macros!(true); + println!("Hello, Rust!"); + trace_macros!(false); +} +``` + +The `cargo build` output: + +```txt +note: trace_macro + --> src/main.rs:5:5 + | +5 | println!("Hello, Rust!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expanding `println! { "Hello, Rust!" }` + = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )` + = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }` + = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) ) + )` + + Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs +``` diff --git a/src/doc/unstable-book/src/language-features/unboxed-closures.md b/src/doc/unstable-book/src/language-features/unboxed-closures.md new file mode 100644 index 0000000000000..d845c99a88a69 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/unboxed-closures.md @@ -0,0 +1,25 @@ +# `unboxed_closures` + +The tracking issue for this feature is [#29625] + +See Also: [`fn_traits`](library-features/fn-traits.html) + +[#29625]: https://github.com/rust-lang/rust/issues/29625 + +---- + +The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI, +required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have +exactly one (non self) argument, a tuple representing the argument list. + +[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html + +```rust +#![feature(unboxed_closures)] + +extern "rust-call" fn add_args(args: (u32, u32)) -> u32 { + args.0 + args.1 +} + +fn main() {} +``` diff --git a/src/doc/unstable-book/src/language-features/universal-impl-trait.md b/src/doc/unstable-book/src/language-features/universal-impl-trait.md new file mode 100644 index 0000000000000..6b3c5e92720df --- /dev/null +++ b/src/doc/unstable-book/src/language-features/universal-impl-trait.md @@ -0,0 +1,32 @@ +# `universal_impl_trait` + +The tracking issue for this feature is: [#34511]. + +[#34511]: https://github.com/rust-lang/rust/issues/34511 + +-------------------- + +The `universal_impl_trait` feature extends the [`conservative_impl_trait`] +feature allowing the `impl Trait` syntax in arguments (universal +quantification). + +[`conservative_impl_trait`]: ./language-features/conservative-impl-trait.html + +## Examples + +```rust +#![feature(universal_impl_trait)] +use std::ops::Not; + +fn any_zero(values: impl IntoIterator) -> bool { + for val in values { if val == 0 { return true; } } + false +} + +fn main() { + let test1 = -5..; + let test2 = vec![1, 8, 42, -87, 60]; + assert!(any_zero(test1)); + assert!(bool::not(any_zero(test2))); +} +``` diff --git a/src/doc/unstable-book/src/language-features/use-nested-groups.md b/src/doc/unstable-book/src/language-features/use-nested-groups.md new file mode 100644 index 0000000000000..47b635bad736f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/use-nested-groups.md @@ -0,0 +1,90 @@ +# `use_nested_groups` + +The tracking issue for this feature is: [#44494] + +[#44494]: https://github.com/rust-lang/rust/issues/44494 + +------------------------ + +The `use_nested_groups` feature allows you to import multiple items from a +complex module tree easily, by nesting different imports in the same +declaration. For example: + +```rust +#![feature(use_nested_groups)] +# #![allow(unused_imports, dead_code)] +# +# mod foo { +# pub mod bar { +# pub type Foo = (); +# } +# pub mod baz { +# pub mod quux { +# pub type Bar = (); +# } +# } +# } + +use foo::{ + bar::{self, Foo}, + baz::{*, quux::Bar}, +}; +# +# fn main() {} +``` + +## Snippet for the book's new features appendix + +When stabilizing, add this to +`src/doc/book/second-edition/src/appendix-07-newest-features.md`: + +### Nested groups in `use` declarations + +If you have a complex module tree with many different submodules and you need +to import a few items from each one, it might be useful to group all the +imports in the same declaration to keep your code clean and avoid repeating the +base modules' name. + +The `use` declaration supports nesting to help you in those cases, both with +simple imports and glob ones. For example this snippets imports `bar`, `Foo`, +all the items in `baz` and `Bar`: + +```rust +# #![feature(use_nested_groups)] +# #![allow(unused_imports, dead_code)] +# +# mod foo { +# pub mod bar { +# pub type Foo = (); +# } +# pub mod baz { +# pub mod quux { +# pub type Bar = (); +# } +# } +# } +# +use foo::{ + bar::{self, Foo}, + baz::{*, quux::Bar}, +}; +# +# fn main() {} +``` + +## Updated reference + +When stabilizing, replace the shortcut list in +`src/doc/reference/src/items/use-declarations.md` with this updated one: + +* Simultaneously binding a list of paths with a common prefix, using the + glob-like brace syntax `use a::b::{c, d, e::f, g::h::i};` +* Simultaneously binding a list of paths with a common prefix and their common + parent module, using the `self` keyword, such as `use a::b::{self, c, d::e};` +* Rebinding the target name as a new local name, using the syntax `use p::q::r + as x;`. This can also be used with the last two features: + `use a::b::{self as ab, c as abc}`. +* Binding all paths matching a given prefix, using the asterisk wildcard syntax + `use a::b::*;`. +* Nesting groups of the previous features multiple times, such as + `use a::b::{self as ab, c d::{*, e::f}};` diff --git a/src/doc/unstable-book/src/library-features/alloc-jemalloc.md b/src/doc/unstable-book/src/library-features/alloc-jemalloc.md index 18ff838dd32b9..425d4cb79b2df 100644 --- a/src/doc/unstable-book/src/library-features/alloc-jemalloc.md +++ b/src/doc/unstable-book/src/library-features/alloc-jemalloc.md @@ -8,55 +8,6 @@ See also [`alloc_system`](library-features/alloc-system.html). ------------------------ -The compiler currently ships two default allocators: `alloc_system` and -`alloc_jemalloc` (some targets don't have jemalloc, however). These allocators -are normal Rust crates and contain an implementation of the routines to -allocate and deallocate memory. The standard library is not compiled assuming -either one, and the compiler will decide which allocator is in use at -compile-time depending on the type of output artifact being produced. - -Binaries generated by the compiler will use `alloc_jemalloc` by default (where -available). In this situation the compiler "controls the world" in the sense of -it has power over the final link. Primarily this means that the allocator -decision can be left up the compiler. - -Dynamic and static libraries, however, will use `alloc_system` by default. Here -Rust is typically a 'guest' in another application or another world where it -cannot authoritatively decide what allocator is in use. As a result it resorts -back to the standard APIs (e.g. `malloc` and `free`) for acquiring and releasing -memory. - -# Switching Allocators - -Although the compiler's default choices may work most of the time, it's often -necessary to tweak certain aspects. Overriding the compiler's decision about -which allocator is in use is done simply by linking to the desired allocator: - -```rust,no_run -#![feature(alloc_system)] - -extern crate alloc_system; - -fn main() { - let a = Box::new(4); // Allocates from the system allocator. - println!("{}", a); -} -``` - -In this example the binary generated will not link to jemalloc by default but -instead use the system allocator. Conversely to generate a dynamic library which -uses jemalloc by default one would write: - -```rust,ignore -#![feature(alloc_jemalloc)] -#![crate_type = "dylib"] - -extern crate alloc_jemalloc; - -pub fn foo() { - let a = Box::new(4); // Allocates from jemalloc. - println!("{}", a); -} -# fn main() {} -``` +This feature has been replaced by [the `jemallocator` crate on crates.io.][jemallocator]. +[jemallocator]: https://crates.io/crates/jemallocator diff --git a/src/doc/unstable-book/src/library-features/alloc-system.md b/src/doc/unstable-book/src/library-features/alloc-system.md index 1d261db6ba1b3..9effab202cabd 100644 --- a/src/doc/unstable-book/src/library-features/alloc-system.md +++ b/src/doc/unstable-book/src/library-features/alloc-system.md @@ -1,10 +1,10 @@ # `alloc_system` -The tracking issue for this feature is: [#33082] +The tracking issue for this feature is: [#32838] -[#33082]: https://github.com/rust-lang/rust/issues/33082 +[#32838]: https://github.com/rust-lang/rust/issues/32838 -See also [`alloc_jemalloc`](library-features/alloc-jemalloc.html). +See also [`global_allocator`](language-features/global-allocator.html). ------------------------ @@ -30,13 +30,18 @@ memory. Although the compiler's default choices may work most of the time, it's often necessary to tweak certain aspects. Overriding the compiler's decision about -which allocator is in use is done simply by linking to the desired allocator: +which allocator is in use is done through the `#[global_allocator]` attribute: ```rust,no_run -#![feature(alloc_system)] +#![feature(alloc_system, global_allocator, allocator_api)] extern crate alloc_system; +use alloc_system::System; + +#[global_allocator] +static A: System = System; + fn main() { let a = Box::new(4); // Allocates from the system allocator. println!("{}", a); @@ -47,11 +52,22 @@ In this example the binary generated will not link to jemalloc by default but instead use the system allocator. Conversely to generate a dynamic library which uses jemalloc by default one would write: +(The `alloc_jemalloc` crate cannot be used to control the global allocator, +crate.io’s `jemallocator` crate provides equivalent functionality.) + +```toml +# Cargo.toml +[dependencies] +jemallocator = "0.1" +``` ```rust,ignore -#![feature(alloc_jemalloc)] +#![feature(global_allocator)] #![crate_type = "dylib"] -extern crate alloc_jemalloc; +extern crate jemallocator; + +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; pub fn foo() { let a = Box::new(4); // Allocates from jemalloc. @@ -59,4 +75,3 @@ pub fn foo() { } # fn main() {} ``` - diff --git a/src/doc/unstable-book/src/library-features/collections.md b/src/doc/unstable-book/src/library-features/collections.md deleted file mode 100644 index 5c937833c9e26..0000000000000 --- a/src/doc/unstable-book/src/library-features/collections.md +++ /dev/null @@ -1,5 +0,0 @@ -# `collections` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/doc/unstable-book/src/library-features/entry-and-modify.md b/src/doc/unstable-book/src/library-features/entry-and-modify.md new file mode 100644 index 0000000000000..1280c71e83c92 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/entry-and-modify.md @@ -0,0 +1,77 @@ +# `entry_and_modify` + +The tracking issue for this feature is: [#44733] + +[#44733]: https://github.com/rust-lang/rust/issues/44733 + +------------------------ + +This introduces a new method for the Entry API of maps +(`std::collections::HashMap` and `std::collections::BTreeMap`), so that +occupied entries can be modified before any potential inserts into the +map. + +For example: + +```rust +#![feature(entry_and_modify)] +# fn main() { +use std::collections::HashMap; + +struct Foo { + new: bool, +} + +let mut map: HashMap<&str, Foo> = HashMap::new(); + +map.entry("quux") + .and_modify(|e| e.new = false) + .or_insert(Foo { new: true }); +# } +``` + +This is not possible with the stable API alone since inserting a default +_before_ modifying the `new` field would mean we would lose the default state: + +```rust +# fn main() { +use std::collections::HashMap; + +struct Foo { + new: bool, +} + +let mut map: HashMap<&str, Foo> = HashMap::new(); + +map.entry("quux").or_insert(Foo { new: true }).new = false; +# } +``` + +In the above code the `new` field will never be `true`, even though we only +intended to update that field to `false` for previously extant entries. + +To achieve the same effect as `and_modify` we would have to manually match +against the `Occupied` and `Vacant` variants of the `Entry` enum, which is +a little less user-friendly, and much more verbose: + +```rust +# fn main() { +use std::collections::HashMap; +use std::collections::hash_map::Entry; + +struct Foo { + new: bool, +} + +let mut map: HashMap<&str, Foo> = HashMap::new(); + +match map.entry("quux") { + Entry::Occupied(entry) => { + entry.into_mut().new = false; + }, + Entry::Vacant(entry) => { + entry.insert(Foo { new: true }); + }, +}; +# } +``` diff --git a/src/doc/unstable-book/src/library-features/fn-traits.md b/src/doc/unstable-book/src/library-features/fn-traits.md new file mode 100644 index 0000000000000..72a3f36c10b69 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/fn-traits.md @@ -0,0 +1,35 @@ +# `fn_traits` + +The tracking issue for this feature is [#29625] + +See Also: [`unboxed_closures`](language-features/unboxed-closures.html) + +[#29625]: https://github.com/rust-lang/rust/issues/29625 + +---- + +The `fn_traits` feature allows for implementation of the [`Fn*`] traits +for creating custom closure-like types. + +[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html + +```rust +#![feature(unboxed_closures)] +#![feature(fn_traits)] + +struct Adder { + a: u32 +} + +impl FnOnce<(u32, )> for Adder { + type Output = u32; + extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { + self.a + b.0 + } +} + +fn main() { + let adder = Adder { a: 3 }; + assert_eq!(adder(2), 5); +} +``` diff --git a/src/doc/unstable-book/src/library-features/hint-core-should-pause.md b/src/doc/unstable-book/src/library-features/hint-core-should-pause.md deleted file mode 100644 index 05e057be4932d..0000000000000 --- a/src/doc/unstable-book/src/library-features/hint-core-should-pause.md +++ /dev/null @@ -1,41 +0,0 @@ -# `hint_core_should_pause` - -The tracking issue for this feature is: [#41196] - -[#41196]: https://github.com/rust-lang/rust/issues/41196 - ------------------------- - -Many programs have spin loops like the following: - -```rust,no_run -use std::sync::atomic::{AtomicBool,Ordering}; - -fn spin_loop(value: &AtomicBool) { - loop { - if value.load(Ordering::Acquire) { - break; - } - } -} -``` - -These programs can be improved in performance like so: - -```rust,no_run -#![feature(hint_core_should_pause)] -use std::sync::atomic; -use std::sync::atomic::{AtomicBool,Ordering}; - -fn spin_loop(value: &AtomicBool) { - loop { - if value.load(Ordering::Acquire) { - break; - } - atomic::hint_core_should_pause(); - } -} -``` - -Further improvements could combine `hint_core_should_pause` with -exponential backoff or `std::thread::yield_now`. diff --git a/src/doc/unstable-book/src/library-features/rand.md b/src/doc/unstable-book/src/library-features/rand.md deleted file mode 100644 index d0229d94c20bf..0000000000000 --- a/src/doc/unstable-book/src/library-features/rand.md +++ /dev/null @@ -1,5 +0,0 @@ -# `rand` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/etc/cat-and-grep.sh b/src/etc/cat-and-grep.sh new file mode 100755 index 0000000000000..ef9884d2e980d --- /dev/null +++ b/src/etc/cat-and-grep.sh @@ -0,0 +1,89 @@ +#!/bin/sh +set -eu + +# Copyright 2017 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# Performs `cat` and `grep` simultaneously for `run-make` tests in the Rust CI. +# +# This program will read lines from stdin and print them to stdout immediately. +# At the same time, it will check if the input line contains the substring or +# regex specified in the command line. If any match is found, the program will +# set the exit code to 0, otherwise 1. +# +# This is written to simplify debugging runmake tests. Since `grep` swallows all +# output, when a test involving `grep` failed, it is impossible to know the +# reason just by reading the failure log. While it is possible to `tee` the +# output into another stream, it becomes pretty annoying to do this for all test +# cases. + +USAGE=' +cat-and-grep.sh [-v] [-e] [-i] s1 s2 s3 ... < input.txt + +Prints the stdin, and exits successfully only if all of `sN` can be found in +some lines of the input. + +Options: + -v Invert match, exits successfully only if all of `sN` cannot be found + -e Regex search, search using extended Regex instead of fixed string + -i Case insensitive search. +' + +GREPPER=fgrep +INVERT=0 +GREPFLAGS='q' +while getopts ':vieh' OPTION; do + case "$OPTION" in + v) + INVERT=1 + ERROR_MSG='should not be found' + ;; + i) + GREPFLAGS="i$GREPFLAGS" + ;; + e) + GREPPER=egrep + ;; + h) + echo "$USAGE" + exit 2 + ;; + *) + break + ;; + esac +done + +shift $((OPTIND - 1)) + +LOG=$(mktemp -t cgrep.XXXXXX) +trap "rm -f $LOG" EXIT + +printf "[[[ begin stdout ]]]\n\033[90m" +tee "$LOG" +echo >> "$LOG" # ensure at least 1 line of output, otherwise `grep -v` may unconditionally fail. +printf "\033[0m\n[[[ end stdout ]]]\n" + +HAS_ERROR=0 +for MATCH in "$@"; do + if "$GREPPER" "-$GREPFLAGS" -- "$MATCH" "$LOG"; then + if [ "$INVERT" = 1 ]; then + printf "\033[1;31mError: should not match: %s\033[0m\n" "$MATCH" + HAS_ERROR=1 + fi + else + if [ "$INVERT" = 0 ]; then + printf "\033[1;31mError: cannot match: %s\033[0m\n" "$MATCH" + HAS_ERROR=1 + fi + fi +done + +exit "$HAS_ERROR" diff --git a/src/etc/char_private.py b/src/etc/char_private.py index 75ab3f1a17be4..cfe5b01e934e7 100644 --- a/src/etc/char_private.py +++ b/src/etc/char_private.py @@ -177,7 +177,7 @@ def main(): normal1 = compress_normal(normal1) print("""\ -// Copyright 2012-2016 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. // @@ -226,7 +226,7 @@ def main(): current } -pub fn is_printable(x: char) -> bool { +pub(crate) fn is_printable(x: char) -> bool { let x = x as u32; let lower = x as u16; if x < 0x10000 { diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index 822dc58140470..0612873e28153 100755 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -248,7 +248,10 @@ def __init__(self, val): def to_string(self): (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) raw_ptr = data_ptr.get_wrapped_value() - return '"%s"' % raw_ptr.string(encoding="utf-8", length=length) + return raw_ptr.lazy_string(encoding="utf-8", length=length) + + def display_hint(self): + return "string" class RustStdVecPrinter(object): @@ -278,9 +281,11 @@ def __init__(self, val): def to_string(self): vec = self.__val.get_child_at_index(0) (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) - return '"%s"' % data_ptr.get_wrapped_value().string(encoding="utf-8", - length=length) + return data_ptr.get_wrapped_value().lazy_string(encoding="utf-8", + length=length) + def display_hint(self): + return "string" class RustOsStringPrinter(object): def __init__(self, val): @@ -294,8 +299,10 @@ def to_string(self): (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec( vec) - return '"%s"' % data_ptr.get_wrapped_value().string(length=length) + return data_ptr.get_wrapped_value().lazy_string(length=length) + def display_hint(self): + return "string" class RustCStyleVariantPrinter(object): def __init__(self, val): diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py index 6642da858e551..15c9fc2e504a1 100755 --- a/src/etc/generate-deriving-span-tests.py +++ b/src/etc/generate-deriving-span-tests.py @@ -74,7 +74,7 @@ ENUM_TUPLE, ENUM_STRUCT, STRUCT_FIELDS, STRUCT_TUPLE = range(4) -def create_test_case(type, trait, super_traits, number_of_errors): +def create_test_case(type, trait, super_traits, error_count): string = [ENUM_STRING, ENUM_STRUCT_VARIANT_STRING, STRUCT_STRING, STRUCT_TUPLE_STRING][type] all_traits = ','.join([trait] + super_traits) super_traits = ','.join(super_traits) @@ -113,7 +113,7 @@ def write_file(name, string): for (trait, supers, errs) in [('Clone', [], 1), ('PartialEq', [], 2), - ('PartialOrd', ['PartialEq'], 9), + ('PartialOrd', ['PartialEq'], 3), ('Eq', ['PartialEq'], 1), ('Ord', ['Eq', 'PartialOrd', 'PartialEq'], 1), ('Debug', [], 1), diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 7e8fde2034640..8a11c6f7cfc4c 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -99,6 +99,8 @@ * `@count PATH XPATH COUNT' checks for the occurrence of given XPath in the given file. The number of occurrences must match the given count. +* `@has-dir PATH` checks for the existence of the given directory. + All conditions can be negated with `!`. `@!has foo/type.NoSuch.html` checks if the given file does not exist, for example. @@ -308,6 +310,12 @@ def get_tree(self, path): self.trees[path] = tree return self.trees[path] + def get_dir(self, path): + path = self.resolve_path(path) + abspath = os.path.join(self.root, path) + if not(os.path.exists(abspath) and os.path.isdir(abspath)): + raise FailedCheck('Directory does not exist {!r}'.format(path)) + def check_string(data, pat, regexp): if not pat: @@ -407,6 +415,16 @@ def check_command(c, cache): ret = expected == found else: raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) + elif c.cmd == 'has-dir': # has-dir test + if len(c.args) == 1: # @has-dir = has-dir test + try: + cache.get_dir(c.args[0]) + ret = True + except FailedCheck as err: + cerr = str(err) + ret = False + else: + raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) elif c.cmd == 'valid-html': raise InvalidCheck('Unimplemented @valid-html') diff --git a/src/etc/indenter b/src/etc/indenter index b3eed6a144342..21bfc448ae2b4 100755 --- a/src/etc/indenter +++ b/src/etc/indenter @@ -13,7 +13,7 @@ while True: if more_re.match(line): indent += 1 - print "%03d %s%s" % (indent, " " * indent, line.strip()) + print("%03d %s%s" % (indent, " " * indent, line.strip())) if less_re.match(line): indent -= 1 diff --git a/src/etc/installer/exe/modpath.iss b/src/etc/installer/exe/modpath.iss index 35cc0097035c3..2cfc8698c4b67 100644 --- a/src/etc/installer/exe/modpath.iss +++ b/src/etc/installer/exe/modpath.iss @@ -144,7 +144,7 @@ begin end; end; -// Split a string into an array using passed delimeter +// Split a string into an array using passed delimiter procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String); var i: Integer; diff --git a/src/etc/installer/exe/rust.iss b/src/etc/installer/exe/rust.iss index e7d4ec6194686..c22d60b6c5df1 100644 --- a/src/etc/installer/exe/rust.iss +++ b/src/etc/installer/exe/rust.iss @@ -46,7 +46,9 @@ Name: gcc; Description: "Linker and platform libraries"; Types: full Name: docs; Description: "HTML documentation"; Types: full Name: cargo; Description: "Cargo, the Rust package manager"; Types: full Name: std; Description: "The Rust Standard Library"; Types: full +// tool-rls-start Name: rls; Description: "RLS, the Rust Language Server" +// tool-rls-end [Files] Source: "rustc/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: rust @@ -56,8 +58,10 @@ Source: "rust-mingw/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Source: "rust-docs/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: docs Source: "cargo/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: cargo Source: "rust-std/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: std +// tool-rls-start Source: "rls/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: rls Source: "rust-analysis/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: rls +// tool-rls-end [Code] const diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 258291cbb72e1..d95b096d732f4 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -170,8 +170,10 @@ + + @@ -275,6 +277,7 @@ + + diff --git a/src/etc/installer/pkg/Distribution.xml b/src/etc/installer/pkg/Distribution.xml index f138a1a315489..077ee17511655 100644 --- a/src/etc/installer/pkg/Distribution.xml +++ b/src/etc/installer/pkg/Distribution.xml @@ -16,7 +16,9 @@ + + @@ -62,6 +64,7 @@ > + + rustc.pkg cargo.pkg rust-docs.pkg rust-std.pkg + rls.pkg + rust-analysis.pkg uninstall.pkg or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is a small "shim" program which is used when wasm32 unit tests are run +// in this repository. This program is intended to be run in node.js and will +// load a wasm module into memory, instantiate it with a set of imports, and +// then run it. +// +// There's a bunch of helper functions defined here in `imports.env`, but note +// that most of them aren't actually needed to execute most programs. Many of +// these are just intended for completeness or debugging. Hopefully over time +// nothing here is needed for completeness. + +const fs = require('fs'); +const process = require('process'); +const buffer = fs.readFileSync(process.argv[2]); + +Error.stackTraceLimit = 20; + +let m = new WebAssembly.Module(buffer); + +let memory = null; + +function copystr(a, b) { + if (memory === null) { + return null + } + let view = new Uint8Array(memory.buffer).slice(a, a + b); + return String.fromCharCode.apply(null, view); +} + +let imports = {}; +imports.env = { + // These are generated by LLVM itself for various intrinsic calls. Hopefully + // one day this is not necessary and something will automatically do this. + fmod: function(x, y) { return x % y; }, + exp2: function(x) { return Math.pow(2, x); }, + exp2f: function(x) { return Math.pow(2, x); }, + ldexp: function(x, y) { return x * Math.pow(2, y); }, + ldexpf: function(x, y) { return x * Math.pow(2, y); }, + log10: Math.log10, + log10f: Math.log10, + + // These are called in src/libstd/sys/wasm/stdio.rs and are used when + // debugging is enabled. + rust_wasm_write_stdout: function(a, b) { + let s = copystr(a, b); + if (s !== null) { + process.stdout.write(s); + } + }, + rust_wasm_write_stderr: function(a, b) { + let s = copystr(a, b); + if (s !== null) { + process.stderr.write(s); + } + }, + + // These are called in src/libstd/sys/wasm/args.rs and are used when + // debugging is enabled. + rust_wasm_args_count: function() { + if (memory === null) + return 0; + return process.argv.length - 2; + }, + rust_wasm_args_arg_size: function(i) { + return Buffer.byteLength(process.argv[i + 2]); + }, + rust_wasm_args_arg_fill: function(idx, ptr) { + let arg = process.argv[idx + 2]; + let view = new Uint8Array(memory.buffer); + Buffer.from(arg).copy(view, ptr); + }, + + // These are called in src/libstd/sys/wasm/os.rs and are used when + // debugging is enabled. + rust_wasm_getenv_len: function(a, b) { + let key = copystr(a, b); + if (key === null) { + return -1; + } + if (!(key in process.env)) { + return -1; + } + return Buffer.byteLength(process.env[key]); + }, + rust_wasm_getenv_data: function(a, b, ptr) { + let key = copystr(a, b); + let value = process.env[key]; + let view = new Uint8Array(memory.buffer); + Buffer.from(value).copy(view, ptr); + }, +}; + +let module_imports = WebAssembly.Module.imports(m); + +for (var i = 0; i < module_imports.length; i++) { + let imp = module_imports[i]; + if (imp.module != 'env') { + continue + } + if (imp.name == 'memory' && imp.kind == 'memory') { + memory = new WebAssembly.Memory({initial: 20}); + imports.env.memory = memory; + } +} + +let instance = new WebAssembly.Instance(m, imports); diff --git a/src/grammar/lexer.l b/src/grammar/lexer.l index 91652bfdf2467..2f282c8281d6e 100644 --- a/src/grammar/lexer.l +++ b/src/grammar/lexer.l @@ -85,16 +85,23 @@ ident [a-zA-Z\x80-\xff_][a-zA-Z0-9\x80-\xff_]* (.|\n) { } _ { return UNDERSCORE; } +abstract { return ABSTRACT; } +alignof { return ALIGNOF; } as { return AS; } +become { return BECOME; } box { return BOX; } break { return BREAK; } +catch { return CATCH; } const { return CONST; } continue { return CONTINUE; } crate { return CRATE; } +default { return DEFAULT; } +do { return DO; } else { return ELSE; } enum { return ENUM; } extern { return EXTERN; } false { return FALSE; } +final { return FINAL; } fn { return FN; } for { return FOR; } if { return IF; } @@ -102,26 +109,36 @@ impl { return IMPL; } in { return IN; } let { return LET; } loop { return LOOP; } +macro { return MACRO; } match { return MATCH; } mod { return MOD; } move { return MOVE; } mut { return MUT; } +offsetof { return OFFSETOF; } +override { return OVERRIDE; } priv { return PRIV; } proc { return PROC; } +pure { return PURE; } pub { return PUB; } ref { return REF; } return { return RETURN; } self { return SELF; } +sizeof { return SIZEOF; } static { return STATIC; } struct { return STRUCT; } +super { return SUPER; } trait { return TRAIT; } true { return TRUE; } type { return TYPE; } typeof { return TYPEOF; } +union { return UNION; } unsafe { return UNSAFE; } +unsized { return UNSIZED; } use { return USE; } +virtual { return VIRTUAL; } where { return WHERE; } while { return WHILE; } +yield { return YIELD; } {ident} { return IDENT; } @@ -189,25 +206,25 @@ while { return WHILE; } \>\>= { return SHREQ; } \> { return '>'; } -\x27 { BEGIN(ltorchar); yymore(); } -static { BEGIN(INITIAL); return STATIC_LIFETIME; } -{ident} { BEGIN(INITIAL); return LIFETIME; } -\\[nrt\\\x27\x220]\x27 { BEGIN(suffix); return LIT_CHAR; } -\\x[0-9a-fA-F]{2}\x27 { BEGIN(suffix); return LIT_CHAR; } -\\u\{[0-9a-fA-F]?{6}\}\x27 { BEGIN(suffix); return LIT_CHAR; } -.\x27 { BEGIN(suffix); return LIT_CHAR; } -[\x80-\xff]{2,4}\x27 { BEGIN(suffix); return LIT_CHAR; } -<> { BEGIN(INITIAL); return -1; } +\x27 { BEGIN(ltorchar); yymore(); } +static { BEGIN(INITIAL); return STATIC_LIFETIME; } +{ident} { BEGIN(INITIAL); return LIFETIME; } +\\[nrt\\\x27\x220]\x27 { BEGIN(suffix); return LIT_CHAR; } +\\x[0-9a-fA-F]{2}\x27 { BEGIN(suffix); return LIT_CHAR; } +\\u\{([0-9a-fA-F]_*){1,6}\}\x27 { BEGIN(suffix); return LIT_CHAR; } +.\x27 { BEGIN(suffix); return LIT_CHAR; } +[\x80-\xff]{2,4}\x27 { BEGIN(suffix); return LIT_CHAR; } +<> { BEGIN(INITIAL); return -1; } b\x22 { BEGIN(bytestr); yymore(); } \x22 { BEGIN(suffix); return LIT_BYTE_STR; } -<> { return -1; } -\\[n\nrt\\\x27\x220] { yymore(); } -\\x[0-9a-fA-F]{2} { yymore(); } -\\u\{[0-9a-fA-F]?{6}\} { yymore(); } -\\[^n\nrt\\\x27\x220] { return -1; } -(.|\n) { yymore(); } +<> { return -1; } +\\[n\nrt\\\x27\x220] { yymore(); } +\\x[0-9a-fA-F]{2} { yymore(); } +\\u\{([0-9a-fA-F]_*){1,6}\} { yymore(); } +\\[^n\nrt\\\x27\x220] { return -1; } +(.|\n) { yymore(); } br\x22 { BEGIN(rawbytestr_nohash); yymore(); } \x22 { BEGIN(suffix); return LIT_BYTE_STR_RAW; } @@ -252,13 +269,13 @@ br/# { } <> { return -1; } -b\x27 { BEGIN(byte); yymore(); } -\\[nrt\\\x27\x220]\x27 { BEGIN(INITIAL); return LIT_BYTE; } -\\x[0-9a-fA-F]{2}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -\\u[0-9a-fA-F]{4}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -\\U[0-9a-fA-F]{8}\x27 { BEGIN(INITIAL); return LIT_BYTE; } -.\x27 { BEGIN(INITIAL); return LIT_BYTE; } -<> { BEGIN(INITIAL); return -1; } +b\x27 { BEGIN(byte); yymore(); } +\\[nrt\\\x27\x220]\x27 { BEGIN(INITIAL); return LIT_BYTE; } +\\x[0-9a-fA-F]{2}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +\\u([0-9a-fA-F]_*){4}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +\\U([0-9a-fA-F]_*){8}\x27 { BEGIN(INITIAL); return LIT_BYTE; } +.\x27 { BEGIN(INITIAL); return LIT_BYTE; } +<> { BEGIN(INITIAL); return -1; } r\x22 { BEGIN(rawstr); yymore(); } \x22 { BEGIN(suffix); return LIT_STR_RAW; } @@ -310,12 +327,12 @@ r/# { \x22 { BEGIN(str); yymore(); } \x22 { BEGIN(suffix); return LIT_STR; } -<> { return -1; } -\\[n\nr\rt\\\x27\x220] { yymore(); } -\\x[0-9a-fA-F]{2} { yymore(); } -\\u\{[0-9a-fA-F]?{6}\} { yymore(); } -\\[^n\nrt\\\x27\x220] { return -1; } -(.|\n) { yymore(); } +<> { return -1; } +\\[n\nr\rt\\\x27\x220] { yymore(); } +\\x[0-9a-fA-F]{2} { yymore(); } +\\u\{([0-9a-fA-F]_*){1,6}\} { yymore(); } +\\[^n\nrt\\\x27\x220] { return -1; } +(.|\n) { yymore(); } \<- { return LARROW; } -\> { return RARROW; } diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index c9fcdf7647b9c..de1f96aac5046 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -62,13 +62,19 @@ extern char *yytext; // keywords %token SELF %token STATIC +%token ABSTRACT +%token ALIGNOF %token AS +%token BECOME %token BREAK +%token CATCH %token CRATE +%token DO %token ELSE %token ENUM %token EXTERN %token FALSE +%token FINAL %token FN %token FOR %token IF @@ -76,19 +82,29 @@ extern char *yytext; %token IN %token LET %token LOOP +%token MACRO %token MATCH %token MOD %token MOVE %token MUT +%token OFFSETOF +%token OVERRIDE %token PRIV %token PUB +%token PURE %token REF %token RETURN +%token SIZEOF %token STRUCT +%token SUPER +%token UNION +%token UNSIZED %token TRUE %token TRAIT %token TYPE %token UNSAFE +%token VIRTUAL +%token YIELD %token DEFAULT %token USE %token WHILE @@ -141,6 +157,10 @@ extern char *yytext; // 'foo:bar . <' is shifted (in a trait reference occurring in a // bounds list), parsing as foo:(bar) rather than (foo:bar). %precedence IDENT + // Put the weak keywords that can be used as idents here as well +%precedence CATCH +%precedence DEFAULT +%precedence UNION // A couple fake-precedence symbols to use in rules associated with + // and < in trailing type contexts. These come up when you have a type @@ -161,13 +181,13 @@ extern char *yytext; %precedence FOR // Binops & unops, and their precedences +%precedence '?' %precedence BOX -%precedence BOXPLACE %nonassoc DOTDOT // RETURN needs to be lower-precedence than tokens that start // prefix_exprs -%precedence RETURN +%precedence RETURN YIELD %right '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ %right LARROW @@ -321,6 +341,8 @@ view_path | path_no_types_allowed MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 2, $1, $4); } | MOD_SEP '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $3); } | path_no_types_allowed MOD_SEP '*' { $$ = mk_node("ViewPathGlob", 1, $1); } +| MOD_SEP '*' { $$ = mk_atom("ViewPathGlob"); } +| '*' { $$ = mk_atom("ViewPathGlob"); } | '{' '}' { $$ = mk_atom("ViewPathListEmpty"); } | '{' idents_or_self '}' { $$ = mk_node("ViewPathList", 1, $2); } | '{' idents_or_self ',' '}' { $$ = mk_node("ViewPathList", 1, $2); } @@ -334,6 +356,7 @@ block_item | item_foreign_mod { $$ = mk_node("ItemForeignMod", 1, $1); } | item_struct | item_enum +| item_union | item_trait | item_impl ; @@ -387,6 +410,7 @@ struct_decl_field struct_tuple_fields : struct_tuple_field { $$ = mk_node("StructFields", 1, $1); } | struct_tuple_fields ',' struct_tuple_field { $$ = ext_node($1, 1, $3); } +| %empty { $$ = mk_none(); } ; struct_tuple_field @@ -417,6 +441,11 @@ enum_args | %empty { $$ = mk_none(); } ; +// unions +item_union +: UNION ident generic_params maybe_where_clause '{' struct_decl_fields '}' { $$ = mk_node("ItemUnion", 0); } +| UNION ident generic_params maybe_where_clause '{' struct_decl_fields ',' '}' { $$ = mk_node("ItemUnion", 0); } + item_mod : MOD ident ';' { $$ = mk_node("ItemMod", 1, $2); } | MOD ident '{' maybe_mod_items '}' { $$ = mk_node("ItemMod", 2, $2, $4); } @@ -475,7 +504,7 @@ visibility idents_or_self : ident_or_self { $$ = mk_node("IdentsOrSelf", 1, $1); } -| ident_or_self AS ident { $$ = mk_node("IdentsOrSelf", 2, $1, $3); } +| idents_or_self AS ident { $$ = mk_node("IdentsOrSelf", 2, $1, $3); } | idents_or_self ',' ident_or_self { $$ = ext_node($1, 1, $3); } ; @@ -515,6 +544,7 @@ trait_item : trait_const | trait_type | trait_method +| maybe_outer_attrs item_macro { $$ = mk_node("TraitMacroItem", 2, $1, $2); } ; trait_const @@ -547,36 +577,48 @@ trait_method ; type_method -: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' +: maybe_outer_attrs maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' { $$ = mk_node("TypeMethod", 6, $1, $2, $4, $5, $6, $7); } -| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' +| maybe_outer_attrs CONST maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' +{ + $$ = mk_node("TypeMethod", 6, $1, $3, $5, $6, $7, $8); +} +| maybe_outer_attrs maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause ';' { $$ = mk_node("TypeMethod", 7, $1, $2, $4, $6, $7, $8, $9); } ; method -: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block +: maybe_outer_attrs maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block { $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8); } -| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block +| maybe_outer_attrs CONST maybe_unsafe FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 7, $1, $3, $5, $6, $7, $8, $9); +} +| maybe_outer_attrs maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause inner_attrs_and_block { $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); } ; impl_method -: attrs_and_vis maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block +: attrs_and_vis maybe_default maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block { - $$ = mk_node("Method", 7, $1, $2, $4, $5, $6, $7, $8); + $$ = mk_node("Method", 8, $1, $2, $3, $5, $6, $7, $8, $9); } -| attrs_and_vis maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block +| attrs_and_vis maybe_default CONST maybe_unsafe FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block { $$ = mk_node("Method", 8, $1, $2, $4, $6, $7, $8, $9, $10); } +| attrs_and_vis maybe_default maybe_unsafe EXTERN maybe_abi FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("Method", 9, $1, $2, $3, $5, $7, $8, $9, $10, $11); +} ; // There are two forms of impl: @@ -638,12 +680,17 @@ impl_item | impl_type ; +maybe_default +: DEFAULT { $$ = mk_atom("Default"); } +| %empty { $$ = mk_none(); } +; + impl_const -: attrs_and_vis item_const { $$ = mk_node("ImplConst", 1, $1, $2); } +: attrs_and_vis maybe_default item_const { $$ = mk_node("ImplConst", 3, $1, $2, $3); } ; impl_type -: attrs_and_vis TYPE ident generic_params '=' ty_sum ';' { $$ = mk_node("ImplType", 4, $1, $3, $4, $6); } +: attrs_and_vis maybe_default TYPE ident generic_params '=' ty_sum ';' { $$ = mk_node("ImplType", 5, $1, $2, $4, $5, $7); } ; item_fn @@ -651,6 +698,10 @@ item_fn { $$ = mk_node("ItemFn", 5, $2, $3, $4, $5, $6); } +| CONST FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("ItemFn", 5, $3, $4, $5, $6, $7); +} ; item_unsafe_fn @@ -658,6 +709,10 @@ item_unsafe_fn { $$ = mk_node("ItemUnsafeFn", 5, $3, $4, $5, $6, $7); } +| CONST UNSAFE FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block +{ + $$ = mk_node("ItemUnsafeFn", 5, $4, $5, $6, $7, $8); +} | UNSAFE EXTERN maybe_abi FN ident generic_params fn_decl maybe_where_clause inner_attrs_and_block { $$ = mk_node("ItemUnsafeFn", 6, $3, $5, $6, $7, $8, $9); @@ -723,12 +778,6 @@ inferrable_param : pat maybe_ty_ascription { $$ = mk_node("InferrableParam", 2, $1, $2); } ; -maybe_unboxed_closure_kind -: %empty -| ':' -| '&' maybe_mut ':' -; - maybe_comma_params : ',' { $$ = mk_none(); } | ',' params { $$ = $2; } @@ -784,7 +833,8 @@ ret_ty ; generic_params -: '<' lifetimes '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } +: '<' '>' { $$ = mk_node("Generics", 2, mk_none(), mk_none()); } +| '<' lifetimes '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } | '<' lifetimes ',' '>' { $$ = mk_node("Generics", 2, $2, mk_none()); } | '<' lifetimes SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } | '<' lifetimes ',' SHR { push_back('>'); $$ = mk_node("Generics", 2, $2, mk_none()); } @@ -837,6 +887,8 @@ path_no_types_allowed | MOD_SEP ident { $$ = mk_node("ViewPath", 1, $2); } | SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } | MOD_SEP SELF { $$ = mk_node("ViewPath", 1, mk_atom("Self")); } +| SUPER { $$ = mk_node("ViewPath", 1, mk_atom("Super")); } +| MOD_SEP SUPER { $$ = mk_node("ViewPath", 1, mk_atom("Super")); } | path_no_types_allowed MOD_SEP ident { $$ = ext_node($1, 1, $3); } ; @@ -882,7 +934,7 @@ generic_args ; generic_values -: maybe_lifetimes maybe_ty_sums_and_or_bindings { $$ = mk_node("GenericValues", 2, $1, $2); } +: maybe_ty_sums_and_or_bindings { $$ = mk_node("GenericValues", 1, $1); } ; maybe_ty_sums_and_or_bindings @@ -910,12 +962,11 @@ pat | ANDAND pat { $$ = mk_node("PatRegion", 1, mk_node("PatRegion", 1, $2)); } | '(' ')' { $$ = mk_atom("PatUnit"); } | '(' pat_tup ')' { $$ = mk_node("PatTup", 1, $2); } -| '(' pat_tup ',' ')' { $$ = mk_node("PatTup", 1, $2); } | '[' pat_vec ']' { $$ = mk_node("PatVec", 1, $2); } | lit_or_path | lit_or_path DOTDOTDOT lit_or_path { $$ = mk_node("PatRange", 2, $1, $3); } | path_expr '{' pat_struct '}' { $$ = mk_node("PatStruct", 2, $1, $3); } -| path_expr '(' DOTDOT ')' { $$ = mk_node("PatEnum", 1, $1); } +| path_expr '(' ')' { $$ = mk_node("PatEnum", 2, $1, mk_none()); } | path_expr '(' pat_tup ')' { $$ = mk_node("PatEnum", 2, $1, $3); } | path_expr '!' maybe_ident delimited_token_trees { $$ = mk_node("PatMac", 3, $1, $3, $4); } | binding_mode ident { $$ = mk_node("PatIdent", 2, $1, $2); } @@ -953,6 +1004,7 @@ pat_field | BOX binding_mode ident { $$ = mk_node("PatField", 3, mk_atom("box"), $2, $3); } | ident ':' pat { $$ = mk_node("PatField", 2, $1, $3); } | binding_mode ident ':' pat { $$ = mk_node("PatField", 3, $1, $2, $4); } +| LIT_INTEGER ':' pat { $$ = mk_node("PatField", 2, mk_atom(yytext), $3); } ; pat_fields @@ -965,11 +1017,26 @@ pat_struct | pat_fields ',' { $$ = mk_node("PatStruct", 2, $1, mk_atom("false")); } | pat_fields ',' DOTDOT { $$ = mk_node("PatStruct", 2, $1, mk_atom("true")); } | DOTDOT { $$ = mk_node("PatStruct", 1, mk_atom("true")); } +| %empty { $$ = mk_node("PatStruct", 1, mk_none()); } ; pat_tup -: pat { $$ = mk_node("pat_tup", 1, $1); } -| pat_tup ',' pat { $$ = ext_node($1, 1, $3); } +: pat_tup_elts { $$ = mk_node("PatTup", 2, $1, mk_none()); } +| pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, mk_none()); } +| pat_tup_elts DOTDOT { $$ = mk_node("PatTup", 2, $1, mk_none()); } +| pat_tup_elts ',' DOTDOT { $$ = mk_node("PatTup", 2, $1, mk_none()); } +| pat_tup_elts DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, $1, $4); } +| pat_tup_elts DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, $4); } +| pat_tup_elts ',' DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, $1, $5); } +| pat_tup_elts ',' DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, $1, $5); } +| DOTDOT ',' pat_tup_elts { $$ = mk_node("PatTup", 2, mk_none(), $3); } +| DOTDOT ',' pat_tup_elts ',' { $$ = mk_node("PatTup", 2, mk_none(), $3); } +| DOTDOT { $$ = mk_node("PatTup", 2, mk_none(), mk_none()); } +; + +pat_tup_elts +: pat { $$ = mk_node("PatTupElts", 1, $1); } +| pat_tup_elts ',' pat { $$ = ext_node($1, 1, $3); } ; pat_vec @@ -1007,24 +1074,25 @@ ty ; ty_prim -: %prec IDENT path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("false")), $1); } -| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("true")), $2); } -| %prec IDENT SELF MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("self", 1, mk_atom("true")), $3); } -| BOX ty { $$ = mk_node("TyBox", 1, $2); } -| '*' maybe_mut_or_const ty { $$ = mk_node("TyPtr", 2, $2, $3); } -| '&' ty { $$ = mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2); } -| '&' MUT ty { $$ = mk_node("TyRptr", 2, mk_atom("MutMutable"), $3); } -| ANDAND ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2)); } -| ANDAND MUT ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutMutable"), $3)); } -| '&' lifetime maybe_mut ty { $$ = mk_node("TyRptr", 3, $2, $3, $4); } -| ANDAND lifetime maybe_mut ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 3, $2, $3, $4)); } -| '[' ty ']' { $$ = mk_node("TyVec", 1, $2); } -| '[' ty ',' DOTDOT expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $5); } -| '[' ty ';' expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $4); } -| TYPEOF '(' expr ')' { $$ = mk_node("TyTypeof", 1, $3); } -| UNDERSCORE { $$ = mk_atom("TyInfer"); } +: %prec IDENT path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("false")), $1); } +| %prec IDENT MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("global", 1, mk_atom("true")), $2); } +| %prec IDENT SELF MOD_SEP path_generic_args_without_colons { $$ = mk_node("TyPath", 2, mk_node("self", 1, mk_atom("true")), $3); } +| %prec IDENT path_generic_args_without_colons '!' maybe_ident delimited_token_trees { $$ = mk_node("TyMacro", 3, $1, $3, $4); } +| %prec IDENT MOD_SEP path_generic_args_without_colons '!' maybe_ident delimited_token_trees { $$ = mk_node("TyMacro", 3, $2, $4, $5); } +| BOX ty { $$ = mk_node("TyBox", 1, $2); } +| '*' maybe_mut_or_const ty { $$ = mk_node("TyPtr", 2, $2, $3); } +| '&' ty { $$ = mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2); } +| '&' MUT ty { $$ = mk_node("TyRptr", 2, mk_atom("MutMutable"), $3); } +| ANDAND ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutImmutable"), $2)); } +| ANDAND MUT ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 2, mk_atom("MutMutable"), $3)); } +| '&' lifetime maybe_mut ty { $$ = mk_node("TyRptr", 3, $2, $3, $4); } +| ANDAND lifetime maybe_mut ty { $$ = mk_node("TyRptr", 1, mk_node("TyRptr", 3, $2, $3, $4)); } +| '[' ty ']' { $$ = mk_node("TyVec", 1, $2); } +| '[' ty ',' DOTDOT expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $5); } +| '[' ty ';' expr ']' { $$ = mk_node("TyFixedLengthVec", 2, $2, $4); } +| TYPEOF '(' expr ')' { $$ = mk_node("TyTypeof", 1, $3); } +| UNDERSCORE { $$ = mk_atom("TyInfer"); } | ty_bare_fn -| ty_proc | for_in_type ; @@ -1046,17 +1114,12 @@ ty_closure | OROR maybe_bounds ret_ty { $$ = mk_node("TyClosure", 2, $2, $3); } ; -ty_proc -: PROC generic_params fn_params maybe_bounds ret_ty { $$ = mk_node("TyProc", 4, $2, $3, $4, $5); } -; - for_in_type : FOR '<' maybe_lifetimes '>' for_in_type_suffix { $$ = mk_node("ForInType", 2, $3, $5); } ; for_in_type_suffix -: ty_proc -| ty_bare_fn +: ty_bare_fn | trait_ref | ty_closure ; @@ -1100,13 +1163,23 @@ ty_sums ; ty_sum -: ty { $$ = mk_node("TySum", 1, $1); } -| ty '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); } +: ty_sum_elt { $$ = mk_node("TySum", 1, $1); } +| ty_sum '+' ty_sum_elt { $$ = ext_node($1, 1, $3); } +; + +ty_sum_elt +: ty +| lifetime ; ty_prim_sum -: ty_prim { $$ = mk_node("TySum", 1, $1); } -| ty_prim '+' ty_param_bounds { $$ = mk_node("TySum", 2, $1, $3); } +: ty_prim_sum_elt { $$ = mk_node("TySum", 1, $1); } +| ty_prim_sum '+' ty_prim_sum_elt { $$ = ext_node($1, 1, $3); } +; + +ty_prim_sum_elt +: ty_prim +| lifetime ; maybe_ty_param_bounds @@ -1127,6 +1200,7 @@ boundseq polybound : FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $3, $5); } | bound +| '?' FOR '<' maybe_lifetimes '>' bound { $$ = mk_node("PolyBound", 2, $4, $6); } | '?' bound { $$ = $2; } ; @@ -1244,11 +1318,6 @@ maybe_stmts // block, nonblock-prefix, and nonblock-nonprefix. // // In non-stmts contexts, expr can relax this trichotomy. -// -// There is also one other expr subtype: nonparen_expr disallows exprs -// surrounded by parens (including tuple expressions), this is -// necessary for BOX (place) expressions, so a parens expr following -// the BOX is always parsed as the place. stmts : stmt { $$ = mk_node("stmts", 1, $1); } @@ -1256,14 +1325,15 @@ stmts ; stmt -: let +: maybe_outer_attrs let { $$ = $2; } | stmt_item | PUB stmt_item { $$ = $2; } | outer_attrs stmt_item { $$ = $2; } | outer_attrs PUB stmt_item { $$ = $3; } | full_block_expr -| block -| nonblock_expr ';' +| maybe_outer_attrs block { $$ = $2; } +| nonblock_expr ';' +| outer_attrs nonblock_expr ';' { $$ = $2; } | ';' { $$ = mk_none(); } ; @@ -1296,7 +1366,9 @@ path_expr // expressions. path_generic_args_with_colons : ident { $$ = mk_node("components", 1, $1); } +| SUPER { $$ = mk_atom("Super"); } | path_generic_args_with_colons MOD_SEP ident { $$ = ext_node($1, 1, $3); } +| path_generic_args_with_colons MOD_SEP SUPER { $$ = ext_node($1, 1, mk_atom("Super")); } | path_generic_args_with_colons MOD_SEP generic_args { $$ = ext_node($1, 1, $3); } ; @@ -1313,6 +1385,7 @@ nonblock_expr | SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } | macro_expr { $$ = mk_node("ExprMac", 1, $1); } | path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| nonblock_expr '?' { $$ = mk_node("ExprTry", 1, $1); } | nonblock_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } | nonblock_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } | nonblock_expr '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } @@ -1325,6 +1398,8 @@ nonblock_expr | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); } +| YIELD { $$ = mk_node("ExprYield", 0); } +| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } | nonblock_expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } @@ -1360,8 +1435,8 @@ nonblock_expr | DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } | DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } | nonblock_expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } -| %prec BOXPLACE BOX '(' maybe_expr ')' nonblock_expr { $$ = mk_node("ExprBox", 2, $3, $5); } +| nonblock_expr ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } +| BOX expr { $$ = mk_node("ExprBox", 1, $2); } | expr_qualified_path | nonblock_prefix_expr ; @@ -1373,6 +1448,7 @@ expr | SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } | macro_expr { $$ = mk_node("ExprMac", 1, $1); } | path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } +| expr '?' { $$ = mk_node("ExprTry", 1, $1); } | expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } | expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } | expr '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } @@ -1385,6 +1461,8 @@ expr | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| YIELD { $$ = mk_node("ExprYield", 0); } +| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } | expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } @@ -1420,69 +1498,8 @@ expr | DOTDOT expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } | DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } | expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } -| %prec BOXPLACE BOX '(' maybe_expr ')' expr { $$ = mk_node("ExprBox", 2, $3, $5); } -| expr_qualified_path -| block_expr -| block -| nonblock_prefix_expr -; - -nonparen_expr -: lit { $$ = mk_node("ExprLit", 1, $1); } -| %prec IDENT - path_expr { $$ = mk_node("ExprPath", 1, $1); } -| SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } -| macro_expr { $$ = mk_node("ExprMac", 1, $1); } -| path_expr '{' struct_expr_fields '}' { $$ = mk_node("ExprStruct", 2, $1, $3); } -| nonparen_expr '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } -| nonparen_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } -| nonparen_expr '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } -| nonparen_expr '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 2, $1, $3); } -| '[' vec_expr ']' { $$ = mk_node("ExprVec", 1, $2); } -| CONTINUE { $$ = mk_node("ExprAgain", 0); } -| CONTINUE ident { $$ = mk_node("ExprAgain", 1, $2); } -| RETURN { $$ = mk_node("ExprRet", 0); } -| RETURN expr { $$ = mk_node("ExprRet", 1, $2); } -| BREAK { $$ = mk_node("ExprBreak", 0); } -| BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } -| nonparen_expr LARROW nonparen_expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } -| nonparen_expr '=' nonparen_expr { $$ = mk_node("ExprAssign", 2, $1, $3); } -| nonparen_expr SHLEQ nonparen_expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } -| nonparen_expr SHREQ nonparen_expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } -| nonparen_expr MINUSEQ nonparen_expr { $$ = mk_node("ExprAssignSub", 2, $1, $3); } -| nonparen_expr ANDEQ nonparen_expr { $$ = mk_node("ExprAssignBitAnd", 2, $1, $3); } -| nonparen_expr OREQ nonparen_expr { $$ = mk_node("ExprAssignBitOr", 2, $1, $3); } -| nonparen_expr PLUSEQ nonparen_expr { $$ = mk_node("ExprAssignAdd", 2, $1, $3); } -| nonparen_expr STAREQ nonparen_expr { $$ = mk_node("ExprAssignMul", 2, $1, $3); } -| nonparen_expr SLASHEQ nonparen_expr { $$ = mk_node("ExprAssignDiv", 2, $1, $3); } -| nonparen_expr CARETEQ nonparen_expr { $$ = mk_node("ExprAssignBitXor", 2, $1, $3); } -| nonparen_expr PERCENTEQ nonparen_expr { $$ = mk_node("ExprAssignRem", 2, $1, $3); } -| nonparen_expr OROR nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiOr"), $1, $3); } -| nonparen_expr ANDAND nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAnd"), $1, $3); } -| nonparen_expr EQEQ nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiEq"), $1, $3); } -| nonparen_expr NE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiNe"), $1, $3); } -| nonparen_expr '<' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLt"), $1, $3); } -| nonparen_expr '>' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGt"), $1, $3); } -| nonparen_expr LE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiLe"), $1, $3); } -| nonparen_expr GE nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiGe"), $1, $3); } -| nonparen_expr '|' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitOr"), $1, $3); } -| nonparen_expr '^' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitXor"), $1, $3); } -| nonparen_expr '&' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiBitAnd"), $1, $3); } -| nonparen_expr SHL nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShl"), $1, $3); } -| nonparen_expr SHR nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiShr"), $1, $3); } -| nonparen_expr '+' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiAdd"), $1, $3); } -| nonparen_expr '-' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiSub"), $1, $3); } -| nonparen_expr '*' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiMul"), $1, $3); } -| nonparen_expr '/' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiDiv"), $1, $3); } -| nonparen_expr '%' nonparen_expr { $$ = mk_node("ExprBinary", 3, mk_atom("BiRem"), $1, $3); } -| nonparen_expr DOTDOT { $$ = mk_node("ExprRange", 2, $1, mk_none()); } -| nonparen_expr DOTDOT nonparen_expr { $$ = mk_node("ExprRange", 2, $1, $3); } -| DOTDOT nonparen_expr { $$ = mk_node("ExprRange", 2, mk_none(), $2); } -| DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } -| nonparen_expr AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } -| %prec BOXPLACE BOX '(' maybe_expr ')' expr { $$ = mk_node("ExprBox", 1, $3, $5); } +| expr ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } +| BOX expr { $$ = mk_node("ExprBox", 1, $2); } | expr_qualified_path | block_expr | block @@ -1495,6 +1512,7 @@ expr_nostruct path_expr { $$ = mk_node("ExprPath", 1, $1); } | SELF { $$ = mk_node("ExprPath", 1, mk_node("ident", 1, mk_atom("self"))); } | macro_expr { $$ = mk_node("ExprMac", 1, $1); } +| expr_nostruct '?' { $$ = mk_node("ExprTry", 1, $1); } | expr_nostruct '.' path_generic_args_with_colons { $$ = mk_node("ExprField", 2, $1, $3); } | expr_nostruct '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } | expr_nostruct '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 2, $1, $3); } @@ -1507,6 +1525,8 @@ expr_nostruct | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| YIELD { $$ = mk_node("ExprYield", 0); } +| YIELD expr { $$ = mk_node("ExprYield", 1, $2); } | expr_nostruct LARROW expr_nostruct { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); } @@ -1542,8 +1562,8 @@ expr_nostruct | DOTDOT expr_nostruct { $$ = mk_node("ExprRange", 2, mk_none(), $2); } | DOTDOT { $$ = mk_node("ExprRange", 2, mk_none(), mk_none()); } | expr_nostruct AS ty { $$ = mk_node("ExprCast", 2, $1, $3); } -| BOX nonparen_expr { $$ = mk_node("ExprBox", 1, $2); } -| %prec BOXPLACE BOX '(' maybe_expr ')' expr_nostruct { $$ = mk_node("ExprBox", 1, $3, $5); } +| expr_nostruct ':' ty { $$ = mk_node("ExprTypeAscr", 2, $1, $3); } +| BOX expr { $$ = mk_node("ExprBox", 1, $2); } | expr_qualified_path | block_expr | block @@ -1558,7 +1578,6 @@ nonblock_prefix_expr_nostruct | ANDAND maybe_mut expr_nostruct { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } | lambda_expr_nostruct | MOVE lambda_expr_nostruct { $$ = $2; } -| proc_expr_nostruct ; nonblock_prefix_expr @@ -1569,7 +1588,6 @@ nonblock_prefix_expr | ANDAND maybe_mut expr { $$ = mk_node("ExprAddrOf", 1, mk_node("ExprAddrOf", 2, $2, $3)); } | lambda_expr | MOVE lambda_expr { $$ = $2; } -| proc_expr ; expr_qualified_path @@ -1606,43 +1624,42 @@ maybe_as_trait_ref lambda_expr : %prec LAMBDA - OROR ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } -| %prec LAMBDA - '|' maybe_unboxed_closure_kind '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $4, $5); } + OROR ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } | %prec LAMBDA - '|' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $2, $4, $5); } + '|' '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $3, $4); } | %prec LAMBDA - '|' '&' maybe_mut ':' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $5, $7, $8); } + '|' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $2, $4, $5); } | %prec LAMBDA - '|' ':' inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $3, $5, $6); } + '|' inferrable_params OROR lambda_expr_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $2, mk_none(), $4); } ; -lambda_expr_nostruct +lambda_expr_no_first_bar : %prec LAMBDA - OROR expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $2); } -| %prec LAMBDA - '|' maybe_unboxed_closure_kind '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $4); } + '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } | %prec LAMBDA - '|' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $2, $4); } + inferrable_params '|' ret_ty expr { $$ = mk_node("ExprFnBlock", 3, $1, $3, $4); } | %prec LAMBDA - '|' '&' maybe_mut ':' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $5, $7); } -| %prec LAMBDA - '|' ':' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $3, $5); } - + inferrable_params OROR lambda_expr_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $1, mk_none(), $3); } ; -proc_expr +lambda_expr_nostruct : %prec LAMBDA - PROC '(' ')' expr { $$ = mk_node("ExprProc", 2, mk_none(), $4); } + OROR expr_nostruct { $$ = mk_node("ExprFnBlock", 2, mk_none(), $2); } +| %prec LAMBDA + '|' '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, mk_none(), $3, $4); } | %prec LAMBDA - PROC '(' inferrable_params ')' expr { $$ = mk_node("ExprProc", 2, $3, $5); } + '|' inferrable_params '|' expr_nostruct { $$ = mk_node("ExprFnBlock", 2, $2, $4); } +| %prec LAMBDA + '|' inferrable_params OROR lambda_expr_nostruct_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $2, mk_none(), $4); } ; -proc_expr_nostruct +lambda_expr_nostruct_no_first_bar : %prec LAMBDA - PROC '(' ')' expr_nostruct { $$ = mk_node("ExprProc", 2, mk_none(), $4); } + '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, mk_none(), $2, $3); } +| %prec LAMBDA + inferrable_params '|' ret_ty expr_nostruct { $$ = mk_node("ExprFnBlock", 3, $1, $3, $4); } | %prec LAMBDA - PROC '(' inferrable_params ')' expr_nostruct { $$ = mk_node("ExprProc", 2, $3, $5); } + inferrable_params OROR lambda_expr_nostruct_no_first_bar { $$ = mk_node("ExprFnBlock", 3, $1, mk_none(), $3); } ; vec_expr @@ -1654,6 +1671,7 @@ struct_expr_fields : field_inits | field_inits ',' | maybe_field_inits default_field_init { $$ = ext_node($1, 1, $2); } +| %empty { $$ = mk_none(); } ; maybe_field_inits @@ -1668,7 +1686,9 @@ field_inits ; field_init -: ident ':' expr { $$ = mk_node("FieldInit", 2, $1, $3); } +: ident { $$ = mk_node("FieldInit", 1, $1); } +| ident ':' expr { $$ = mk_node("FieldInit", 2, $1, $3); } +| LIT_INTEGER ':' expr { $$ = mk_node("FieldInit", 2, mk_atom(yytext), $3); } ; default_field_init @@ -1689,10 +1709,18 @@ block_expr full_block_expr : block_expr -| full_block_expr '.' path_generic_args_with_colons %prec IDENT { $$ = mk_node("ExprField", 2, $1, $3); } -| full_block_expr '.' path_generic_args_with_colons '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 3, $1, $3, $5); } -| full_block_expr '.' path_generic_args_with_colons '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 3, $1, $3, $5); } -| full_block_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| block_expr_dot +; + +block_expr_dot +: block_expr '.' path_generic_args_with_colons %prec IDENT { $$ = mk_node("ExprField", 2, $1, $3); } +| block_expr_dot '.' path_generic_args_with_colons %prec IDENT { $$ = mk_node("ExprField", 2, $1, $3); } +| block_expr '.' path_generic_args_with_colons '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 3, $1, $3, $5); } +| block_expr_dot '.' path_generic_args_with_colons '[' maybe_expr ']' { $$ = mk_node("ExprIndex", 3, $1, $3, $5); } +| block_expr '.' path_generic_args_with_colons '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 3, $1, $3, $5); } +| block_expr_dot '.' path_generic_args_with_colons '(' maybe_exprs ')' { $$ = mk_node("ExprCall", 3, $1, $3, $5); } +| block_expr '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } +| block_expr_dot '.' LIT_INTEGER { $$ = mk_node("ExprTupleIndex", 1, $1); } ; expr_match @@ -1714,12 +1742,13 @@ match_clause ; nonblock_match_clause -: maybe_outer_attrs pats_or maybe_guard FAT_ARROW nonblock_expr { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } -| maybe_outer_attrs pats_or maybe_guard FAT_ARROW full_block_expr { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } +: maybe_outer_attrs pats_or maybe_guard FAT_ARROW nonblock_expr { $$ = mk_node("ArmNonblock", 4, $1, $2, $3, $5); } +| maybe_outer_attrs pats_or maybe_guard FAT_ARROW block_expr_dot { $$ = mk_node("ArmNonblock", 4, $1, $2, $3, $5); } ; block_match_clause -: maybe_outer_attrs pats_or maybe_guard FAT_ARROW block { $$ = mk_node("Arm", 4, $1, $2, $3, $5); } +: maybe_outer_attrs pats_or maybe_guard FAT_ARROW block { $$ = mk_node("ArmBlock", 4, $1, $2, $3, $5); } +| maybe_outer_attrs pats_or maybe_guard FAT_ARROW block_expr { $$ = mk_node("ArmBlock", 4, $1, $2, $3, $5); } ; maybe_guard @@ -1796,6 +1825,10 @@ maybe_ident ident : IDENT { $$ = mk_node("ident", 1, mk_atom(yytext)); } +// Weak keywords that can be used as identifiers +| CATCH { $$ = mk_node("ident", 1, mk_atom(yytext)); } +| DEFAULT { $$ = mk_node("ident", 1, mk_atom(yytext)); } +| UNION { $$ = mk_node("ident", 1, mk_atom(yytext)); } ; unpaired_token @@ -1836,13 +1869,20 @@ unpaired_token | LIFETIME { $$ = mk_atom(yytext); } | SELF { $$ = mk_atom(yytext); } | STATIC { $$ = mk_atom(yytext); } +| ABSTRACT { $$ = mk_atom(yytext); } +| ALIGNOF { $$ = mk_atom(yytext); } | AS { $$ = mk_atom(yytext); } +| BECOME { $$ = mk_atom(yytext); } | BREAK { $$ = mk_atom(yytext); } +| CATCH { $$ = mk_atom(yytext); } | CRATE { $$ = mk_atom(yytext); } +| DEFAULT { $$ = mk_atom(yytext); } +| DO { $$ = mk_atom(yytext); } | ELSE { $$ = mk_atom(yytext); } | ENUM { $$ = mk_atom(yytext); } | EXTERN { $$ = mk_atom(yytext); } | FALSE { $$ = mk_atom(yytext); } +| FINAL { $$ = mk_atom(yytext); } | FN { $$ = mk_atom(yytext); } | FOR { $$ = mk_atom(yytext); } | IF { $$ = mk_atom(yytext); } @@ -1850,21 +1890,31 @@ unpaired_token | IN { $$ = mk_atom(yytext); } | LET { $$ = mk_atom(yytext); } | LOOP { $$ = mk_atom(yytext); } +| MACRO { $$ = mk_atom(yytext); } | MATCH { $$ = mk_atom(yytext); } | MOD { $$ = mk_atom(yytext); } | MOVE { $$ = mk_atom(yytext); } | MUT { $$ = mk_atom(yytext); } +| OFFSETOF { $$ = mk_atom(yytext); } +| OVERRIDE { $$ = mk_atom(yytext); } | PRIV { $$ = mk_atom(yytext); } | PUB { $$ = mk_atom(yytext); } +| PURE { $$ = mk_atom(yytext); } | REF { $$ = mk_atom(yytext); } | RETURN { $$ = mk_atom(yytext); } | STRUCT { $$ = mk_atom(yytext); } +| SIZEOF { $$ = mk_atom(yytext); } +| SUPER { $$ = mk_atom(yytext); } | TRUE { $$ = mk_atom(yytext); } | TRAIT { $$ = mk_atom(yytext); } | TYPE { $$ = mk_atom(yytext); } +| UNION { $$ = mk_atom(yytext); } | UNSAFE { $$ = mk_atom(yytext); } +| UNSIZED { $$ = mk_atom(yytext); } | USE { $$ = mk_atom(yytext); } +| VIRTUAL { $$ = mk_atom(yytext); } | WHILE { $$ = mk_atom(yytext); } +| YIELD { $$ = mk_atom(yytext); } | CONTINUE { $$ = mk_atom(yytext); } | PROC { $$ = mk_atom(yytext); } | BOX { $$ = mk_atom(yytext); } @@ -1942,4 +1992,4 @@ brackets_delimited_token_trees $2, mk_node("TTTok", 1, mk_atom("]"))); } -; \ No newline at end of file +; diff --git a/src/grammar/tokens.h b/src/grammar/tokens.h index 081bd05025967..15ea738ed0057 100644 --- a/src/grammar/tokens.h +++ b/src/grammar/tokens.h @@ -30,6 +30,7 @@ enum Token { DOTDOT, DOTDOTDOT, MOD_SEP, + LARROW, RARROW, FAT_ARROW, LIT_BYTE, @@ -47,13 +48,20 @@ enum Token { // keywords SELF, STATIC, + ABSTRACT, + ALIGNOF, AS, + BECOME, BREAK, + CATCH, CRATE, + DEFAULT, + DO, ELSE, ENUM, EXTERN, FALSE, + FINAL, FN, FOR, IF, @@ -61,21 +69,31 @@ enum Token { IN, LET, LOOP, + MACRO, MATCH, MOD, MOVE, MUT, + OFFSETOF, + OVERRIDE, PRIV, PUB, + PURE, REF, RETURN, + SIZEOF, STRUCT, + SUPER, + UNION, TRUE, TRAIT, TYPE, UNSAFE, + UNSIZED, USE, + VIRTUAL, WHILE, + YIELD, CONTINUE, PROC, BOX, diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 686e5681d12b4..0a265ee1376a0 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -11,6 +11,9 @@ path = "lib.rs" core = { path = "../libcore" } std_unicode = { path = "../libstd_unicode" } +[dev-dependencies] +rand = "0.3" + [[test]] name = "collectionstests" path = "../liballoc/tests/lib.rs" diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index 5a9cd82b9d119..3a2022ad429f7 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -70,7 +70,7 @@ impl Layout { /// /// * `align` must be a power of two, /// - /// * `align` must not exceed 2^31 (i.e. `1 << 31`), + /// * `align` must not exceed 231 (i.e. `1 << 31`), /// /// * `size`, when rounded up to the nearest multiple of `align`, /// must not overflow (i.e. the rounded value must be less than @@ -113,7 +113,7 @@ impl Layout { /// # Safety /// /// This function is unsafe as it does not verify that `align` is - /// a power-of-two that is also less than or equal to 2^31, nor + /// a power-of-two that is also less than or equal to 231, nor /// that `size` aligned to `align` fits within the address space /// (i.e. the `Layout::from_size_align` preconditions). #[inline] @@ -227,7 +227,7 @@ impl Layout { }; // We can assume that `self.align` is a power-of-two that does - // not exceed 2^31. Furthermore, `alloc_size` has already been + // not exceed 231. Furthermore, `alloc_size` has already been // rounded up to a multiple of `self.align`; therefore, the // call to `Layout::from_size_align` below should never panic. Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 3b7dbd813cf05..fc0a3c0fd881a 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -52,8 +52,10 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize; /// also destroyed. /// /// Shared references in Rust disallow mutation by default, and `Arc` is no -/// exception. If you need to mutate through an `Arc`, use [`Mutex`][mutex], -/// [`RwLock`][rwlock], or one of the [`Atomic`][atomic] types. +/// exception: you cannot generally obtain a mutable reference to something +/// inside an `Arc`. If you need to mutate through an `Arc`, use +/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] +/// types. /// /// ## Thread Safety /// @@ -1326,7 +1328,7 @@ impl fmt::Debug for Arc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Pointer for Arc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr, f) + fmt::Pointer::fmt(&(&**self as *const T), f) } } diff --git a/src/liballoc/benches/str.rs b/src/liballoc/benches/str.rs index fc4063fae9277..38c94d4d8b5f3 100644 --- a/src/liballoc/benches/str.rs +++ b/src/liballoc/benches/str.rs @@ -272,15 +272,12 @@ make_test!(match_indices_a_str, s, s.match_indices("a").count()); make_test!(split_a_str, s, s.split("a").count()); make_test!(trim_ascii_char, s, { - use std::ascii::AsciiExt; s.trim_matches(|c: char| c.is_ascii()) }); make_test!(trim_left_ascii_char, s, { - use std::ascii::AsciiExt; s.trim_left_matches(|c: char| c.is_ascii()) }); make_test!(trim_right_ascii_char, s, { - use std::ascii::AsciiExt; s.trim_right_matches(|c: char| c.is_ascii()) }); diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index 57640af816a57..94bbaf92ce9b0 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -926,7 +926,7 @@ impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { } } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Clone for Iter<'a, T> { fn clone(&self) -> Iter<'a, T> { diff --git a/src/liballoc/borrow.rs b/src/liballoc/borrow.rs index a662e4b1f4f93..acae0daa86b6b 100644 --- a/src/liballoc/borrow.rs +++ b/src/liballoc/borrow.rs @@ -191,7 +191,6 @@ impl<'a, B: ?Sized> Cow<'a, B> /// # Examples /// /// ``` - /// use std::ascii::AsciiExt; /// use std::borrow::Cow; /// /// let mut cow = Cow::Borrowed("foo"); @@ -233,7 +232,7 @@ impl<'a, B: ?Sized> Cow<'a, B> /// /// assert_eq!( /// cow.into_owned(), - /// Cow::Owned(String::from(s)) + /// String::from(s) /// ); /// ``` /// @@ -247,7 +246,7 @@ impl<'a, B: ?Sized> Cow<'a, B> /// /// assert_eq!( /// cow.into_owned(), - /// Cow::Owned(String::from(s)) + /// String::from(s) /// ); /// ``` #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 4341b0b2975be..6f125cdba8190 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -151,7 +151,7 @@ impl Place for IntermediateBox { unsafe fn finalize(b: IntermediateBox) -> Box { let p = b.ptr as *mut T; mem::forget(b); - mem::transmute(p) + Box::from_raw(p) } fn make_place() -> IntermediateBox { @@ -269,7 +269,38 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub unsafe fn from_raw(raw: *mut T) -> Self { - mem::transmute(raw) + Box::from_unique(Unique::new_unchecked(raw)) + } + + /// Constructs a `Box` from a `Unique` pointer. + /// + /// After calling this function, the memory is owned by a `Box` and `T` can + /// then be destroyed and released upon drop. + /// + /// # Safety + /// + /// A `Unique` can be safely created via [`Unique::new`] and thus doesn't + /// necessarily own the data pointed to nor is the data guaranteed to live + /// as long as the pointer. + /// + /// [`Unique::new`]: ../../core/ptr/struct.Unique.html#method.new + /// + /// # Examples + /// + /// ``` + /// #![feature(unique)] + /// + /// fn main() { + /// let x = Box::new(5); + /// let ptr = Box::into_unique(x); + /// let x = unsafe { Box::from_unique(ptr) }; + /// } + /// ``` + #[unstable(feature = "unique", reason = "needs an RFC to flesh out design", + issue = "27730")] + #[inline] + pub unsafe fn from_unique(u: Unique) -> Self { + Box(u) } /// Consumes the `Box`, returning the wrapped raw pointer. @@ -295,7 +326,7 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Box) -> *mut T { - unsafe { mem::transmute(b) } + Box::into_unique(b).as_ptr() } /// Consumes the `Box`, returning the wrapped pointer as `Unique`. @@ -303,13 +334,18 @@ impl Box { /// After calling this function, the caller is responsible for the /// memory previously managed by the `Box`. In particular, the /// caller should properly destroy `T` and release the memory. The - /// proper way to do so is to convert the raw pointer back into a - /// `Box` with the [`Box::from_raw`] function. + /// proper way to do so is to either convert the `Unique` pointer: + /// + /// - Into a `Box` with the [`Box::from_unique`] function. + /// + /// - Into a raw pointer and back into a `Box` with the [`Box::from_raw`] + /// function. /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_unique(b)` instead of `b.into_unique()`. This /// is so that there is no conflict with a method on the inner type. /// + /// [`Box::from_unique`]: struct.Box.html#method.from_unique /// [`Box::from_raw`]: struct.Box.html#method.from_raw /// /// # Examples @@ -326,7 +362,62 @@ impl Box { issue = "27730")] #[inline] pub fn into_unique(b: Box) -> Unique { - unsafe { mem::transmute(b) } + let unique = b.0; + mem::forget(b); + unique + } + + /// Consumes and leaks the `Box`, returning a mutable reference, + /// `&'a mut T`. Here, the lifetime `'a` may be chosen to be `'static`. + /// + /// This function is mainly useful for data that lives for the remainder of + /// the program's life. Dropping the returned reference will cause a memory + /// leak. If this is not acceptable, the reference should first be wrapped + /// with the [`Box::from_raw`] function producing a `Box`. This `Box` can + /// then be dropped which will properly destroy `T` and release the + /// allocated memory. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::leak(b)` instead of `b.leak()`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// [`Box::from_raw`]: struct.Box.html#method.from_raw + /// + /// # Examples + /// + /// Simple usage: + /// + /// ``` + /// #![feature(box_leak)] + /// + /// fn main() { + /// let x = Box::new(41); + /// let static_ref: &'static mut usize = Box::leak(x); + /// *static_ref += 1; + /// assert_eq!(*static_ref, 42); + /// } + /// ``` + /// + /// Unsized data: + /// + /// ``` + /// #![feature(box_leak)] + /// + /// fn main() { + /// let x = vec![1, 2, 3].into_boxed_slice(); + /// let static_ref = Box::leak(x); + /// static_ref[0] = 4; + /// assert_eq!(*static_ref, [4, 2, 3]); + /// } + /// ``` + #[unstable(feature = "box_leak", reason = "needs an FCP to stabilize", + issue = "46179")] + #[inline] + pub fn leak<'a>(b: Box) -> &'a mut T + where + T: 'a // Technically not needed, but kept to be explicit. + { + unsafe { &mut *Box::into_raw(b) } } } @@ -528,9 +619,7 @@ impl<'a> From<&'a str> for Box { #[stable(feature = "boxed_str_conv", since = "1.19.0")] impl From> for Box<[u8]> { fn from(s: Box) -> Self { - unsafe { - mem::transmute(s) - } + unsafe { Box::from_raw(Box::into_raw(s) as *mut [u8]) } } } @@ -593,7 +682,7 @@ impl Box { pub fn downcast(self) -> Result, Box> { >::downcast(self).map_err(|s| unsafe { // reapply the Send marker - mem::transmute::, Box>(s) + Box::from_raw(Box::into_raw(s) as *mut (Any + Send)) }) } } diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 4c93fead17237..b114dc640fbaf 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2102,6 +2102,40 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { Vacant(ref entry) => entry.key(), } } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(entry_and_modify)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[unstable(feature = "entry_and_modify", issue = "44733")] + pub fn and_modify(self, mut f: F) -> Self + where F: FnMut(&mut V) + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + }, + Vacant(entry) => Vacant(entry), + } + } } impl<'a, K: Ord, V: Default> Entry<'a, K, V> { diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 578d90c5ba9bb..a092bfb3b0a8a 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Utilities for formatting and printing `String`s +//! Utilities for formatting and printing `String`s. //! //! This module contains the runtime support for the [`format!`] syntax extension. //! This macro is implemented in the compiler to emit calls to this module in @@ -236,6 +236,8 @@ //! writeln! // same as write but appends a newline //! print! // the format string is printed to the standard output //! println! // same as print but appends a newline +//! eprint! // the format string is printed to the standard error +//! eprintln! // same as eprint but appends a newline //! format_args! // described below. //! ``` //! @@ -264,6 +266,11 @@ //! print!("Hello {}!", "world"); //! println!("I have a newline {}", "character at the end"); //! ``` +//! ### `eprint!` +//! +//! The [`eprint!`] and [`eprintln!`] macros are identical to +//! [`print!`] and [`println!`], respectively, except they emit their +//! output to stderr. //! //! ### `format_args!` //! @@ -475,7 +482,6 @@ //! them with the same character. For example, the `{` character is escaped with //! `{{` and the `}` character is escaped with `}}`. //! -//! [`format!`]: ../../macro.format.html //! [`usize`]: ../../std/primitive.usize.html //! [`isize`]: ../../std/primitive.isize.html //! [`i8`]: ../../std/primitive.i8.html @@ -491,7 +497,10 @@ //! [`writeln!`]: ../../std/macro.writeln.html //! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt //! [`std::io::Write`]: ../../std/io/trait.Write.html +//! [`print!`]: ../../std/macro.print.html //! [`println!`]: ../../std/macro.println.html +//! [`eprint!`]: ../../std/macro.eprint.html +//! [`eprintln!`]: ../../std/macro.eprintln.html //! [`write!`]: ../../std/macro.write.html //! [`format_args!`]: ../../std/macro.format_args.html //! [`fmt::Arguments`]: struct.Arguments.html @@ -537,7 +546,7 @@ use string; /// assert_eq!(s, "Hello, world!"); /// ``` /// -/// Please note that using [`format!`] might be preferrable. +/// Please note that using [`format!`] might be preferable. /// Example: /// /// ``` diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d51aaa23c6a53..3cc3ea467966b 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -83,6 +83,7 @@ #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] #![feature(allow_internal_unstable)] +#![feature(ascii_ctype)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] @@ -93,6 +94,7 @@ #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] +#![feature(from_ref)] #![feature(fundamental)] #![feature(fused)] #![feature(generic_param_attrs)] @@ -121,6 +123,7 @@ #![feature(unique)] #![feature(unsize)] #![feature(allocator_internals)] +#![feature(on_unimplemented)] #![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))] #![cfg_attr(test, feature(test, box_heap))] @@ -132,6 +135,8 @@ extern crate std; #[cfg(test)] extern crate test; +#[cfg(test)] +extern crate rand; extern crate std_unicode; diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index f9512cbe977a4..0fe3c9724224d 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -80,7 +80,7 @@ impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { } } -// FIXME #19839: deriving is too aggressive on the bounds (T doesn't need to be Clone). +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Clone for Iter<'a, T> { fn clone(&self) -> Self { @@ -220,6 +220,28 @@ impl LinkedList { node }) } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + #[inline] + unsafe fn unlink_node(&mut self, mut node: Shared>) { + let node = node.as_mut(); + + match node.prev { + Some(mut prev) => prev.as_mut().next = node.next.clone(), + // this node is the head node + None => self.head = node.next.clone(), + }; + + match node.next { + Some(mut next) => next.as_mut().prev = node.prev.clone(), + // this node is the tail node + None => self.tail = node.prev.clone(), + }; + + self.len -= 1; + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -722,6 +744,49 @@ impl LinkedList { second_part } + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, it will try again, and call the closure on the next element, + /// seeing if it passes the test. + /// + /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(drain_filter)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] + pub fn drain_filter(&mut self, filter: F) -> DrainFilter + where F: FnMut(&mut T) -> bool + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + DrainFilter { + list: self, + it: it, + pred: filter, + idx: 0, + old_len: old_len, + } + } + /// Returns a place for insertion at the front of the list. /// /// Using this method with placement syntax is equivalent to @@ -967,6 +1032,56 @@ impl<'a, T> IterMut<'a, T> { } } +/// An iterator produced by calling `drain_filter` on LinkedList. +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +pub struct DrainFilter<'a, T: 'a, F: 'a> + where F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl<'a, T, F> Iterator for DrainFilter<'a, T, F> + where F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + self.list.unlink_node(node); + return Some(Box::from_raw(node.as_ptr()).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for DrainFilter<'a, T, F> + where F: FnMut(&mut T) -> bool +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("DrainFilter") + .field(&self.list) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for IntoIter { type Item = T; @@ -1269,10 +1384,11 @@ unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} #[cfg(test)] mod tests { - use std::__rand::{thread_rng, Rng}; use std::thread; use std::vec::Vec; + use rand::{thread_rng, Rng}; + use super::{LinkedList, Node}; #[cfg(test)] @@ -1287,6 +1403,8 @@ mod tests { let mut node_ptr: &Node; match list.head { None => { + // tail node should also be None. + assert!(list.tail.is_none()); assert_eq!(0, list.len); return; } @@ -1313,6 +1431,11 @@ mod tests { } } } + + // verify that the tail node points to the last node. + let tail = list.tail.as_ref().expect("some tail node").as_ref(); + assert_eq!(tail as *const Node, node_ptr as *const Node); + // check that len matches interior links. assert_eq!(len, list.len); } } @@ -1501,4 +1624,28 @@ mod tests { } assert_eq!(i, v.len()); } + + #[test] + fn drain_filter_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|v| *v < 4).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3]); + assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); + } + + #[test] + fn drain_to_empty_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|_| true).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3, 4, 5, 6]); + assert_eq!(m.into_iter().collect::>(), &[]); + } } diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index c2a3019515f10..472eef77d7956 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -72,7 +72,7 @@ macro_rules! vec { /// Creates a `String` using interpolation of runtime expressions. /// -/// The first argument `format!` recieves is a format string. This must be a string +/// The first argument `format!` receives is a format string. This must be a string /// literal. The power of the formatting string is in the `{}`s contained. /// /// Additional parameters passed to `format!` replace the `{}`s within the diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 841f9dc64142e..dbf1fb1367dda 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -114,7 +114,7 @@ impl RawVec { impl RawVec { /// Creates the biggest possible RawVec (on the system heap) /// without allocating. If T has positive size, then this makes a - /// RawVec with capacity 0. If T has 0 size, then it it makes a + /// RawVec with capacity 0. If T has 0 size, then it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. pub fn new() -> Self { diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 553980d463fc5..58f08fd8bc11d 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -19,7 +19,7 @@ //! given value is destroyed, the pointed-to value is also destroyed. //! //! Shared references in Rust disallow mutation by default, and [`Rc`] -//! is no exception: you cannot obtain a mutable reference to +//! is no exception: you cannot generally obtain a mutable reference to //! something inside an [`Rc`]. If you need mutability, put a [`Cell`] //! or [`RefCell`] inside the [`Rc`]; see [an example of mutability //! inside an Rc][mutability]. @@ -346,7 +346,7 @@ impl Rc { unsafe { let val = ptr::read(&*this); // copy the contained object - // Indicate to Weaks that they can't be promoted by decrememting + // Indicate to Weaks that they can't be promoted by decrementing // the strong count, and then remove the implicit "strong weak" // pointer while also handling drop logic by just crafting a // fake Weak. @@ -1072,7 +1072,7 @@ impl fmt::Debug for Rc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Pointer for Rc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr, f) + fmt::Pointer::fmt(&(&**self as *const T), f) } } diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 2045d5ddd972d..ac815629dcfb3 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -119,6 +119,8 @@ pub use core::slice::{SplitN, RSplitN, SplitNMut, RSplitNMut}; pub use core::slice::{RSplit, RSplitMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{from_raw_parts, from_raw_parts_mut}; +#[unstable(feature = "from_ref", issue = "45703")] +pub use core::slice::{from_ref, from_ref_mut}; #[unstable(feature = "slice_get_slice", issue = "35729")] pub use core::slice::SliceIndex; @@ -1426,15 +1428,45 @@ impl [T] { /// /// # Examples /// + /// Cloning two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// dst.clone_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); /// ``` - /// let mut dst = [0, 0, 0]; - /// let src = [1, 2, 3]; /// - /// dst.clone_from_slice(&src); - /// assert!(dst == [1, 2, 3]); + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `clone_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].clone_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.clone_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); /// ``` /// /// [`copy_from_slice`]: #method.copy_from_slice + /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { core_slice::SliceExt::clone_from_slice(self, src) @@ -1452,23 +1484,53 @@ impl [T] { /// /// # Examples /// + /// Copying two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// dst.copy_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); /// ``` - /// let mut dst = [0, 0, 0]; - /// let src = [1, 2, 3]; /// - /// dst.copy_from_slice(&src); - /// assert_eq!(src, dst); + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `copy_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].copy_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.copy_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); /// ``` /// /// [`clone_from_slice`]: #method.clone_from_slice + /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "copy_from_slice", since = "1.9.0")] pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { core_slice::SliceExt::copy_from_slice(self, src) } - /// Swaps all elements in `self` with those in `src`. + /// Swaps all elements in `self` with those in `other`. /// - /// The length of `src` must be the same as `self`. + /// The length of `other` must be the same as `self`. /// /// # Panics /// @@ -1476,19 +1538,52 @@ impl [T] { /// /// # Example /// + /// Swapping two elements across slices: + /// + /// ``` + /// #![feature(swap_with_slice)] + /// + /// let mut slice1 = [0, 0]; + /// let mut slice2 = [1, 2, 3, 4]; + /// + /// slice1.swap_with_slice(&mut slice2[2..]); + /// + /// assert_eq!(slice1, [3, 4]); + /// assert_eq!(slice2, [1, 2, 0, 0]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference to a + /// particular piece of data in a particular scope. Because of this, + /// attempting to use `swap_with_slice` on a single slice will result in + /// a compile failure: + /// + /// ```compile_fail + /// #![feature(swap_with_slice)] + /// + /// let mut slice = [1, 2, 3, 4, 5]; + /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// mutable sub-slices from a slice: + /// /// ``` /// #![feature(swap_with_slice)] /// - /// let mut src = [1, 2, 3]; - /// let mut dst = [7, 8, 9]; + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.swap_with_slice(&mut right[1..]); + /// } /// - /// src.swap_with_slice(&mut dst); - /// assert_eq!(src, [7, 8, 9]); - /// assert_eq!(dst, [1, 2, 3]); + /// assert_eq!(slice, [4, 5, 3, 1, 2]); /// ``` + /// + /// [`split_at_mut`]: #method.split_at_mut #[unstable(feature = "swap_with_slice", issue = "44030")] - pub fn swap_with_slice(&mut self, src: &mut [T]) { - core_slice::SliceExt::swap_with_slice(self, src) + pub fn swap_with_slice(&mut self, other: &mut [T]) { + core_slice::SliceExt::swap_with_slice(self, other) } /// Copies `self` into a new `Vec`. @@ -1531,6 +1626,98 @@ impl [T] { } } +#[lang = "slice_u8"] +#[cfg(not(test))] +impl [u8] { + /// Checks if all bytes in this slice are within the ASCII range. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + self.iter().all(|b| b.is_ascii()) + } + + /// Returns a vector containing a copy of this slice where each byte + /// is mapped to its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> Vec { + let mut me = self.to_vec(); + me.make_ascii_uppercase(); + me + } + + /// Returns a vector containing a copy of this slice where each byte + /// is mapped to its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> Vec { + let mut me = self.to_vec(); + me.make_ascii_lowercase(); + me + } + + /// Checks that two slices are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { + self.len() == other.len() && + self.iter().zip(other).all(|(a, b)| { + a.eq_ignore_ascii_case(b) + }) + } + + /// Converts this slice to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + for byte in self { + byte.make_ascii_uppercase(); + } + } + + /// Converts this slice to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + for byte in self { + byte.make_ascii_lowercase(); + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Extension traits for slices over specific kinds of data //////////////////////////////////////////////////////////////////////////////// diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 62b5f13675c23..975ea4e1a3e01 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -363,16 +363,16 @@ impl str { /// # Examples /// /// ``` - /// let mut v = String::from("🗻∈🌏"); + /// let v = String::from("🗻∈🌏"); /// /// assert_eq!(Some("🗻"), v.get(0..4)); /// /// // indices not on UTF-8 sequence boundaries - /// assert!(v.get_mut(1..).is_none()); - /// assert!(v.get_mut(..8).is_none()); + /// assert!(v.get(1..).is_none()); + /// assert!(v.get(..8).is_none()); /// /// // out of bounds - /// assert!(v.get_mut(..42).is_none()); + /// assert!(v.get(..42).is_none()); /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] @@ -390,8 +390,6 @@ impl str { /// # Examples /// /// ``` - /// use std::ascii::AsciiExt; - /// /// let mut v = String::from("hello"); /// // correct length /// assert!(v.get_mut(0..5).is_some()); @@ -617,8 +615,6 @@ impl str { /// Basic usage: /// /// ``` - /// use std::ascii::AsciiExt; - /// /// let mut s = "Per Martin-Löf".to_string(); /// { /// let (first, last) = s.split_at_mut(3); @@ -959,13 +955,15 @@ impl str { /// assert_eq!(s.find("Léopard"), Some(13)); /// ``` /// - /// More complex patterns with closures: + /// More complex patterns using point-free style and closures: /// /// ``` /// let s = "Löwe 老虎 Léopard"; /// /// assert_eq!(s.find(char::is_whitespace), Some(5)); /// assert_eq!(s.find(char::is_lowercase), Some(1)); + /// assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1)); + /// assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4)); /// ``` /// /// Not finding the pattern: @@ -1736,7 +1734,7 @@ impl str { /// A more complex pattern, using a closure: /// /// ``` - /// assert_eq!("1fooX".trim_left_matches(|c| c == '1' || c == 'X'), "fooX"); + /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str @@ -2047,10 +2045,8 @@ impl str { /// ``` #[stable(feature = "box_str", since = "1.4.0")] pub fn into_string(self: Box) -> String { - unsafe { - let slice = mem::transmute::, Box<[u8]>>(self); - String::from_utf8_unchecked(slice.into_vec()) - } + let slice = Box::<[u8]>::from(self); + unsafe { String::from_utf8_unchecked(slice.into_vec()) } } /// Create a [`String`] by repeating a string `n` times. @@ -2070,6 +2066,134 @@ impl str { s.extend((0..n).map(|_| self)); s } + + /// Checks if all characters in this string are within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = "hello!\n"; + /// let non_ascii = "Grüße, Jürgen ❤"; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + // We can treat each byte as character here: all multibyte characters + // start with a byte that is not in the ascii range, so we will stop + // there already. + self.bytes().all(|b| b.is_ascii()) + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// To uppercase ASCII characters in addition to non-ASCII characters, use + /// [`to_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let s = "Grüße, Jürgen ❤"; + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); + /// ``` + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + /// [`to_uppercase`]: #method.to_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> String { + let mut bytes = self.as_bytes().to_vec(); + bytes.make_ascii_uppercase(); + // make_ascii_uppercase() preserves the UTF-8 invariant. + unsafe { String::from_utf8_unchecked(bytes) } + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// To lowercase ASCII characters in addition to non-ASCII characters, use + /// [`to_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let s = "Grüße, Jürgen ❤"; + /// + /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); + /// ``` + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + /// [`to_lowercase`]: #method.to_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> String { + let mut bytes = self.as_bytes().to_vec(); + bytes.make_ascii_lowercase(); + // make_ascii_lowercase() preserves the UTF-8 invariant. + unsafe { String::from_utf8_unchecked(bytes) } + } + + /// Checks that two strings are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Examples + /// + /// ``` + /// assert!("Ferris".eq_ignore_ascii_case("FERRIS")); + /// assert!("Ferrös".eq_ignore_ascii_case("FERRöS")); + /// assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS")); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &str) -> bool { + self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) + } + + /// Converts this string to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + pub fn make_ascii_uppercase(&mut self) { + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_uppercase() + } + + /// Converts this string to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + pub fn make_ascii_lowercase(&mut self) { + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_lowercase() + } } /// Converts a boxed slice of bytes to a boxed string slice without checking @@ -2087,5 +2211,5 @@ impl str { /// ``` #[stable(feature = "str_box_extras", since = "1.20.0")] pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { - mem::transmute(v) + Box::from_raw(Box::into_raw(v) as *mut str) } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 6d0bb264df186..cd0f4a22e9cfa 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -596,7 +596,7 @@ impl String { /// Decode a UTF-16 encoded vector `v` into a `String`, returning [`Err`] /// if `v` contains any invalid data. /// - /// [`Err`]: ../../std/result/enum.Result.htlm#variant.Err + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err /// /// # Examples /// @@ -773,8 +773,6 @@ impl String { /// Basic usage: /// /// ``` - /// use std::ascii::AsciiExt; - /// /// let mut s = String::from("foobar"); /// let s_mut_str = s.as_mut_str(); /// diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs new file mode 100644 index 0000000000000..d3ce12056bb49 --- /dev/null +++ b/src/liballoc/tests/heap.rs @@ -0,0 +1,45 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc_system::System; +use std::heap::{Heap, Alloc, Layout}; + +/// https://github.com/rust-lang/rust/issues/45955 +/// +/// Note that `#[global_allocator]` is not used, +/// so `liballoc_jemalloc` is linked (on some platforms). +#[test] +fn alloc_system_overaligned_request() { + check_overalign_requests(System) +} + +#[test] +fn std_heap_overaligned_request() { + check_overalign_requests(Heap) +} + +fn check_overalign_requests(mut allocator: T) { + let size = 8; + let align = 16; // greater than size + let iterations = 100; + unsafe { + let pointers: Vec<_> = (0..iterations).map(|_| { + allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap() + }).collect(); + for &ptr in &pointers { + assert_eq!((ptr as usize) % align, 0, "Got a pointer less aligned than requested") + } + + // Clean up + for &ptr in &pointers { + allocator.dealloc(ptr, Layout::from_size_align(size, align).unwrap()) + } + } +} diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index c5beb63d12e9d..f1e95883b3827 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -10,6 +10,8 @@ #![deny(warnings)] +#![feature(allocator_api)] +#![feature(alloc_system)] #![feature(attr_literals)] #![feature(box_syntax)] #![feature(inclusive_range_syntax)] @@ -29,7 +31,9 @@ #![feature(unboxed_closures)] #![feature(unicode)] +extern crate alloc_system; extern crate std_unicode; +extern crate rand; use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; @@ -38,6 +42,7 @@ mod binary_heap; mod btree; mod cow_str; mod fmt; +mod heap; mod linked_list; mod slice; mod str; diff --git a/src/liballoc/tests/linked_list.rs b/src/liballoc/tests/linked_list.rs index a59724a017b12..4e3e855105eb8 100644 --- a/src/liballoc/tests/linked_list.rs +++ b/src/liballoc/tests/linked_list.rs @@ -366,3 +366,191 @@ fn test_contains() { assert!(!l.contains(&3)); } + +#[test] +fn drain_filter_empty() { + let mut list: LinkedList = LinkedList::new(); + + { + let mut iter = list.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(list.len(), 0); + assert_eq!(list.into_iter().collect::>(), vec![]); +} + +#[test] +fn drain_filter_zst() { + let mut list: LinkedList<_> = vec![(), (), (), (), ()].into_iter().collect(); + let initial_len = list.len(); + let mut count = 0; + + { + let mut iter = list.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + while let Some(_) = iter.next() { + count += 1; + assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, initial_len); + assert_eq!(list.len(), 0); + assert_eq!(list.into_iter().collect::>(), vec![]); +} + +#[test] +fn drain_filter_false() { + let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + let initial_len = list.len(); + let mut count = 0; + + { + let mut iter = list.drain_filter(|_| false); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + for _ in iter.by_ref() { + count += 1; + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, 0); + assert_eq!(list.len(), initial_len); + assert_eq!(list.into_iter().collect::>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fn drain_filter_true() { + let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + let initial_len = list.len(); + let mut count = 0; + + { + let mut iter = list.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + while let Some(_) = iter.next() { + count += 1; + assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, initial_len); + assert_eq!(list.len(), 0); + assert_eq!(list.into_iter().collect::>(), vec![]); +} + +#[test] +fn drain_filter_complex() { + + { // [+xxx++++++xxxxx++++x+x++] + let mut list = vec![ + 1, + 2, 4, 6, + 7, 9, 11, 13, 15, 17, + 18, 20, 22, 24, 26, + 27, 29, 31, 33, + 34, + 35, + 36, + 37, 39 + ].into_iter().collect::>(); + + let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(list.len(), 14); + assert_eq!( + list.into_iter().collect::>(), + vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); + } + + { // [xxx++++++xxxxx++++x+x++] + let mut list = vec![ + 2, 4, 6, + 7, 9, 11, 13, 15, 17, + 18, 20, 22, 24, 26, + 27, 29, 31, 33, + 34, + 35, + 36, + 37, 39 + ].into_iter().collect::>(); + + let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(list.len(), 13); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); + } + + { // [xxx++++++xxxxx++++x+x] + let mut list = vec![ + 2, 4, 6, + 7, 9, 11, 13, 15, 17, + 18, 20, 22, 24, 26, + 27, 29, 31, 33, + 34, + 35, + 36 + ].into_iter().collect::>(); + + let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(list.len(), 11); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35] + ); + } + + { // [xxxxxxxxxx+++++++++++] + let mut list = vec![ + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 + ].into_iter().collect::>(); + + let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); + + assert_eq!(list.len(), 10); + assert_eq!(list.into_iter().collect::>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); + } + + { // [+++++++++++xxxxxxxxxx] + let mut list = vec![ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 + ].into_iter().collect::>(); + + let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); + + assert_eq!(list.len(), 10); + assert_eq!(list.into_iter().collect::>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); + } +} diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index c53bf15f1bfb6..85d5ce304b88d 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -10,9 +10,10 @@ use std::cmp::Ordering::{Equal, Greater, Less}; use std::mem; -use std::__rand::{Rng, thread_rng}; use std::rc::Rc; +use rand::{Rng, thread_rng}; + fn square(n: usize) -> usize { n * n } diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index b3178064505e8..a14a5d32738b3 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -706,7 +706,6 @@ fn test_split_at() { #[test] fn test_split_at_mut() { - use std::ascii::AsciiExt; let mut s = "Hello World".to_string(); { let (a, b) = s.split_at_mut(5); @@ -1428,12 +1427,12 @@ mod pattern { Reject(6, 7), Match (7, 7), ]); - make_test!(str_searcher_mulibyte_haystack, " ", "├──", [ + make_test!(str_searcher_multibyte_haystack, " ", "├──", [ Reject(0, 3), Reject(3, 6), Reject(6, 9), ]); - make_test!(str_searcher_empty_needle_mulibyte_haystack, "", "├──", [ + make_test!(str_searcher_empty_needle_multibyte_haystack, "", "├──", [ Match (0, 0), Reject(0, 3), Match (3, 3), @@ -1456,7 +1455,7 @@ mod pattern { Match (5, 6), Reject(6, 7), ]); - make_test!(char_searcher_mulibyte_haystack, ' ', "├──", [ + make_test!(char_searcher_multibyte_haystack, ' ', "├──", [ Reject(0, 3), Reject(3, 6), Reject(6, 9), diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 0e25da5bd3077..9cfde5dcc73c8 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ascii::AsciiExt; use std::borrow::Cow; use std::mem::size_of; use std::panic; @@ -966,5 +965,3 @@ fn drain_filter_complex() { assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); } } - - diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 7dd8895c1ae4c..c29449a241e45 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -507,13 +507,9 @@ impl Vec { /// Converts the vector into [`Box<[T]>`][owned slice]. /// - /// Note that this will drop any excess capacity. Calling this and - /// converting back to a vector with [`into_vec`] is equivalent to calling - /// [`shrink_to_fit`]. + /// Note that this will drop any excess capacity. /// /// [owned slice]: ../../std/boxed/struct.Box.html - /// [`into_vec`]: ../../std/primitive.slice.html#method.into_vec - /// [`shrink_to_fit`]: #method.shrink_to_fit /// /// # Examples /// @@ -857,8 +853,6 @@ impl Vec { /// # Examples /// /// ``` - /// use std::ascii::AsciiExt; - /// /// let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; /// /// vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); @@ -1095,7 +1089,7 @@ impl Vec { // 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 + // the source vector to make sure no uninitialized 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. @@ -1547,6 +1541,7 @@ impl Hash for Vec { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl Index for Vec { type Output = T; @@ -1558,6 +1553,7 @@ impl Index for Vec { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl IndexMut for Vec { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { @@ -1566,8 +1562,8 @@ impl IndexMut for Vec { } } - #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index> for Vec { type Output = [T]; @@ -1576,7 +1572,9 @@ impl ops::Index> for Vec { Index::index(&**self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index> for Vec { type Output = [T]; @@ -1585,7 +1583,9 @@ impl ops::Index> for Vec { Index::index(&**self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index> for Vec { type Output = [T]; @@ -1594,7 +1594,9 @@ impl ops::Index> for Vec { Index::index(&**self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index for Vec { type Output = [T]; @@ -1603,7 +1605,9 @@ impl ops::Index for Vec { self } } + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index> for Vec { type Output = [T]; @@ -1612,7 +1616,9 @@ impl ops::Index> for Vec { Index::index(&**self, index) } } + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::Index> for Vec { type Output = [T]; @@ -1623,41 +1629,52 @@ impl ops::Index> for Vec { } #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut> for Vec { #[inline] fn index_mut(&mut self, index: ops::Range) -> &mut [T] { IndexMut::index_mut(&mut **self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut> for Vec { #[inline] fn index_mut(&mut self, index: ops::RangeTo) -> &mut [T] { IndexMut::index_mut(&mut **self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut> for Vec { #[inline] fn index_mut(&mut self, index: ops::RangeFrom) -> &mut [T] { IndexMut::index_mut(&mut **self, index) } } + #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut for Vec { #[inline] fn index_mut(&mut self, _index: ops::RangeFull) -> &mut [T] { self } } + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut> for Vec { #[inline] fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut [T] { IndexMut::index_mut(&mut **self, index) } } + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] impl ops::IndexMut> for Vec { #[inline] fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut [T] { @@ -1950,7 +1967,7 @@ impl Vec { /// assert_eq!(u, &[1, 2]); /// ``` #[inline] - #[stable(feature = "vec_splice", since = "1.22.0")] + #[stable(feature = "vec_splice", since = "1.21.0")] pub fn splice(&mut self, range: R, replace_with: I) -> Splice where R: RangeArgument, I: IntoIterator { @@ -2553,13 +2570,13 @@ impl<'a, T> InPlace for PlaceBack<'a, T> { /// [`splice()`]: struct.Vec.html#method.splice /// [`Vec`]: struct.Vec.html #[derive(Debug)] -#[stable(feature = "vec_splice", since = "1.22.0")] +#[stable(feature = "vec_splice", since = "1.21.0")] pub struct Splice<'a, I: Iterator + 'a> { drain: Drain<'a, I::Item>, replace_with: I, } -#[stable(feature = "vec_splice", since = "1.22.0")] +#[stable(feature = "vec_splice", since = "1.21.0")] impl<'a, I: Iterator> Iterator for Splice<'a, I> { type Item = I::Item; @@ -2572,18 +2589,18 @@ impl<'a, I: Iterator> Iterator for Splice<'a, I> { } } -#[stable(feature = "vec_splice", since = "1.22.0")] +#[stable(feature = "vec_splice", since = "1.21.0")] impl<'a, I: Iterator> DoubleEndedIterator for Splice<'a, I> { fn next_back(&mut self) -> Option { self.drain.next_back() } } -#[stable(feature = "vec_splice", since = "1.22.0")] +#[stable(feature = "vec_splice", since = "1.21.0")] impl<'a, I: Iterator> ExactSizeIterator for Splice<'a, I> {} -#[stable(feature = "vec_splice", since = "1.22.0")] +#[stable(feature = "vec_splice", since = "1.21.0")] impl<'a, I: Iterator> Drop for Splice<'a, I> { fn drop(&mut self) { // exhaust drain first diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 6d64e9e303f76..f56aa23a4eb2f 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -1922,7 +1922,7 @@ impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { } } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Clone for Iter<'a, T> { fn clone(&self) -> Iter<'a, T> { diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 4042c4d2d4e02..6d7d83dd99388 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -19,7 +19,7 @@ libc = { path = "../rustc/libc_shim" } [build-dependencies] build_helper = { path = "../build_helper" } -cc = "1.0" +cc = "1.0.1" [features] debug = [] diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs index 7dd85ddcc7965..de5006ad3960a 100644 --- a/src/liballoc_jemalloc/build.rs +++ b/src/liballoc_jemalloc/build.rs @@ -31,7 +31,7 @@ fn main() { let host = env::var("HOST").expect("HOST was not set"); if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") || target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") || - target.contains("redox") { + target.contains("redox") || target.contains("wasm32") { println!("cargo:rustc-cfg=dummy_jemalloc"); return; } @@ -63,15 +63,6 @@ fn main() { _ => return, }; - let compiler = cc::Build::new().get_compiler(); - // only msvc returns None for ar so unwrap is okay - let ar = build_helper::cc2ar(compiler.path(), &target).unwrap(); - let cflags = compiler.args() - .iter() - .map(|s| s.to_str().unwrap()) - .collect::>() - .join(" "); - let mut cmd = Command::new("sh"); cmd.arg(native.src_dir.join("configure") .to_str() @@ -79,8 +70,6 @@ fn main() { .replace("C:\\", "/c/") .replace("\\", "/")) .current_dir(&native.out_dir) - .env("CC", compiler.path()) - .env("EXTRA_CFLAGS", cflags.clone()) // jemalloc generates Makefile deps using GCC's "-MM" flag. This means // that GCC will run the preprocessor, and only the preprocessor, over // jemalloc's source files. If we don't specify CPPFLAGS, then at least @@ -89,9 +78,7 @@ fn main() { // passed to GCC, and then GCC won't define the // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to // select an atomic operation implementation. - .env("CPPFLAGS", cflags.clone()) - .env("AR", &ar) - .env("RANLIB", format!("{} s", ar.display())); + .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default()); if target.contains("ios") { cmd.arg("--disable-tls"); @@ -153,6 +140,6 @@ fn main() { cc::Build::new() .flag("-fvisibility=hidden") .file("pthread_atfork_dummy.c") - .compile("libpthread_atfork_dummy.a"); + .compile("pthread_atfork_dummy"); } } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index d153f19c4622f..d7370ae400dac 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -72,8 +72,7 @@ mod contents { const MALLOCX_ZERO: c_int = 0x40; // The minimum alignment guaranteed by the architecture. This value is used to - // add fast paths for low alignment values. In practice, the alignment is a - // constant at the call site and the branch will be optimized out. + // add fast paths for low alignment values. #[cfg(all(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")))] @@ -92,8 +91,8 @@ mod contents { a.trailing_zeros() as c_int } - fn align_to_flags(align: usize) -> c_int { - if align <= MIN_ALIGN { + fn align_to_flags(align: usize, size: usize) -> c_int { + if align <= MIN_ALIGN && align <= size { 0 } else { mallocx_align(align) @@ -107,11 +106,11 @@ mod contents { // ABI #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8 { - let flags = align_to_flags(align); + let flags = align_to_flags(align, size); let ptr = mallocx(size as size_t, flags) as *mut u8; if ptr.is_null() { let layout = Layout::from_size_align_unchecked(size, align); @@ -122,27 +121,27 @@ mod contents { } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_oom(err: *const u8) -> ! { System.oom((*(err as *const AllocErr)).clone()) } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, size: usize, align: usize) { - let flags = align_to_flags(align); + let flags = align_to_flags(align, size); sdallocx(ptr as *mut c_void, size, flags); } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_usable_size(layout: *const u8, min: *mut usize, max: *mut usize) { let layout = &*(layout as *const Layout); - let flags = align_to_flags(layout.align()); + let flags = align_to_flags(layout.align(), layout.size()); let size = nallocx(layout.size(), flags) as usize; *min = layout.size(); if size > 0 { @@ -153,7 +152,7 @@ mod contents { } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_realloc(ptr: *mut u8, _old_size: usize, old_align: usize, @@ -166,7 +165,7 @@ mod contents { return 0 as *mut u8 } - let flags = align_to_flags(new_align); + let flags = align_to_flags(new_align, new_size); let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; if ptr.is_null() { let layout = Layout::from_size_align_unchecked(new_size, new_align); @@ -177,14 +176,14 @@ mod contents { } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8 { - let ptr = if align <= MIN_ALIGN { + let ptr = if align <= MIN_ALIGN && align <= size { calloc(size as size_t, 1) as *mut u8 } else { - let flags = align_to_flags(align) | MALLOCX_ZERO; + let flags = align_to_flags(align, size) | MALLOCX_ZERO; mallocx(size as size_t, flags) as *mut u8 }; if ptr.is_null() { @@ -196,20 +195,21 @@ mod contents { } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc_excess(size: usize, align: usize, excess: *mut usize, err: *mut u8) -> *mut u8 { let p = __rde_alloc(size, align, err); if !p.is_null() { - *excess = size; + let flags = align_to_flags(align, size); + *excess = nallocx(size, flags) as usize; } return p } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_realloc_excess(ptr: *mut u8, old_size: usize, old_align: usize, @@ -219,13 +219,14 @@ mod contents { err: *mut u8) -> *mut u8 { let p = __rde_realloc(ptr, old_size, old_align, new_size, new_align, err); if !p.is_null() { - *excess = new_size; + let flags = align_to_flags(new_align, new_size); + *excess = nallocx(new_size, flags) as usize; } - return p + p } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_grow_in_place(ptr: *mut u8, old_size: usize, old_align: usize, @@ -235,14 +236,14 @@ mod contents { } #[no_mangle] - #[linkage = "external"] + #[rustc_std_internal_symbol] pub unsafe extern fn __rde_shrink_in_place(ptr: *mut u8, _old_size: usize, old_align: usize, new_size: usize, new_align: usize) -> u8 { if old_align == new_align { - let flags = align_to_flags(new_align); + let flags = align_to_flags(new_align, new_size); (xallocx(ptr as *mut c_void, new_size, 0, flags) == new_size) as u8 } else { 0 diff --git a/src/liballoc_system/Cargo.toml b/src/liballoc_system/Cargo.toml index a725a8608be29..f9a57f7d97a74 100644 --- a/src/liballoc_system/Cargo.toml +++ b/src/liballoc_system/Cargo.toml @@ -13,3 +13,7 @@ doc = false alloc = { path = "../liballoc" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } + +# See comments in the source for what this dependency is +[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] +dlmalloc = { path = "../rustc/dlmalloc_shim" } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 2eb659699eb9b..27259cc31a5ed 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -14,7 +14,7 @@ #![unstable(feature = "alloc_system", reason = "this library is unlikely to be stabilized in its current \ form or name", - issue = "27783")] + issue = "32838")] #![feature(global_allocator)] #![feature(allocator_api)] #![feature(alloc)] @@ -25,8 +25,7 @@ #![rustc_alloc_kind = "lib"] // The minimum alignment guaranteed by the architecture. This value is used to -// add fast paths for low alignment values. In practice, the alignment is a -// constant at the call site and the branch will be optimized out. +// add fast paths for low alignment values. #[cfg(all(any(target_arch = "x86", target_arch = "arm", target_arch = "mips", @@ -34,12 +33,14 @@ target_arch = "powerpc64", target_arch = "asmjs", target_arch = "wasm32")))] +#[allow(dead_code)] const MIN_ALIGN: usize = 8; #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "mips64", target_arch = "s390x", target_arch = "sparc64")))] +#[allow(dead_code)] const MIN_ALIGN: usize = 16; extern crate alloc; @@ -130,7 +131,7 @@ mod platform { unsafe impl<'a> Alloc for &'a System { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = if layout.align() <= MIN_ALIGN { + let ptr = if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { libc::malloc(layout.size()) as *mut u8 } else { aligned_malloc(&layout) @@ -146,7 +147,7 @@ mod platform { unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - if layout.align() <= MIN_ALIGN { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { let ptr = libc::calloc(layout.size(), 1) as *mut u8; if !ptr.is_null() { Ok(ptr) @@ -178,7 +179,7 @@ mod platform { }) } - if new_layout.align() <= MIN_ALIGN { + if new_layout.align() <= MIN_ALIGN && new_layout.align() <= new_layout.size(){ let ptr = libc::realloc(ptr as *mut libc::c_void, new_layout.size()); if !ptr.is_null() { Ok(ptr as *mut u8) @@ -458,3 +459,91 @@ mod platform { } } } + +// This is an implementation of a global allocator on the wasm32 platform when +// emscripten is not in use. In that situation there's no actual runtime for us +// to lean on for allocation, so instead we provide our own! +// +// The wasm32 instruction set has two instructions for getting the current +// amount of memory and growing the amount of memory. These instructions are the +// foundation on which we're able to build an allocator, so we do so! Note that +// the instructions are also pretty "global" and this is the "global" allocator +// after all! +// +// The current allocator here is the `dlmalloc` crate which we've got included +// in the rust-lang/rust repository as a submodule. The crate is a port of +// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust" +// for now which is currently technically required (can't link with C yet). +// +// The crate itself provides a global allocator which on wasm has no +// synchronization as there are no threads! +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +mod platform { + extern crate dlmalloc; + + use alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; + use System; + use self::dlmalloc::GlobalDlmalloc; + + #[unstable(feature = "allocator_api", issue = "32838")] + unsafe impl<'a> Alloc for &'a System { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalDlmalloc.alloc(layout) + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) + -> Result<*mut u8, AllocErr> + { + GlobalDlmalloc.alloc_zeroed(layout) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + GlobalDlmalloc.dealloc(ptr, layout) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + old_layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalDlmalloc.realloc(ptr, old_layout, new_layout) + } + + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + GlobalDlmalloc.usable_size(layout) + } + + #[inline] + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + GlobalDlmalloc.alloc_excess(layout) + } + + #[inline] + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + GlobalDlmalloc.realloc_excess(ptr, layout, new_layout) + } + + #[inline] + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + GlobalDlmalloc.grow_in_place(ptr, layout, new_layout) + } + + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + GlobalDlmalloc.shrink_in_place(ptr, layout, new_layout) + } + } +} diff --git a/src/libbacktrace/configure b/src/libbacktrace/configure index ed47ba3c2fa08..8bdb29d25606f 100755 --- a/src/libbacktrace/configure +++ b/src/libbacktrace/configure @@ -12323,6 +12323,12 @@ fi fi fi + +case "${host_os}" in +darwin*) + have_mmap=no ;; +esac + if test "$have_mmap" = "no"; then VIEW_FILE=read.lo ALLOC_FILE=alloc.lo @@ -12338,7 +12344,7 @@ else _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : - ALLOC_FILE=mmap.lo + ALLOC_FILE=alloc.lo else ALLOC_FILE=alloc.lo fi @@ -14578,7 +14584,7 @@ func_basename () # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. -# value retuned in "$func_basename_result" +# value returned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. diff --git a/src/libbacktrace/configure.ac b/src/libbacktrace/configure.ac index 7ae21b8d1a68c..ea1b27d807e13 100644 --- a/src/libbacktrace/configure.ac +++ b/src/libbacktrace/configure.ac @@ -283,6 +283,12 @@ else AC_CHECK_FUNC(mmap, [have_mmap=yes], [have_mmap=no]) fi fi + +case "${host_os}" in +darwin*) + have_mmap=no ;; +esac + if test "$have_mmap" = "no"; then VIEW_FILE=read.lo ALLOC_FILE=alloc.lo diff --git a/src/libbacktrace/ltmain.sh b/src/libbacktrace/ltmain.sh index eaef55a59332a..eff9e62be8a05 100644 --- a/src/libbacktrace/ltmain.sh +++ b/src/libbacktrace/ltmain.sh @@ -177,7 +177,7 @@ basename="s,^.*/,," # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. -# value retuned in "$func_basename_result" +# value returned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. diff --git a/src/libcollections/Cargo.toml b/src/libcollections/Cargo.toml deleted file mode 100644 index 800e36161d245..0000000000000 --- a/src/libcollections/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "collections" -version = "0.0.0" - -[lib] -name = "collections" -path = "lib.rs" - -[dependencies] -alloc = { path = "../liballoc" } -core = { path = "../libcore" } diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs deleted file mode 100644 index 55316db3d5a43..0000000000000 --- a/src/libcollections/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unused_attributes)] -#![unstable(feature = "collections", - reason = "this library is unlikely to be stabilized in its current \ - form or name", - issue = "27783")] -#![rustc_deprecated(since = "1.20.0", - reason = "collections moved to `alloc`")] -#![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/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] -#![no_std] -#![deny(warnings)] - -#![feature(alloc)] -#![feature(collections_range)] -#![feature(macro_reexport)] -#![feature(staged_api)] - -//! Collection types -//! -//! See [`std::collections`](../std/collections/index.html) for a detailed -//! discussion of collections in Rust. - -#[macro_reexport(vec, format)] -extern crate alloc; - -pub use alloc::Bound; - -pub use alloc::binary_heap; -pub use alloc::borrow; -pub use alloc::fmt; -pub use alloc::linked_list; -pub use alloc::range; -pub use alloc::slice; -pub use alloc::str; -pub use alloc::string; -pub use alloc::vec; -pub use alloc::vec_deque; - -pub use alloc::btree_map; -pub use alloc::btree_set; - -#[doc(no_inline)] -pub use alloc::binary_heap::BinaryHeap; -#[doc(no_inline)] -pub use alloc::btree_map::BTreeMap; -#[doc(no_inline)] -pub use alloc::btree_set::BTreeSet; -#[doc(no_inline)] -pub use alloc::linked_list::LinkedList; -#[doc(no_inline)] -pub use alloc::vec_deque::VecDeque; -#[doc(no_inline)] -pub use alloc::string::String; -#[doc(no_inline)] -pub use alloc::vec::Vec; diff --git a/src/libcompiler_builtins b/src/libcompiler_builtins index ef4951582f620..18feaccbfd0df 160000 --- a/src/libcompiler_builtins +++ b/src/libcompiler_builtins @@ -1 +1 @@ -Subproject commit ef4951582f620c589cd9e18ec182538bf116bce3 +Subproject commit 18feaccbfd0dfbd5ab5d0a2a6eac9c04be667266 diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 178df02ccdde3..5af63aa970f2c 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -9,9 +9,6 @@ path = "lib.rs" test = false bench = false -[dev-dependencies] -rand = { path = "../librand" } - [[test]] name = "coretests" path = "../libcore/tests/lib.rs" diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 6a7926fecde38..3d24f8902bd83 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -21,6 +21,7 @@ use borrow::{Borrow, BorrowMut}; use cmp::Ordering; +use convert::TryFrom; use fmt; use hash::{Hash, self}; use marker::Unsize; @@ -57,6 +58,30 @@ unsafe impl> FixedSizeArray for A { } } +/// The error type returned when a conversion from a slice to an array fails. +#[unstable(feature = "try_from", issue = "33417")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromSliceError(()); + +impl fmt::Display for TryFromSliceError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.__description(), f) + } +} + +impl TryFromSliceError { + #[unstable(feature = "array_error_internals", + reason = "available through Error trait and this method should not \ + be exposed publicly", + issue = "0")] + #[inline] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "could not convert slice to array" + } +} + macro_rules! __impl_slice_eq1 { ($Lhs: ty, $Rhs: ty) => { __impl_slice_eq1! { $Lhs, $Rhs, Sized } @@ -123,6 +148,34 @@ macro_rules! array_impls { } } + #[unstable(feature = "try_from", issue = "33417")] + impl<'a, T> TryFrom<&'a [T]> for &'a [T; $N] { + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<&[T; $N], TryFromSliceError> { + if slice.len() == $N { + let ptr = slice.as_ptr() as *const [T; $N]; + unsafe { Ok(&*ptr) } + } else { + Err(TryFromSliceError(())) + } + } + } + + #[unstable(feature = "try_from", issue = "33417")] + impl<'a, T> TryFrom<&'a mut [T]> for &'a mut [T; $N] { + type Error = TryFromSliceError; + + fn try_from(slice: &mut [T]) -> Result<&mut [T; $N], TryFromSliceError> { + if slice.len() == $N { + let ptr = slice.as_mut_ptr() as *mut [T; $N]; + unsafe { Ok(&mut *ptr) } + } else { + Err(TryFromSliceError(())) + } + } + } + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for [T; $N] { fn hash(&self, state: &mut H) { diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 827c6354c60ba..b284d855c4515 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -147,40 +147,137 @@ fn bench_for_each_chain_ref_fold(b: &mut Bencher) { }); } -#[bench] -fn bench_flat_map_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .sum() - }); + +/// Helper to benchmark `sum` for iterators taken by value which +/// can optimize `fold`, and by reference which cannot. +macro_rules! bench_sums { + ($bench_sum:ident, $bench_ref_sum:ident, $iter:expr) => { + #[bench] + fn $bench_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).sum() + }); + } + + #[bench] + fn $bench_ref_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).by_ref().sum() + }); + } + } } -#[bench] -fn bench_flat_map_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_flat_map_sum, + bench_flat_map_ref_sum, + (0i64..1000).flat_map(|x| x..x+1000) } -#[bench] -fn bench_flat_map_chain_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .sum() - }); +bench_sums! { + bench_flat_map_chain_sum, + bench_flat_map_chain_ref_sum, + (0i64..1000000).flat_map(|x| once(x).chain(once(x))) } -#[bench] -fn bench_flat_map_chain_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_enumerate_sum, + bench_enumerate_ref_sum, + (0i64..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_enumerate_chain_sum, + bench_enumerate_chain_ref_sum, + (0i64..1000000).chain(0..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_filter_sum, + bench_filter_ref_sum, + (0i64..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_chain_sum, + bench_filter_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_map_sum, + bench_filter_map_ref_sum, + (0i64..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_filter_map_chain_sum, + bench_filter_map_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_fuse_sum, + bench_fuse_ref_sum, + (0i64..1000000).fuse() +} + +bench_sums! { + bench_fuse_chain_sum, + bench_fuse_chain_ref_sum, + (0i64..1000000).chain(0..1000000).fuse() +} + +bench_sums! { + bench_inspect_sum, + bench_inspect_ref_sum, + (0i64..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_inspect_chain_sum, + bench_inspect_chain_ref_sum, + (0i64..1000000).chain(0..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_peekable_sum, + bench_peekable_ref_sum, + (0i64..1000000).peekable() +} + +bench_sums! { + bench_peekable_chain_sum, + bench_peekable_chain_ref_sum, + (0i64..1000000).chain(0..1000000).peekable() +} + +bench_sums! { + bench_skip_sum, + bench_skip_ref_sum, + (0i64..1000000).skip(1000) +} + +bench_sums! { + bench_skip_chain_sum, + bench_skip_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip(1000) +} + +bench_sums! { + bench_skip_while_sum, + bench_skip_while_ref_sum, + (0i64..1000000).skip_while(|&x| x < 1000) +} + +bench_sums! { + bench_skip_while_chain_sum, + bench_skip_while_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip_while(|&x| x < 1000) +} + +bench_sums! { + bench_take_while_chain_sum, + bench_take_while_chain_ref_sum, + (0i64..1000000).chain(1000000..).take_while(|&x| x < 1111111) } diff --git a/src/libcore/benches/lib.rs b/src/libcore/benches/lib.rs index d2db329da7999..201064e823b1e 100644 --- a/src/libcore/benches/lib.rs +++ b/src/libcore/benches/lib.rs @@ -20,6 +20,6 @@ extern crate test; mod any; mod hash; mod iter; -mod mem; mod num; mod ops; +mod slice; diff --git a/src/libcore/benches/slice.rs b/src/libcore/benches/slice.rs new file mode 100644 index 0000000000000..b2fc74544f1df --- /dev/null +++ b/src/libcore/benches/slice.rs @@ -0,0 +1,67 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use test::black_box; +use test::Bencher; + +enum Cache { + L1, + L2, + L3, +} + +fn binary_search(b: &mut Bencher, cache: Cache, mapper: F) + where F: Fn(usize) -> usize +{ + let size = match cache { + Cache::L1 => 1000, // 8kb + Cache::L2 => 10_000, // 80kb + Cache::L3 => 1_000_000, // 8Mb + }; + let v = (0..size).map(&mapper).collect::>(); + let mut r = 0usize; + b.iter(move || { + // LCG constants from https://en.wikipedia.org/wiki/Numerical_Recipes. + r = r.wrapping_mul(1664525).wrapping_add(1013904223); + // Lookup the whole range to get 50% hits and 50% misses. + let i = mapper(r % size); + black_box(v.binary_search(&i).is_ok()); + }) +} + +#[bench] +fn binary_search_l1(b: &mut Bencher) { + binary_search(b, Cache::L1, |i| i * 2); +} + +#[bench] +fn binary_search_l2(b: &mut Bencher) { + binary_search(b, Cache::L2, |i| i * 2); +} + +#[bench] +fn binary_search_l3(b: &mut Bencher) { + binary_search(b, Cache::L3, |i| i * 2); +} + +#[bench] +fn binary_search_l1_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L1, |i| i / 16 * 16); +} + +#[bench] +fn binary_search_l2_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L2, |i| i / 16 * 16); +} + +#[bench] +fn binary_search_l3_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L3, |i| i / 16 * 16); +} diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index b9c5ff10f87b9..d4cd3f6264efc 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -329,7 +329,6 @@ impl Cell { /// let c = Cell::new(5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_cell_new"))] #[inline] pub const fn new(value: T) -> Cell { Cell { @@ -544,7 +543,6 @@ impl RefCell { /// let c = RefCell::new(5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_refcell_new"))] #[inline] pub const fn new(value: T) -> RefCell { RefCell { @@ -579,25 +577,51 @@ impl RefCell { /// /// This function corresponds to [`std::mem::replace`](../mem/fn.replace.html). /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + /// /// # Examples /// /// ``` /// #![feature(refcell_replace_swap)] /// use std::cell::RefCell; - /// let c = RefCell::new(5); - /// let u = c.replace(6); - /// assert_eq!(u, 5); - /// assert_eq!(c, RefCell::new(6)); + /// let cell = RefCell::new(5); + /// let old_value = cell.replace(6); + /// assert_eq!(old_value, 5); + /// assert_eq!(cell, RefCell::new(6)); /// ``` + #[inline] + #[unstable(feature = "refcell_replace_swap", issue="43570")] + pub fn replace(&self, t: T) -> T { + mem::replace(&mut *self.borrow_mut(), t) + } + + /// Replaces the wrapped value with a new one computed from `f`, returning + /// the old value, without deinitializing either one. + /// + /// This function corresponds to [`std::mem::replace`](../mem/fn.replace.html). /// /// # Panics /// - /// This function will panic if the `RefCell` has any outstanding borrows, - /// whether or not they are full mutable borrows. + /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_replace_swap)] + /// use std::cell::RefCell; + /// let cell = RefCell::new(5); + /// let old_value = cell.replace_with(|&mut old| old + 1); + /// assert_eq!(old_value, 5); + /// assert_eq!(cell, RefCell::new(6)); + /// ``` #[inline] #[unstable(feature = "refcell_replace_swap", issue="43570")] - pub fn replace(&self, t: T) -> T { - mem::replace(&mut *self.borrow_mut(), t) + pub fn replace_with T>(&self, f: F) -> T { + let mut_borrow = &mut *self.borrow_mut(); + let replacement = f(mut_borrow); + mem::replace(mut_borrow, replacement) } /// Swaps the wrapped value of `self` with the wrapped value of `other`, @@ -605,6 +629,10 @@ impl RefCell { /// /// This function corresponds to [`std::mem::swap`](../mem/fn.swap.html). /// + /// # Panics + /// + /// Panics if the value in either `RefCell` is currently borrowed. + /// /// # Examples /// /// ``` @@ -616,11 +644,6 @@ impl RefCell { /// assert_eq!(c, RefCell::new(6)); /// assert_eq!(d, RefCell::new(5)); /// ``` - /// - /// # Panics - /// - /// This function will panic if either `RefCell` has any outstanding borrows, - /// whether or not they are full mutable borrows. #[inline] #[unstable(feature = "refcell_replace_swap", issue="43570")] pub fn swap(&self, other: &Self) { @@ -1190,7 +1213,6 @@ impl UnsafeCell { /// let uc = UnsafeCell::new(5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_unsafe_cell_new"))] #[inline] pub const fn new(value: T) -> UnsafeCell { UnsafeCell { value: value } diff --git a/src/libcore/char_private.rs b/src/libcore/char_private.rs index 2c0f449b27601..e6803745ab543 100644 --- a/src/libcore/char_private.rs +++ b/src/libcore/char_private.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2016 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. // @@ -47,7 +47,7 @@ fn check(x: u16, singletonuppers: &[(u8, u8)], singletonlowers: &[u8], current } -pub fn is_printable(x: char) -> bool { +pub(crate) fn is_printable(x: char) -> bool { let x = x as u32; let lower = x as u16; if x < 0x10000 { @@ -64,7 +64,10 @@ pub fn is_printable(x: char) -> bool { if 0x2b81e <= x && x < 0x2b820 { return false; } - if 0x2cea2 <= x && x < 0x2f800 { + if 0x2cea2 <= x && x < 0x2ceb0 { + return false; + } + if 0x2ebe1 <= x && x < 0x2f800 { return false; } if 0x2fa1e <= x && x < 0xe0100 { @@ -83,12 +86,12 @@ const SINGLETONS0U: &'static [(u8, u8)] = &[ (0x05, 8), (0x06, 3), (0x07, 4), - (0x08, 7), + (0x08, 8), (0x09, 16), (0x0a, 27), - (0x0b, 24), + (0x0b, 25), (0x0c, 22), - (0x0d, 20), + (0x0d, 18), (0x0e, 22), (0x0f, 4), (0x10, 3), @@ -99,16 +102,15 @@ const SINGLETONS0U: &'static [(u8, u8)] = &[ (0x18, 2), (0x19, 3), (0x1a, 7), - (0x1c, 1), + (0x1d, 1), (0x1f, 22), (0x20, 3), - (0x23, 1), (0x2b, 5), (0x2c, 2), (0x2d, 11), (0x2e, 1), (0x30, 3), - (0x31, 1), + (0x31, 3), (0x32, 2), (0xa7, 1), (0xa8, 2), @@ -125,19 +127,19 @@ const SINGLETONS0L: &'static [u8] = &[ 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x60, 0x88, 0x8b, 0x8c, 0x90, 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0x2e, 0x2f, 0x3f, - 0x5c, 0x5d, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, - 0x92, 0xa9, 0xb1, 0xba, 0xbb, 0xc5, 0xc6, 0xc9, - 0xca, 0xde, 0xe4, 0xe5, 0x04, 0x11, 0x12, 0x29, - 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, - 0x5d, 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, - 0xbb, 0xc6, 0xca, 0xce, 0xcf, 0xe4, 0xe5, 0x04, - 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, - 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, - 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x04, - 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, - 0x84, 0x8d, 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, - 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x04, 0x0d, 0x11, - 0x3b, 0x3c, 0x45, 0x49, 0x64, 0x65, 0x80, 0x81, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, + 0x91, 0x92, 0xa9, 0xb1, 0xba, 0xbb, 0xc5, 0xc6, + 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0x04, 0x11, 0x12, + 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, + 0x4a, 0x5d, 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, + 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, 0xe4, 0xe5, + 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, + 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, + 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, + 0xcf, 0x04, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, + 0x64, 0x65, 0x84, 0x8d, 0x91, 0xa9, 0xb4, 0xba, + 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x04, + 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x81, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x86, 0x89, 0x8b, 0x8c, 0x98, 0xa0, 0xa4, 0xa6, 0xa8, 0xa9, 0xac, 0xba, 0xbe, @@ -148,18 +150,18 @@ const SINGLETONS0L: &'static [u8] = &[ 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, - 0x7e, 0xae, 0xaf, 0xf7, 0x16, 0x17, 0x1e, 0x1f, + 0x7e, 0xae, 0xaf, 0xfa, 0x16, 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, - 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0xff, 0x74, 0x75, - 0x96, 0x97, 0xc9, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, - 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, - 0x9a, 0x40, 0x97, 0x98, 0x8f, 0x1f, 0xff, 0xaf, - 0xfe, 0xff, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, - 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, 0xef, - 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, - 0x91, 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, - 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, 0xfe, 0xff, + 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, + 0x97, 0xc9, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, + 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, + 0x40, 0x97, 0x98, 0x2f, 0x30, 0x8f, 0x1f, 0xff, + 0xaf, 0xfe, 0xff, 0xce, 0xff, 0x4e, 0x4f, 0x5a, + 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, + 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, + 0x90, 0x91, 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, + 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, 0xfe, 0xff, ]; const SINGLETONS1U: &'static [(u8, u8)] = &[ (0x00, 6), @@ -176,7 +178,9 @@ const SINGLETONS1U: &'static [(u8, u8)] = &[ (0x13, 18), (0x14, 2), (0x15, 2), + (0x1a, 3), (0x1c, 5), + (0x1d, 4), (0x24, 1), (0x6a, 3), (0x6b, 2), @@ -192,7 +196,7 @@ const SINGLETONS1U: &'static [(u8, u8)] = &[ (0xee, 32), (0xf0, 4), (0xf1, 1), - (0xf9, 4), + (0xf9, 1), ]; const SINGLETONS1L: &'static [u8] = &[ 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, @@ -202,18 +206,18 @@ const SINGLETONS1L: &'static [u8] = &[ 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5a, 0x5c, 0xb6, - 0xb7, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x6f, 0x5f, - 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, - 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, - 0xad, 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, - 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, - 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0xc5, - 0xc6, 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, - 0x38, 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, 0x55, - 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, - 0x66, 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, - 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, 0x2f, 0x1f, 0x31, - 0x32, 0x3f, + 0xb7, 0x84, 0x85, 0x9d, 0x09, 0x37, 0x90, 0x91, + 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x6f, 0x5f, 0xee, + 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, 0x55, + 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, + 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, + 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, + 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, + 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, 0x55, 0x56, + 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, + 0xaf, 0xb0, 0xc0, 0xd0, 0x2f, 0x3f, ]; const NORMAL0: &'static [u8] = &[ 0x00, 0x20, @@ -224,12 +228,12 @@ const NORMAL0: &'static [u8] = &[ 0x05, 0x11, 0x81, 0xac, 0x0e, 0x3b, 0x05, - 0x5f, 0x41, + 0x6b, 0x35, 0x1e, 0x16, 0x80, 0xdf, 0x03, 0x19, 0x08, 0x01, 0x04, - 0x20, 0x05, + 0x22, 0x03, 0x0a, 0x04, 0x34, 0x04, 0x07, 0x03, @@ -238,8 +242,7 @@ const NORMAL0: &'static [u8] = &[ 0x10, 0x0b, 0x50, 0x0f, 0x12, 0x07, - 0x01, 0x07, - 0x4d, 0x08, + 0x55, 0x08, 0x02, 0x04, 0x1c, 0x0a, 0x09, 0x03, @@ -258,8 +261,8 @@ const NORMAL0: &'static [u8] = &[ 0x10, 0x08, 0x56, 0x07, 0x02, 0x07, - 0x15, 0x0e, - 0x4f, 0x04, + 0x15, 0x0d, + 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, @@ -304,34 +307,32 @@ const NORMAL0: &'static [u8] = &[ 0x3c, 0x37, 0x08, 0x08, 0x2a, 0x06, - 0x80, 0xf6, 0x05, - 0x82, 0x04, 0x11, + 0x82, 0xff, 0x11, 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, - 0x1f, 0x11, + 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x87, 0x5a, 0x03, - 0x15, 0x1a, + 0x16, 0x19, 0x04, 0x10, 0x80, 0xf4, 0x05, 0x2f, 0x05, 0x3b, 0x07, 0x02, 0x0e, 0x18, 0x09, - 0x80, 0xa5, 0x3b, + 0x80, 0xaa, 0x36, 0x74, 0x0c, 0x80, 0xd6, 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, - 0x29, 0x03, - 0x80, 0x8a, 0x05, + 0x80, 0xb6, 0x05, 0x24, 0x0c, 0x9b, 0xc6, 0x0a, - 0xd2, 0x16, 0x2a, + 0xd2, 0x2b, 0x15, 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, @@ -378,8 +379,8 @@ const NORMAL1: &'static [u8] = &[ 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, - 0x24, 0x0c, - 0x1b, 0x05, + 0x24, 0x09, + 0x1e, 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, @@ -447,11 +448,16 @@ const NORMAL1: &'static [u8] = &[ 0x0f, 0x04, 0x10, 0x81, 0x60, 0x53, 0x0c, - 0x01, 0x81, 0xc0, + 0x01, 0x81, 0x00, + 0x48, 0x08, + 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, - 0x47, 0x83, 0x49, + 0x47, 0x49, + 0x37, 0x03, + 0x0e, 0x08, + 0x0a, 0x82, 0xa6, 0x83, 0x9a, 0x66, 0x75, 0x0b, 0x80, 0xc4, 0x8a, 0xbc, @@ -467,10 +473,11 @@ const NORMAL1: &'static [u8] = &[ 0x45, 0x0b, 0x2f, 0x10, 0x11, 0x40, - 0x01, 0x1f, + 0x02, 0x1e, 0x97, 0xed, 0x13, 0x82, 0xf3, 0xa5, 0x0d, - 0x02, 0x8b, 0xfe, + 0x81, 0x1f, 0x51, + 0x81, 0x8c, 0x89, 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, @@ -503,20 +510,22 @@ const NORMAL1: &'static [u8] = &[ 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, - 0x02, 0x80, 0xae, - 0x83, 0xd3, 0x0d, + 0x02, 0x0e, + 0x06, 0x80, 0x9a, + 0x83, 0xd5, 0x0b, 0x0d, 0x03, - 0x07, 0x09, + 0x09, 0x07, 0x74, 0x0c, 0x55, 0x2b, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, - 0x1e, 0x62, - 0x18, 0x08, - 0x1c, 0x04, - 0x0f, 0x21, - 0x12, 0x2e, - 0x01, 0x86, 0x3f, + 0x1e, 0x52, + 0x0c, 0x04, + 0x3d, 0x03, + 0x1c, 0x14, + 0x18, 0x28, + 0x01, 0x0f, + 0x17, 0x86, 0x19, ]; diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 6f86f8caad073..cc71e09caeacc 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -162,8 +162,8 @@ pub trait PartialEq { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait Eq: PartialEq { - // FIXME #13101: this method is used solely by #[deriving] to - // assert that every component of a type implements #[deriving] + // this method is used solely by #[deriving] to assert + // that every component of a type implements #[deriving] // itself, the current deriving infrastructure means doing this // assertion without using a method on this trait is nearly // impossible. @@ -456,7 +456,7 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(2, 1.max(2)); /// assert_eq!(2, 2.max(2)); /// ``` - #[stable(feature = "ord_max_min", since = "1.22.0")] + #[stable(feature = "ord_max_min", since = "1.21.0")] fn max(self, other: Self) -> Self where Self: Sized { if other >= self { other } else { self } @@ -472,7 +472,7 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1, 1.min(2)); /// assert_eq!(2, 2.min(2)); /// ``` - #[stable(feature = "ord_max_min", since = "1.22.0")] + #[stable(feature = "ord_max_min", since = "1.21.0")] fn min(self, other: Self) -> Self where Self: Sized { if self <= other { self } else { other } diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 6f3c3863fae1d..e815d72d36646 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -48,8 +48,25 @@ #![stable(feature = "rust1", since = "1.0.0")] -use str::FromStr; +use fmt; +/// A type used as the error type for implementations of fallible conversion +/// traits in cases where conversions cannot actually fail. +/// +/// Because `Infallible` has no variants, a value of this type can never exist. +/// It is used only to satisfy trait signatures that expect an error type, and +/// signals to both the compiler and the user that the error case is impossible. +#[unstable(feature = "try_from", issue = "33417")] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Infallible {} + +#[unstable(feature = "try_from", issue = "33417")] +impl fmt::Display for Infallible { + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + match *self { + } + } +} /// A cheap reference-to-reference conversion. Used to convert a value to a /// reference value within generic code. /// @@ -417,6 +434,17 @@ impl TryInto for T where U: TryFrom } } +// Infallible conversions are semantically equivalent to fallible conversions +// with an uninhabited error type. +#[unstable(feature = "try_from", issue = "33417")] +impl TryFrom for T where T: From { + type Error = Infallible; + + fn try_from(value: U) -> Result { + Ok(T::from(value)) + } +} + //////////////////////////////////////////////////////////////////////////////// // CONCRETE IMPLS //////////////////////////////////////////////////////////////////////////////// @@ -442,14 +470,3 @@ impl AsRef for str { self } } - -// FromStr implies TryFrom<&str> -#[unstable(feature = "try_from", issue = "33417")] -impl<'a, T> TryFrom<&'a str> for T where T: FromStr -{ - type Error = ::Err; - - fn try_from(s: &'a str) -> Result { - FromStr::from_str(s) - } -} diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index b594c886b64f5..60b9eeb1283cd 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use fmt::{self, FlagV1}; +use fmt; struct PadAdapter<'a, 'b: 'a> { fmt: &'a mut fmt::Formatter<'b>, @@ -140,7 +140,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -233,7 +233,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -277,7 +277,7 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -519,6 +519,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index b84a1deb61144..551aa929ce457 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -261,6 +261,14 @@ pub struct Formatter<'a> { struct Void { _priv: (), + /// Erases all oibits, because `Void` erases the type of the object that + /// will be used to produce formatted output. Since we do not know what + /// oibits the real types have (and they can have any or none), we need to + /// take the most conservative approach and forbid all oibits. + /// + /// It was added after #45197 showed that one could share a `!Sync` + /// object across threads by passing it into `format_args!`. + _oibit_remover: PhantomData<*mut Fn()>, } /// This struct represents the generic "argument" which is taken by the Xprintf @@ -322,7 +330,6 @@ impl<'a> ArgumentV1<'a> { // flags available in the v1 format of format_args #[derive(Copy, Clone)] -#[allow(dead_code)] // SignMinus isn't currently used enum FlagV1 { SignPlus, SignMinus, Alternate, SignAwareZeroPad, } impl<'a> Arguments<'a> { @@ -427,7 +434,7 @@ impl<'a> Display for Arguments<'a> { } } -/// Format trait for the `?` character. +/// `?` formatting. /// /// `Debug` should format the output in a programmer-facing, debugging context. /// @@ -488,13 +495,14 @@ impl<'a> Display for Arguments<'a> { /// The origin is: Point { x: 0, y: 0 } /// ``` /// -/// There are a number of `debug_*` methods on `Formatter` to help you with manual +/// There are a number of `debug_*` methods on [`Formatter`] to help you with manual /// implementations, such as [`debug_struct`][debug_struct]. /// /// `Debug` implementations using either `derive` or the debug builder API -/// on `Formatter` support pretty printing using the alternate flag: `{:#?}`. +/// on [`Formatter`] support pretty printing using the alternate flag: `{:#?}`. /// /// [debug_struct]: ../../std/fmt/struct.Formatter.html#method.debug_struct +/// [`Formatter`]: ../../std/fmt/struct.Formatter.html /// /// Pretty printing with `#?`: /// @@ -525,6 +533,26 @@ impl<'a> Display for Arguments<'a> { #[lang = "debug_trait"] pub trait Debug { /// Formats the value using the given formatter. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Position { + /// longitude: f32, + /// latitude: f32, + /// } + /// + /// impl fmt::Debug for Position { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "({:?}, {:?})", self.longitude, self.latitude) + /// } + /// } + /// + /// assert_eq!("(1.987, 2.983)".to_owned(), + /// format!("{:?}", Position { longitude: 1.987, latitude: 2.983, })); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter) -> Result; } @@ -592,10 +620,13 @@ pub trait Display { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `o` character. +/// `o` formatting. /// /// The `Octal` trait should format its output as a number in base-8. /// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// /// The alternate flag, `#`, adds a `0o` in front of the output. /// /// For more information on formatters, see [the module-level documentation][module]. @@ -611,6 +642,8 @@ pub trait Display { /// /// assert_eq!(format!("{:o}", x), "52"); /// assert_eq!(format!("{:#o}", x), "0o52"); +/// +/// assert_eq!(format!("{:o}", -16), "37777777760"); /// ``` /// /// Implementing `Octal` on a type: @@ -639,10 +672,13 @@ pub trait Octal { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `b` character. +/// `b` formatting. /// /// The `Binary` trait should format its output as a number in binary. /// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// /// The alternate flag, `#`, adds a `0b` in front of the output. /// /// For more information on formatters, see [the module-level documentation][module]. @@ -658,6 +694,8 @@ pub trait Octal { /// /// assert_eq!(format!("{:b}", x), "101010"); /// assert_eq!(format!("{:#b}", x), "0b101010"); +/// +/// assert_eq!(format!("{:b}", -16), "11111111111111111111111111110000"); /// ``` /// /// Implementing `Binary` on a type: @@ -686,11 +724,14 @@ pub trait Binary { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `x` character. +/// `x` formatting. /// /// The `LowerHex` trait should format its output as a number in hexadecimal, with `a` through `f` /// in lower case. /// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// /// The alternate flag, `#`, adds a `0x` in front of the output. /// /// For more information on formatters, see [the module-level documentation][module]. @@ -706,6 +747,8 @@ pub trait Binary { /// /// assert_eq!(format!("{:x}", x), "2a"); /// assert_eq!(format!("{:#x}", x), "0x2a"); +/// +/// assert_eq!(format!("{:x}", -16), "fffffff0"); /// ``` /// /// Implementing `LowerHex` on a type: @@ -734,11 +777,14 @@ pub trait LowerHex { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `X` character. +/// `X` formatting. /// /// The `UpperHex` trait should format its output as a number in hexadecimal, with `A` through `F` /// in upper case. /// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// /// The alternate flag, `#`, adds a `0x` in front of the output. /// /// For more information on formatters, see [the module-level documentation][module]. @@ -754,6 +800,8 @@ pub trait LowerHex { /// /// assert_eq!(format!("{:X}", x), "2A"); /// assert_eq!(format!("{:#X}", x), "0x2A"); +/// +/// assert_eq!(format!("{:X}", -16), "FFFFFFF0"); /// ``` /// /// Implementing `UpperHex` on a type: @@ -782,7 +830,7 @@ pub trait UpperHex { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `p` character. +/// `p` formatting. /// /// The `Pointer` trait should format its output as a memory location. This is commonly presented /// as hexadecimal. @@ -827,7 +875,7 @@ pub trait Pointer { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `e` character. +/// `e` formatting. /// /// The `LowerExp` trait should format its output in scientific notation with a lower-case `e`. /// @@ -870,7 +918,7 @@ pub trait LowerExp { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `E` character. +/// `E` formatting. /// /// The `UpperExp` trait should format its output in scientific notation with an upper-case `E`. /// @@ -932,7 +980,7 @@ pub trait UpperExp { /// assert_eq!(output, "Hello world!"); /// ``` /// -/// Please note that using [`write!`] might be preferrable. Example: +/// Please note that using [`write!`] might be preferable. Example: /// /// ``` /// use std::fmt::Write; @@ -1275,8 +1323,11 @@ impl<'a> Formatter<'a> { write(self.buf, fmt) } - /// Flags for formatting (packed version of rt::Flag) + /// Flags for formatting #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.24.0", + reason = "use the `sign_plus`, `sign_minus`, `alternate`, \ + or `sign_aware_zero_pad` methods instead")] pub fn flags(&self) -> u32 { self.flags } /// Character used as 'fill' whenever there is alignment @@ -1321,8 +1372,11 @@ impl<'a> Formatter<'a> { self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 } - /// Creates a `DebugStruct` builder designed to assist with creation of - /// `fmt::Debug` implementations for structs. + /// Creates a [`DebugStruct`] builder designed to assist with creation of + /// [`fmt::Debug`] implementations for structs. + /// + /// [`DebugStruct`]: ../../std/fmt/struct.DebugStruct.html + /// [`fmt::Debug`]: ../../std/fmt/trait.Debug.html /// /// # Examples /// diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index 8ea388fddf884..ee989854a3772 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -12,7 +12,6 @@ #![allow(deprecated)] -// FIXME: #6220 Implement floating point formatting use fmt; use ops::{Div, Rem, Sub}; @@ -135,7 +134,7 @@ macro_rules! radix { } } -radix! { Binary, 2, "0b", x @ 0 ... 2 => b'0' + x } +radix! { Binary, 2, "0b", x @ 0 ... 1 => b'0' + x } radix! { Octal, 8, "0o", x @ 0 ... 7 => b'0' + x } radix! { Decimal, 10, "", x @ 0 ... 9 => b'0' + x } radix! { LowerHex, 16, "0x", x @ 0 ... 9 => b'0' + x, diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs index bc1b911cd78cc..15545a04b64de 100644 --- a/src/libcore/hash/mod.rs +++ b/src/libcore/hash/mod.rs @@ -259,7 +259,7 @@ pub trait Hasher { /// println!("Hash is {:x}!", hasher.finish()); /// ``` /// - /// ['write']: #tymethod.write + /// [`write`]: #tymethod.write #[stable(feature = "rust1", since = "1.0.0")] fn finish(&self) -> u64; @@ -665,16 +665,36 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for *const T { + impl Hash for *const T { fn hash(&self, state: &mut H) { - state.write_usize(*self as usize) + if mem::size_of::() == mem::size_of::() { + // Thin pointer + state.write_usize(*self as *const () as usize); + } else { + // Fat pointer + let (a, b) = unsafe { + *(self as *const Self as *const (usize, usize)) + }; + state.write_usize(a); + state.write_usize(b); + } } } #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for *mut T { + impl Hash for *mut T { fn hash(&self, state: &mut H) { - state.write_usize(*self as usize) + if mem::size_of::() == mem::size_of::() { + // Thin pointer + state.write_usize(*self as *const () as usize); + } else { + // Fat pointer + let (a, b) = unsafe { + *(self as *const Self as *const (usize, usize)) + }; + state.write_usize(a); + state.write_usize(b); + } } } } diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs index 91fd01b36d495..4e4d9b3f1e2f0 100644 --- a/src/libcore/hash/sip.rs +++ b/src/libcore/hash/sip.rs @@ -22,7 +22,7 @@ use mem; /// This is currently the default hashing function used by standard library /// (eg. `collections::HashMap` uses it by default). /// -/// See: https://131002.net/siphash/ +/// See: #[unstable(feature = "sip_hash_13", issue = "34767")] #[rustc_deprecated(since = "1.13.0", reason = "use `std::collections::hash_map::DefaultHasher` instead")] @@ -33,7 +33,7 @@ pub struct SipHasher13 { /// An implementation of SipHash 2-4. /// -/// See: https://131002.net/siphash/ +/// See: #[unstable(feature = "sip_hash_13", issue = "34767")] #[rustc_deprecated(since = "1.13.0", reason = "use `std::collections::hash_map::DefaultHasher` instead")] @@ -44,7 +44,7 @@ pub struct SipHasher24 { /// An implementation of SipHash 2-4. /// -/// See: https://131002.net/siphash/ +/// See: /// /// SipHash is a general-purpose hashing function: it runs at a good /// speed (competitive with Spooky and City) and permits strong _keyed_ @@ -72,6 +72,7 @@ struct Hasher { } #[derive(Debug, Clone, Copy)] +#[repr(C)] struct State { // v0, v2 and v1, v3 show up in pairs in the algorithm, // and simd implementations of SipHash will use vectors diff --git a/src/libcore/internal_macros.rs b/src/libcore/internal_macros.rs index 9a7914064fdd5..cb215a38e5356 100644 --- a/src/libcore/internal_macros.rs +++ b/src/libcore/internal_macros.rs @@ -68,3 +68,22 @@ macro_rules! forward_ref_binop { } } } + +// implements "T op= &U", based on "T op= U" +// where U is expected to be `Copy`able +macro_rules! forward_ref_op_assign { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + forward_ref_op_assign!(impl $imp, $method for $t, $u, + #[stable(feature = "op_assign_builtins_by_ref", since = "1.22.0")]); + }; + (impl $imp:ident, $method:ident for $t:ty, $u:ty, #[$attr:meta]) => { + #[$attr] + impl<'a> $imp<&'a $u> for $t { + #[inline] + fn $method(&mut self, other: &'a $u) { + $imp::$method(self, *other); + } + } + } +} + diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index bc82f0230e5b4..f1e51e995c238 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -627,6 +627,9 @@ extern "rust-intrinsic" { pub fn rustc_peek(_: T) -> T; /// Aborts the execution of the process. + /// + /// The stabilized version of this intrinsic is + /// [`std::process::abort`](../../std/process/fn.abort.html) pub fn abort() -> !; /// Tells LLVM that this point in the code is not reachable, enabling @@ -676,6 +679,10 @@ extern "rust-intrinsic" { pub fn min_align_of() -> usize; pub fn pref_align_of() -> usize; + /// The size of the referenced value in bytes. + /// + /// The stabilized version of this intrinsic is + /// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html). pub fn size_of_val(_: &T) -> usize; pub fn min_align_of_val(_: &T) -> usize; @@ -921,6 +928,9 @@ extern "rust-intrinsic" { /// /// If the actual type neither requires drop glue nor implements /// `Copy`, then may return `true` or `false`. + /// + /// The stabilized version of this intrinsic is + /// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html). pub fn needs_drop() -> bool; /// Calculates the offset from a pointer. @@ -1376,17 +1386,10 @@ extern "rust-intrinsic" { /// } /// # } } /// ``` - #[cfg(not(stage0))] pub fn align_offset(ptr: *const (), align: usize) -> usize; -} -#[cfg(stage0)] -/// remove me after the next release -pub unsafe fn align_offset(ptr: *const (), align: usize) -> usize { - let offset = ptr as usize % align; - if offset == 0 { - 0 - } else { - align - offset - } + /// Emits a `!nontemporal` store according to LLVM (see their docs). + /// Probably will never become stable. + #[cfg(not(stage0))] + pub fn nontemporal_store(ptr: *mut T, val: T); } diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 36bf9633b4a35..7f6d627536da3 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -9,7 +9,9 @@ // except according to those terms. use cmp::Ordering; +use ops::Try; +use super::{AlwaysOk, LoopState}; use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse}; use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev}; use super::{Zip, Sum, Product}; @@ -28,6 +30,7 @@ fn _assert_is_object_safe(_: &Iterator) {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling \ `.iter()` or a similar method"] +#[doc(spotlight)] pub trait Iterator { /// The type of the elements being iterated over. #[stable(feature = "rust1", since = "1.0.0")] @@ -518,7 +521,7 @@ pub trait Iterator { /// .for_each(|(i, x)| println!("{}:{}", i, x)); /// ``` #[inline] - #[stable(feature = "iterator_for_each", since = "1.22.0")] + #[stable(feature = "iterator_for_each", since = "1.21.0")] fn for_each(self, mut f: F) where Self: Sized, F: FnMut(Self::Item), { @@ -1337,6 +1340,78 @@ pub trait Iterator { (left, right) } + /// An iterator method that applies a function as long as it returns + /// successfully, producing a single, final value. + /// + /// `try_fold()` takes two arguments: an initial value, and a closure with + /// two arguments: an 'accumulator', and an element. The closure either + /// returns successfully, with the value that the accumulator should have + /// for the next iteration, or it returns failure, with an error value that + /// is propagated back to the caller immediately (short-circuiting). + /// + /// The initial value is the value the accumulator will have on the first + /// call. If applying the closure succeeded against every element of the + /// iterator, `try_fold()` returns the final accumulator as success. + /// + /// Folding is useful whenever you have a collection of something, and want + /// to produce a single value from it. + /// + /// # Note to Implementors + /// + /// Most of the other (forward) methods have default implementations in + /// terms of this one, so try to implement this explicitly if it can + /// do something better than the default `for` loop implementation. + /// + /// In particular, try to have this call `try_fold()` on the internal parts + /// from which this iterator is composed. If multiple calls are needed, + /// the `?` operator be convenient for chaining the accumulator value along, + /// but beware any invariants that need to be upheld before those early + /// returns. This is a `&mut self` method, so iteration needs to be + /// resumable after hitting an error here. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_try_fold)] + /// let a = [1, 2, 3]; + /// + /// // the checked sum of all of the elements of the array + /// let sum = a.iter() + /// .try_fold(0i8, |acc, &x| acc.checked_add(x)); + /// + /// assert_eq!(sum, Some(6)); + /// ``` + /// + /// Short-circuiting: + /// + /// ``` + /// #![feature(iterator_try_fold)] + /// let a = [10, 20, 30, 100, 40, 50]; + /// let mut it = a.iter(); + /// + /// // This sum overflows when adding the 100 element + /// let sum = it.try_fold(0i8, |acc, &x| acc.checked_add(x)); + /// assert_eq!(sum, None); + /// + /// // Because it short-circuited, the remaining elements are still + /// // available through the iterator. + /// assert_eq!(it.len(), 2); + /// assert_eq!(it.next(), Some(&40)); + /// ``` + #[inline] + #[unstable(feature = "iterator_try_fold", issue = "45594")] + fn try_fold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x)?; + } + Try::from_ok(accum) + } + /// An iterator method that applies a function, producing a single, final value. /// /// `fold()` takes two arguments: an initial value, and a closure with two @@ -1361,7 +1436,7 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// // the sum of all of the elements of a + /// // the sum of all of the elements of the array /// let sum = a.iter() /// .fold(0, |acc, &x| acc + x); /// @@ -1403,14 +1478,10 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - fn fold(self, init: B, mut f: F) -> B where + fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - let mut accum = init; - for x in self { - accum = f(accum, x); - } - accum + self.try_fold(init, move |acc, x| AlwaysOk(f(acc, x))).0 } /// Tests if every element of the iterator matches a predicate. @@ -1455,12 +1526,10 @@ pub trait Iterator { fn all(&mut self, mut f: F) -> bool where Self: Sized, F: FnMut(Self::Item) -> bool { - for x in self { - if !f(x) { - return false; - } - } - true + self.try_fold((), move |(), x| { + if f(x) { LoopState::Continue(()) } + else { LoopState::Break(()) } + }) == LoopState::Continue(()) } /// Tests if any element of the iterator matches a predicate. @@ -1506,12 +1575,10 @@ pub trait Iterator { Self: Sized, F: FnMut(Self::Item) -> bool { - for x in self { - if f(x) { - return true; - } - } - false + self.try_fold((), move |(), x| { + if f(x) { LoopState::Break(()) } + else { LoopState::Continue(()) } + }) == LoopState::Break(()) } /// Searches for an element of an iterator that satisfies a predicate. @@ -1562,10 +1629,10 @@ pub trait Iterator { Self: Sized, P: FnMut(&Self::Item) -> bool, { - for x in self { - if predicate(&x) { return Some(x) } - } - None + self.try_fold((), move |(), x| { + if predicate(&x) { LoopState::Break(x) } + else { LoopState::Continue(()) } + }).break_value() } /// Searches for an element in an iterator, returning its index. @@ -1623,18 +1690,17 @@ pub trait Iterator { /// /// ``` #[inline] + #[rustc_inherit_overflow_checks] #[stable(feature = "rust1", since = "1.0.0")] fn position

( + self_arg_ty: Ty<'tcx>, + is_self_ty: P + ) -> ExplicitSelf<'tcx> + where + P: Fn(Ty<'tcx>) -> bool + { + use self::ExplicitSelf::*; - layout + match self_arg_ty.sty { + _ if is_self_ty(self_arg_ty) => ByValue, + ty::TyRef(region, ty::TypeAndMut { ty, mutbl}) if is_self_ty(ty) => { + ByReference(region, mutbl) + } + ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, + _ => Other + } + } } pub fn provide(providers: &mut ty::maps::Providers) { @@ -1180,7 +1251,6 @@ pub fn provide(providers: &mut ty::maps::Providers) { is_sized_raw, is_freeze_raw, needs_drop_raw, - layout_raw, ..*providers }; } diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index df07844ccebaf..448ad4cf675c7 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -82,7 +82,8 @@ pub fn walk_shallow<'tcx>(ty: Ty<'tcx>) -> AccIntoIter> { fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { match parent_ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | - ty::TyStr | ty::TyInfer(_) | ty::TyParam(_) | ty::TyNever | ty::TyError => { + ty::TyStr | ty::TyInfer(_) | ty::TyParam(_) | ty::TyNever | ty::TyError | + ty::TyForeign(..) => { } ty::TyArray(ty, len) => { push_const(stack, len); diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 41e27fca3f320..a851ccc34bfd6 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -284,7 +284,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { ty::TyError | ty::TyStr | ty::TyNever | - ty::TyParam(_) => { + ty::TyParam(_) | + ty::TyForeign(..) => { // WfScalar, WfParameter, etc } @@ -335,14 +336,50 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { } } - ty::TyGenerator(..) | ty::TyClosure(..) => { - // the types in a closure or generator are always the types of - // local variables (or possibly references to local - // variables), we'll walk those. + ty::TyGenerator(..) => { + // Walk ALL the types in the generator: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // generators don't take arguments. + } + + ty::TyClosure(def_id, substs) => { + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. // - // (Though, local variables are probably not - // needed, as they are separately checked w/r/t - // WFedness.) + // Note that we are also skipping the generic + // types. This is consistent with the `outlives` + // code, but anyway doesn't matter: within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + subtys.skip_current_subtree(); // subtree handled by compute_projection + for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + self.compute(upvar_ty); + } } ty::TyFnDef(..) | ty::TyFnPtr(_) => { diff --git a/src/librustc/util/nodemap.rs b/src/librustc/util/nodemap.rs index c397371c5c767..674f67d5cd2f1 100644 --- a/src/librustc/util/nodemap.rs +++ b/src/librustc/util/nodemap.rs @@ -25,10 +25,12 @@ pub type ItemLocalMap = FxHashMap; pub type NodeSet = FxHashSet; pub type DefIdSet = FxHashSet; +pub type ItemLocalSet = FxHashSet; pub fn NodeMap() -> NodeMap { FxHashMap() } pub fn DefIdMap() -> DefIdMap { FxHashMap() } pub fn ItemLocalMap() -> ItemLocalMap { FxHashMap() } pub fn NodeSet() -> NodeSet { FxHashSet() } pub fn DefIdSet() -> DefIdSet { FxHashSet() } +pub fn ItemLocalSet() -> ItemLocalSet { FxHashSet() } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 214973e308586..9ff3d73f5c40e 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -17,9 +17,10 @@ use ty::{BrAnon, BrEnv, BrFresh, BrNamed}; use ty::{TyBool, TyChar, TyAdt}; use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr}; use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple}; -use ty::{TyClosure, TyGenerator, TyProjection, TyAnon}; +use ty::{TyClosure, TyGenerator, TyForeign, TyProjection, TyAnon}; use ty::{TyDynamic, TyInt, TyUint, TyInfer}; use ty::{self, Ty, TyCtxt, TypeFoldable}; +use util::nodemap::FxHashSet; use std::cell::Cell; use std::fmt; @@ -32,310 +33,543 @@ use syntax::ast::CRATE_NODE_ID; use syntax::symbol::Symbol; use hir; -pub fn verbose() -> bool { - ty::tls::with(|tcx| tcx.sess.verbose()) +macro_rules! gen_display_debug_body { + ( $with:path ) => { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut cx = PrintContext::new(); + $with(self, f, &mut cx) + } + }; } - -pub fn identify_regions() -> bool { - ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.identify_regions) +macro_rules! gen_display_debug { + ( ($($x:tt)+) $target:ty, display yes ) => { + impl<$($x)+> fmt::Display for $target { + gen_display_debug_body! { Print::print_display } + } + }; + ( () $target:ty, display yes ) => { + impl fmt::Display for $target { + gen_display_debug_body! { Print::print_display } + } + }; + ( ($($x:tt)+) $target:ty, debug yes ) => { + impl<$($x)+> fmt::Debug for $target { + gen_display_debug_body! { Print::print_debug } + } + }; + ( () $target:ty, debug yes ) => { + impl fmt::Debug for $target { + gen_display_debug_body! { Print::print_debug } + } + }; + ( $generic:tt $target:ty, $t:ident no ) => {}; } - -fn fn_sig(f: &mut fmt::Formatter, - inputs: &[Ty], - variadic: bool, - output: Ty) - -> fmt::Result { - write!(f, "(")?; - let mut inputs = inputs.iter(); - if let Some(&ty) = inputs.next() { - write!(f, "{}", ty)?; - for &ty in inputs { - write!(f, ", {}", ty)?; +macro_rules! gen_print_impl { + ( ($($x:tt)+) $target:ty, ($self:ident, $f:ident, $cx:ident) $disp:block $dbg:block ) => { + impl<$($x)+> Print for $target { + fn print(&$self, $f: &mut F, $cx: &mut PrintContext) -> fmt::Result { + if $cx.is_debug $dbg + else $disp + } } - if variadic { - write!(f, ", ...")?; + }; + ( () $target:ty, ($self:ident, $f:ident, $cx:ident) $disp:block $dbg:block ) => { + impl Print for $target { + fn print(&$self, $f: &mut F, $cx: &mut PrintContext) -> fmt::Result { + if $cx.is_debug $dbg + else $disp + } } + }; + ( $generic:tt $target:ty, + $vars:tt $gendisp:ident $disp:block $gendbg:ident $dbg:block ) => { + gen_print_impl! { $generic $target, $vars $disp $dbg } + gen_display_debug! { $generic $target, display $gendisp } + gen_display_debug! { $generic $target, debug $gendbg } } - write!(f, ")")?; - if !output.is_nil() { - write!(f, " -> {}", output)?; +} +macro_rules! define_print { + ( $generic:tt $target:ty, + $vars:tt { display $disp:block debug $dbg:block } ) => { + gen_print_impl! { $generic $target, $vars yes $disp yes $dbg } + }; + ( $generic:tt $target:ty, + $vars:tt { debug $dbg:block display $disp:block } ) => { + gen_print_impl! { $generic $target, $vars yes $disp yes $dbg } + }; + ( $generic:tt $target:ty, + $vars:tt { debug $dbg:block } ) => { + gen_print_impl! { $generic $target, $vars no { + bug!(concat!("display not implemented for ", stringify!($target))); + } yes $dbg } + }; + ( $generic:tt $target:ty, + ($self:ident, $f:ident, $cx:ident) { display $disp:block } ) => { + gen_print_impl! { $generic $target, ($self, $f, $cx) yes $disp no { + write!($f, "{:?}", $self) + } } + }; +} +macro_rules! define_print_multi { + ( [ $($generic:tt $target:ty),* ] $vars:tt $def:tt ) => { + $(define_print! { $generic $target, $vars $def })* + }; +} +macro_rules! print_inner { + ( $f:expr, $cx:expr, write ($($data:expr),+) ) => { + write!($f, $($data),+) + }; + ( $f:expr, $cx:expr, $kind:ident ($data:expr) ) => { + $data.$kind($f, $cx) + }; +} +macro_rules! print { + ( $f:expr, $cx:expr $(, $kind:ident $data:tt)+ ) => { + Ok(())$(.and_then(|_| print_inner!($f, $cx, $kind $data)))+ + }; +} + + +struct LateBoundRegionNameCollector(FxHashSet); +impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + match *r { + ty::ReLateBound(_, ty::BrNamed(_, name)) => { + self.0.insert(name); + }, + _ => {}, + } + r.super_visit_with(self) } +} - Ok(()) +#[derive(Debug)] +pub struct PrintContext { + is_debug: bool, + is_verbose: bool, + identify_regions: bool, + used_region_names: Option>, + region_index: usize, + binder_depth: usize, +} +impl PrintContext { + fn new() -> Self { + ty::tls::with_opt(|tcx| { + let (is_verbose, identify_regions) = tcx.map( + |tcx| (tcx.sess.verbose(), tcx.sess.opts.debugging_opts.identify_regions) + ).unwrap_or((false, false)); + PrintContext { + is_debug: false, + is_verbose: is_verbose, + identify_regions: identify_regions, + used_region_names: None, + region_index: 0, + binder_depth: 0, + } + }) + } + fn prepare_late_bound_region_info<'tcx, T>(&mut self, value: &ty::Binder) + where T: TypeFoldable<'tcx> + { + let mut collector = LateBoundRegionNameCollector(FxHashSet()); + value.visit_with(&mut collector); + self.used_region_names = Some(collector.0); + self.region_index = 0; + } } -pub fn parameterized(f: &mut fmt::Formatter, - substs: &subst::Substs, - mut did: DefId, - projections: &[ty::ProjectionPredicate]) - -> fmt::Result { - let key = ty::tls::with(|tcx| tcx.def_key(did)); - let mut item_name = if let Some(name) = key.disambiguated_data.data.get_opt_name() { - Some(name) - } else { - did.index = key.parent.unwrap_or_else( - || bug!("finding type for {:?}, encountered def-id {:?} with no parent", - did, did)); - parameterized(f, substs, did, projections)?; - return write!(f, "::{}", key.disambiguated_data.data.as_interned_str()); - }; +pub trait Print { + fn print(&self, f: &mut F, cx: &mut PrintContext) -> fmt::Result; + fn print_to_string(&self, cx: &mut PrintContext) -> String { + let mut result = String::new(); + let _ = self.print(&mut result, cx); + result + } + fn print_display(&self, f: &mut F, cx: &mut PrintContext) -> fmt::Result { + let old_debug = cx.is_debug; + cx.is_debug = false; + let result = self.print(f, cx); + cx.is_debug = old_debug; + result + } + fn print_display_to_string(&self, cx: &mut PrintContext) -> String { + let mut result = String::new(); + let _ = self.print_display(&mut result, cx); + result + } + fn print_debug(&self, f: &mut F, cx: &mut PrintContext) -> fmt::Result { + let old_debug = cx.is_debug; + cx.is_debug = true; + let result = self.print(f, cx); + cx.is_debug = old_debug; + result + } + fn print_debug_to_string(&self, cx: &mut PrintContext) -> String { + let mut result = String::new(); + let _ = self.print_debug(&mut result, cx); + result + } +} - let mut verbose = false; - let mut num_supplied_defaults = 0; - let mut has_self = false; - let mut num_regions = 0; - let mut num_types = 0; - let mut is_value_path = false; - let fn_trait_kind = ty::tls::with(|tcx| { - // Unfortunately, some kinds of items (e.g., closures) don't have - // generics. So walk back up the find the closest parent that DOES - // have them. - let mut item_def_id = did; - loop { - let key = tcx.def_key(item_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) => { - break; - } - DefPathData::ValueNs(_) | DefPathData::EnumVariant(_) => { - is_value_path = true; - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - item_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", did, item_def_id); - }); - } +impl PrintContext { + fn fn_sig(&mut self, + f: &mut F, + inputs: &[Ty], + variadic: bool, + output: Ty) + -> fmt::Result { + write!(f, "(")?; + let mut inputs = inputs.iter(); + if let Some(&ty) = inputs.next() { + print!(f, self, print_display(ty))?; + for &ty in inputs { + print!(f, self, write(", "), print_display(ty))?; + } + if variadic { + write!(f, ", ...")?; } } - let mut generics = tcx.generics_of(item_def_id); - let mut path_def_id = did; - verbose = tcx.sess.verbose(); - has_self = generics.has_self; - - let mut child_types = 0; - if let Some(def_id) = generics.parent { - // Methods. - assert!(is_value_path); - child_types = generics.types.len(); - generics = tcx.generics_of(def_id); - num_regions = generics.regions.len(); - num_types = generics.types.len(); + write!(f, ")")?; + if !output.is_nil() { + print!(f, self, write(" -> "), print_display(output))?; + } - if has_self { - write!(f, "<{} as ", substs.type_at(0))?; - } + Ok(()) + } - path_def_id = def_id; + fn parameterized(&mut self, + f: &mut F, + substs: &subst::Substs, + mut did: DefId, + projections: &[ty::ProjectionPredicate]) + -> fmt::Result { + let key = ty::tls::with(|tcx| tcx.def_key(did)); + let mut item_name = if let Some(name) = key.disambiguated_data.data.get_opt_name() { + Some(name) } else { - item_name = None; + did.index = key.parent.unwrap_or_else( + || bug!("finding type for {:?}, encountered def-id {:?} with no parent", + did, did)); + self.parameterized(f, substs, did, projections)?; + return write!(f, "::{}", key.disambiguated_data.data.as_interned_str()); + }; - if is_value_path { - // Functions. - assert_eq!(has_self, false); - } else { - // Types and traits. + let verbose = self.is_verbose; + let mut num_supplied_defaults = 0; + let mut has_self = false; + let mut num_regions = 0; + let mut num_types = 0; + let mut is_value_path = false; + let fn_trait_kind = ty::tls::with(|tcx| { + // Unfortunately, some kinds of items (e.g., closures) don't have + // generics. So walk back up the find the closest parent that DOES + // have them. + let mut item_def_id = did; + loop { + let key = tcx.def_key(item_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) => { + break; + } + DefPathData::ValueNs(_) | DefPathData::EnumVariant(_) => { + is_value_path = true; + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + item_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", did, item_def_id); + }); + } + } + } + let mut generics = tcx.generics_of(item_def_id); + let mut path_def_id = did; + has_self = generics.has_self; + + let mut child_types = 0; + if let Some(def_id) = generics.parent { + // Methods. + assert!(is_value_path); + child_types = generics.types.len(); + generics = tcx.generics_of(def_id); num_regions = generics.regions.len(); num_types = generics.types.len(); + + if has_self { + print!(f, self, write("<"), print_display(substs.type_at(0)), write(" as "))?; + } + + path_def_id = def_id; + } else { + item_name = None; + + if is_value_path { + // Functions. + assert_eq!(has_self, false); + } else { + // Types and traits. + num_regions = generics.regions.len(); + num_types = generics.types.len(); + } } - } - if !verbose { - if generics.types.last().map_or(false, |def| def.has_default) { - if let Some(substs) = tcx.lift(&substs) { - let tps = substs.types().rev().skip(child_types); - for (def, actual) in generics.types.iter().rev().zip(tps) { - if !def.has_default { - break; - } - if tcx.type_of(def.def_id).subst(tcx, substs) != actual { - break; + if !verbose { + if generics.types.last().map_or(false, |def| def.has_default) { + if let Some(substs) = tcx.lift(&substs) { + let tps = substs.types().rev().skip(child_types); + for (def, actual) in generics.types.iter().rev().zip(tps) { + if !def.has_default { + break; + } + if tcx.type_of(def.def_id).subst(tcx, substs) != actual { + break; + } + num_supplied_defaults += 1; } - num_supplied_defaults += 1; } } } - } - write!(f, "{}", tcx.item_path_str(path_def_id))?; - Ok(tcx.lang_items().fn_trait_kind(path_def_id)) - })?; + print!(f, self, write("{}", tcx.item_path_str(path_def_id)))?; + Ok(tcx.lang_items().fn_trait_kind(path_def_id)) + })?; - if !verbose && fn_trait_kind.is_some() && projections.len() == 1 { - let projection_ty = projections[0].ty; - if let TyTuple(ref args, _) = substs.type_at(1).sty { - return fn_sig(f, args, false, projection_ty); + if !verbose && fn_trait_kind.is_some() && projections.len() == 1 { + let projection_ty = projections[0].ty; + if let TyTuple(ref args, _) = substs.type_at(1).sty { + return self.fn_sig(f, args, false, projection_ty); + } } - } - let empty = Cell::new(true); - let start_or_continue = |f: &mut fmt::Formatter, start: &str, cont: &str| { - if empty.get() { - empty.set(false); - write!(f, "{}", start) - } else { - write!(f, "{}", cont) - } - }; + let empty = Cell::new(true); + let start_or_continue = |f: &mut F, start: &str, cont: &str| { + if empty.get() { + empty.set(false); + write!(f, "{}", start) + } else { + write!(f, "{}", cont) + } + }; - let print_regions = |f: &mut fmt::Formatter, start: &str, skip, count| { - // Don't print any regions if they're all erased. - let regions = || substs.regions().skip(skip).take(count); - if regions().all(|r: ty::Region| *r == ty::ReErased) { - return Ok(()); - } + let print_regions = |f: &mut F, start: &str, skip, count| { + // Don't print any regions if they're all erased. + let regions = || substs.regions().skip(skip).take(count); + if regions().all(|r: ty::Region| *r == ty::ReErased) { + return Ok(()); + } - for region in regions() { - let region: ty::Region = region; - start_or_continue(f, start, ", ")?; - if verbose { - write!(f, "{:?}", region)?; - } else { - let s = region.to_string(); - if s.is_empty() { - // This happens when the value of the region - // parameter is not easily serialized. This may be - // because the user omitted it in the first place, - // or because it refers to some block in the code, - // etc. I'm not sure how best to serialize this. - write!(f, "'_")?; + for region in regions() { + let region: ty::Region = region; + start_or_continue(f, start, ", ")?; + if verbose { + write!(f, "{:?}", region)?; } else { - write!(f, "{}", s)?; + let s = region.to_string(); + if s.is_empty() { + // This happens when the value of the region + // parameter is not easily serialized. This may be + // because the user omitted it in the first place, + // or because it refers to some block in the code, + // etc. I'm not sure how best to serialize this. + write!(f, "'_")?; + } else { + write!(f, "{}", s)?; + } } } + + Ok(()) + }; + + print_regions(f, "<", 0, num_regions)?; + + let tps = substs.types().take(num_types - num_supplied_defaults) + .skip(has_self as usize); + + for ty in tps { + start_or_continue(f, "<", ", ")?; + ty.print_display(f, self)?; } - Ok(()) - }; + for projection in projections { + start_or_continue(f, "<", ", ")?; + ty::tls::with(|tcx| + print!(f, self, + write("{}=", + tcx.associated_item(projection.projection_ty.item_def_id).name), + print_display(projection.ty)) + )?; + } - print_regions(f, "<", 0, num_regions)?; + start_or_continue(f, "", ">")?; - let tps = substs.types().take(num_types - num_supplied_defaults) - .skip(has_self as usize); + // For values, also print their name and type parameters. + if is_value_path { + empty.set(true); - for ty in tps { - start_or_continue(f, "<", ", ")?; - write!(f, "{}", ty)?; - } + if has_self { + write!(f, ">")?; + } - for projection in projections { - start_or_continue(f, "<", ", ")?; - ty::tls::with(|tcx| - write!(f, "{}={}", - tcx.associated_item(projection.projection_ty.item_def_id).name, - projection.ty) - )?; - } + if let Some(item_name) = item_name { + write!(f, "::{}", item_name)?; + } - start_or_continue(f, "", ">")?; + print_regions(f, "::<", num_regions, usize::MAX)?; - // For values, also print their name and type parameters. - if is_value_path { - empty.set(true); + // FIXME: consider being smart with defaults here too + for ty in substs.types().skip(num_types) { + start_or_continue(f, "::<", ", ")?; + ty.print_display(f, self)?; + } - if has_self { - write!(f, ">")?; + start_or_continue(f, "", ">")?; } - if let Some(item_name) = item_name { - write!(f, "::{}", item_name)?; + Ok(()) + } + + fn in_binder<'a, 'gcx, 'tcx, T, U, F>(&mut self, + f: &mut F, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + original: &ty::Binder, + lifted: Option>) -> fmt::Result + where T: Print, U: Print + TypeFoldable<'tcx>, F: fmt::Write + { + fn name_by_region_index(index: usize) -> Symbol { + match index { + 0 => Symbol::intern("'r"), + 1 => Symbol::intern("'s"), + i => Symbol::intern(&format!("'t{}", i-2)), + } } - print_regions(f, "::<", num_regions, usize::MAX)?; + // Replace any anonymous late-bound regions with named + // variants, using gensym'd identifiers, so that we can + // clearly differentiate between named and unnamed regions in + // the output. We'll probably want to tweak this over time to + // decide just how much information to give. + let value = if let Some(v) = lifted { + v + } else { + return original.0.print_display(f, self); + }; - // FIXME: consider being smart with defaults here too - for ty in substs.types().skip(num_types) { - start_or_continue(f, "::<", ", ")?; - write!(f, "{}", ty)?; + if self.binder_depth == 0 { + self.prepare_late_bound_region_info(&value); } - start_or_continue(f, "", ">")?; + let mut empty = true; + let mut start_or_continue = |f: &mut F, start: &str, cont: &str| { + if empty { + empty = false; + write!(f, "{}", start) + } else { + write!(f, "{}", cont) + } + }; + + let old_region_index = self.region_index; + let mut region_index = old_region_index; + let new_value = tcx.replace_late_bound_regions(&value, |br| { + let _ = start_or_continue(f, "for<", ", "); + let br = match br { + ty::BrNamed(_, name) => { + let _ = write!(f, "{}", name); + br + } + ty::BrAnon(_) | + ty::BrFresh(_) | + ty::BrEnv => { + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.is_name_used(&name) { + break name; + } + }; + let _ = write!(f, "{}", name); + ty::BrNamed(tcx.hir.local_def_id(CRATE_NODE_ID), + name) + } + }; + tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), br)) + }).0; + start_or_continue(f, "", "> ")?; + + // Push current state to gcx, and restore after writing new_value. + self.binder_depth += 1; + self.region_index = region_index; + let result = new_value.print_display(f, self); + self.region_index = old_region_index; + self.binder_depth -= 1; + result } - Ok(()) + fn is_name_used(&self, name: &Symbol) -> bool { + match self.used_region_names { + Some(ref names) => names.contains(name), + None => false, + } + } } -fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - original: &ty::Binder, - lifted: Option>) -> fmt::Result - where T: fmt::Display, U: fmt::Display + TypeFoldable<'tcx> -{ - // Replace any anonymous late-bound regions with named - // variants, using gensym'd identifiers, so that we can - // clearly differentiate between named and unnamed regions in - // the output. We'll probably want to tweak this over time to - // decide just how much information to give. - let value = if let Some(v) = lifted { - v - } else { - return write!(f, "{}", original.0); - }; +pub fn verbose() -> bool { + ty::tls::with(|tcx| tcx.sess.verbose()) +} - let mut empty = true; - let mut start_or_continue = |f: &mut fmt::Formatter, start: &str, cont: &str| { - if empty { - empty = false; - write!(f, "{}", start) - } else { - write!(f, "{}", cont) - } - }; +pub fn identify_regions() -> bool { + ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.identify_regions) +} + +pub fn parameterized(f: &mut F, + substs: &subst::Substs, + did: DefId, + projections: &[ty::ProjectionPredicate]) + -> fmt::Result { + PrintContext::new().parameterized(f, substs, did, projections) +} - let new_value = tcx.replace_late_bound_regions(&value, |br| { - let _ = start_or_continue(f, "for<", ", "); - let br = match br { - ty::BrNamed(_, name) => { - let _ = write!(f, "{}", name); - br - } - ty::BrAnon(_) | - ty::BrFresh(_) | - ty::BrEnv => { - let name = Symbol::intern("'r"); - let _ = write!(f, "{}", name); - ty::BrNamed(tcx.hir.local_def_id(CRATE_NODE_ID), - name) - } - }; - tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), br)) - }).0; - start_or_continue(f, "", "> ")?; - write!(f, "{}", new_value) +impl<'a, T: Print> Print for &'a T { + fn print(&self, f: &mut F, cx: &mut PrintContext) -> fmt::Result { + (*self).print(f, cx) + } } -impl<'tcx> fmt::Display for &'tcx ty::Slice> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Generate the main trait ref, including associated types. - ty::tls::with(|tcx| { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = tcx.mk_infer(ty::FreshTy(0)); - - if let Some(p) = self.principal() { - let principal = tcx.lift(&p).expect("could not lift TraitRef for printing") - .with_self_ty(tcx, dummy_self); - let projections = self.projection_bounds().map(|p| { - tcx.lift(&p) - .expect("could not lift projection for printing") - .with_self_ty(tcx, dummy_self) - }).collect::>(); - parameterized(f, principal.substs, principal.def_id, &projections)?; - } +define_print! { + ('tcx) &'tcx ty::Slice>, (self, f, cx) { + display { + // Generate the main trait ref, including associated types. + ty::tls::with(|tcx| { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = tcx.mk_infer(ty::FreshTy(0)); + + if let Some(p) = self.principal() { + let principal = tcx.lift(&p).expect("could not lift TraitRef for printing") + .with_self_ty(tcx, dummy_self); + let projections = self.projection_bounds().map(|p| { + tcx.lift(&p) + .expect("could not lift projection for printing") + .with_self_ty(tcx, dummy_self) + }).collect::>(); + cx.parameterized(f, principal.substs, principal.def_id, &projections)?; + } - // Builtin bounds. - for did in self.auto_traits() { - write!(f, " + {}", tcx.item_path_str(did))?; - } + // Builtin bounds. + for did in self.auto_traits() { + write!(f, " + {}", tcx.item_path_str(did))?; + } - Ok(()) - })?; + Ok(()) + })?; - Ok(()) + Ok(()) + } } } @@ -357,42 +591,6 @@ impl fmt::Debug for ty::RegionParameterDef { } } -impl<'tcx> fmt::Debug for ty::TyS<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", *self) - } -} - -impl<'tcx> fmt::Display for ty::TypeAndMut<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", - if self.mutbl == hir::MutMutable { "mut " } else { "" }, - self.ty) - } -} - -impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // when printing out the debug representation, we don't need - // to enumerate the `for<...>` etc because the debruijn index - // tells you everything you need to know. - write!(f, "<{:?} as {}>", self.self_ty(), *self) - } -} - -impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| { - let dummy_self = tcx.mk_infer(ty::FreshTy(0)); - - let trait_ref = tcx.lift(&ty::Binder(*self)) - .expect("could not lift TraitRef for printing") - .with_self_ty(tcx, dummy_self).0; - parameterized(f, trait_ref.substs, trait_ref.def_id, &[]) - }) - } -} - impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ty::tls::with(|tcx| { @@ -409,196 +607,226 @@ impl fmt::Debug for ty::AdtDef { } } -impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> { +impl<'tcx> fmt::Debug for ty::ClosureUpvar<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} -> {}", self.kind, self.target) + write!(f, "ClosureUpvar({:?},{:?})", + self.def, + self.ty) } } -impl<'tcx> fmt::Debug for ty::Predicate<'tcx> { +impl fmt::Debug for ty::UpvarId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::Predicate::Trait(ref a) => write!(f, "{:?}", a), - ty::Predicate::Equate(ref pair) => write!(f, "{:?}", pair), - ty::Predicate::Subtype(ref pair) => write!(f, "{:?}", pair), - ty::Predicate::RegionOutlives(ref pair) => write!(f, "{:?}", pair), - ty::Predicate::TypeOutlives(ref pair) => write!(f, "{:?}", pair), - ty::Predicate::Projection(ref pair) => write!(f, "{:?}", pair), - ty::Predicate::WellFormed(ty) => write!(f, "WF({:?})", ty), - ty::Predicate::ObjectSafe(trait_def_id) => { - write!(f, "ObjectSafe({:?})", trait_def_id) - } - ty::Predicate::ClosureKind(closure_def_id, kind) => { - write!(f, "ClosureKind({:?}, {:?})", closure_def_id, kind) - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) - } - } + write!(f, "UpvarId({:?};`{}`;{:?})", + self.var_id, + ty::tls::with(|tcx| tcx.hir.name(tcx.hir.hir_to_node_id(self.var_id))), + self.closure_expr_id) } } -impl fmt::Display for ty::BoundRegion { +impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if verbose() { - return write!(f, "{:?}", *self); + write!(f, "UpvarBorrow({:?}, {:?})", + self.kind, self.region) + } +} + +define_print! { + ('tcx) ty::TypeAndMut<'tcx>, (self, f, cx) { + display { + print!(f, cx, + write("{}", if self.mutbl == hir::MutMutable { "mut " } else { "" }), + print(self.ty)) } + } +} - match *self { - BrNamed(_, name) => write!(f, "{}", name), - BrAnon(_) | BrFresh(_) | BrEnv => Ok(()) +define_print! { + ('tcx) ty::ExistentialTraitRef<'tcx>, (self, f, cx) { + debug { + ty::tls::with(|tcx| { + let dummy_self = tcx.mk_infer(ty::FreshTy(0)); + + let trait_ref = tcx.lift(&ty::Binder(*self)) + .expect("could not lift TraitRef for printing") + .with_self_ty(tcx, dummy_self).0; + cx.parameterized(f, trait_ref.substs, trait_ref.def_id, &[]) + }) } } } -impl fmt::Debug for ty::BoundRegion { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - BrAnon(n) => write!(f, "BrAnon({:?})", n), - BrFresh(n) => write!(f, "BrFresh({:?})", n), - BrNamed(did, name) => { - write!(f, "BrNamed({:?}:{:?}, {:?})", - did.krate, did.index, name) - } - BrEnv => "BrEnv".fmt(f), +define_print! { + ('tcx) ty::adjustment::Adjustment<'tcx>, (self, f, cx) { + debug { + print!(f, cx, write("{:?} -> ", self.kind), print(self.target)) } } } -impl fmt::Debug for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::ReEarlyBound(ref data) => { - write!(f, "ReEarlyBound({}, {})", - data.index, - data.name) +define_print! { + () ty::BoundRegion, (self, f, cx) { + display { + if cx.is_verbose { + return self.print_debug(f, cx); } - ty::ReLateBound(binder_id, ref bound_region) => { - write!(f, "ReLateBound({:?}, {:?})", - binder_id, - bound_region) + match *self { + BrNamed(_, name) => write!(f, "{}", name), + BrAnon(_) | BrFresh(_) | BrEnv => Ok(()) } + } + debug { + return match *self { + BrAnon(n) => write!(f, "BrAnon({:?})", n), + BrFresh(n) => write!(f, "BrFresh({:?})", n), + BrNamed(did, name) => { + write!(f, "BrNamed({:?}:{:?}, {:?})", + did.krate, did.index, name) + } + BrEnv => write!(f, "BrEnv"), + }; + } + } +} - ty::ReFree(ref fr) => write!(f, "{:?}", fr), - - ty::ReScope(id) => { - write!(f, "ReScope({:?})", id) +define_print! { + () ty::RegionKind, (self, f, cx) { + display { + if cx.is_verbose { + return self.print_debug(f, cx); } - ty::ReStatic => write!(f, "ReStatic"), - - ty::ReVar(ref vid) => { - write!(f, "{:?}", vid) + // These printouts are concise. They do not contain all the information + // the user might want to diagnose an error, but there is basically no way + // to fit that into a short string. Hence the recommendation to use + // `explain_region()` or `note_and_explain_region()`. + match *self { + ty::ReEarlyBound(ref data) => { + write!(f, "{}", data.name) + } + ty::ReLateBound(_, br) | + ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | + ty::ReSkolemized(_, br) => { + write!(f, "{}", br) + } + ty::ReScope(scope) if cx.identify_regions => { + match scope.data() { + region::ScopeData::Node(id) => + write!(f, "'{}s", id.as_usize()), + region::ScopeData::CallSite(id) => + write!(f, "'{}cs", id.as_usize()), + region::ScopeData::Arguments(id) => + write!(f, "'{}as", id.as_usize()), + region::ScopeData::Destruction(id) => + write!(f, "'{}ds", id.as_usize()), + region::ScopeData::Remainder(BlockRemainder + { block, first_statement_index }) => + write!(f, "'{}_{}rs", block.as_usize(), first_statement_index.index()), + } + } + ty::ReVar(region_vid) if cx.identify_regions => { + write!(f, "'{}rv", region_vid.index()) + } + ty::ReScope(_) | + ty::ReVar(_) | + ty::ReErased => Ok(()), + ty::ReStatic => write!(f, "'static"), + ty::ReEmpty => write!(f, "'"), } + } + debug { + match *self { + ty::ReEarlyBound(ref data) => { + write!(f, "ReEarlyBound({}, {})", + data.index, + data.name) + } - ty::ReSkolemized(id, ref bound_region) => { - write!(f, "ReSkolemized({}, {:?})", id.index, bound_region) - } + ty::ReLateBound(binder_id, ref bound_region) => { + write!(f, "ReLateBound({:?}, {:?})", + binder_id, + bound_region) + } - ty::ReEmpty => write!(f, "ReEmpty"), + ty::ReFree(ref fr) => write!(f, "{:?}", fr), - ty::ReErased => write!(f, "ReErased") - } - } -} + ty::ReScope(id) => { + write!(f, "ReScope({:?})", id) + } -impl<'tcx> fmt::Debug for ty::ClosureUpvar<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ClosureUpvar({:?},{:?})", - self.def, - self.ty) - } -} + ty::ReStatic => write!(f, "ReStatic"), -impl fmt::Display for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if verbose() { - return write!(f, "{:?}", *self); - } + ty::ReVar(ref vid) => { + write!(f, "{:?}", vid) + } - // These printouts are concise. They do not contain all the information - // the user might want to diagnose an error, but there is basically no way - // to fit that into a short string. Hence the recommendation to use - // `explain_region()` or `note_and_explain_region()`. - match *self { - ty::ReEarlyBound(ref data) => { - write!(f, "{}", data.name) - } - ty::ReLateBound(_, br) | - ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | - ty::ReSkolemized(_, br) => { - write!(f, "{}", br) - } - ty::ReScope(scope) if identify_regions() => { - match scope.data() { - region::ScopeData::Node(id) => - write!(f, "'{}s", id.as_usize()), - region::ScopeData::CallSite(id) => - write!(f, "'{}cs", id.as_usize()), - region::ScopeData::Arguments(id) => - write!(f, "'{}as", id.as_usize()), - region::ScopeData::Destruction(id) => - write!(f, "'{}ds", id.as_usize()), - region::ScopeData::Remainder(BlockRemainder { block, first_statement_index }) => - write!(f, "'{}_{}rs", block.as_usize(), first_statement_index.index()), + ty::ReSkolemized(id, ref bound_region) => { + write!(f, "ReSkolemized({}, {:?})", id.index, bound_region) } + + ty::ReEmpty => write!(f, "ReEmpty"), + + ty::ReErased => write!(f, "ReErased") } - ty::ReVar(region_vid) if identify_regions() => { - write!(f, "'{}rv", region_vid.index) - } - ty::ReScope(_) | - ty::ReVar(_) | - ty::ReErased => Ok(()), - ty::ReStatic => write!(f, "'static"), - ty::ReEmpty => write!(f, "'"), } } } -impl fmt::Debug for ty::FreeRegion { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ReFree({:?}, {:?})", - self.scope, self.bound_region) +define_print! { + () ty::FreeRegion, (self, f, cx) { + debug { + write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) + } } } -impl fmt::Debug for ty::Variance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - ty::Covariant => "+", - ty::Contravariant => "-", - ty::Invariant => "o", - ty::Bivariant => "*", - }) +define_print! { + () ty::Variance, (self, f, cx) { + debug { + f.write_str(match *self { + ty::Covariant => "+", + ty::Contravariant => "-", + ty::Invariant => "o", + ty::Bivariant => "*", + }) + } } } -impl<'tcx> fmt::Debug for ty::GenericPredicates<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "GenericPredicates({:?})", self.predicates) +define_print! { + ('tcx) ty::GenericPredicates<'tcx>, (self, f, cx) { + debug { + write!(f, "GenericPredicates({:?})", self.predicates) + } } } -impl<'tcx> fmt::Debug for ty::InstantiatedPredicates<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "InstantiatedPredicates({:?})", - self.predicates) +define_print! { + ('tcx) ty::InstantiatedPredicates<'tcx>, (self, f, cx) { + debug { + write!(f, "InstantiatedPredicates({:?})", self.predicates) + } } } -impl<'tcx> fmt::Display for ty::FnSig<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.unsafety == hir::Unsafety::Unsafe { - write!(f, "unsafe ")?; - } +define_print! { + ('tcx) ty::FnSig<'tcx>, (self, f, cx) { + display { + if self.unsafety == hir::Unsafety::Unsafe { + write!(f, "unsafe ")?; + } - if self.abi != Abi::Rust { - write!(f, "extern {} ", self.abi)?; - } + if self.abi != Abi::Rust { + write!(f, "extern {} ", self.abi)?; + } - write!(f, "fn")?; - fn_sig(f, self.inputs(), self.variadic, self.output()) + write!(f, "fn")?; + cx.fn_sig(f, self.inputs(), self.variadic, self.output()) + } + debug { + write!(f, "({:?}; variadic: {})->{:?}", self.inputs(), self.variadic, self.output()) + } } } @@ -622,25 +850,31 @@ impl fmt::Debug for ty::FloatVid { impl fmt::Debug for ty::RegionVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "'_#{}r", self.index) + write!(f, "'_#{}r", self.index()) } } -impl<'tcx> fmt::Debug for ty::FnSig<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "({:?}; variadic: {})->{:?}", self.inputs(), self.variadic, self.output()) - } -} - -impl fmt::Debug for ty::InferTy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::TyVar(ref v) => v.fmt(f), - ty::IntVar(ref v) => v.fmt(f), - ty::FloatVar(ref v) => v.fmt(f), - ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v) +define_print! { + () ty::InferTy, (self, f, cx) { + display { + match *self { + ty::TyVar(_) => write!(f, "_"), + ty::IntVar(_) => write!(f, "{}", "{integer}"), + ty::FloatVar(_) => write!(f, "{}", "{float}"), + ty::FreshTy(v) => write!(f, "FreshTy({})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) + } + } + debug { + match *self { + ty::TyVar(ref v) => write!(f, "{:?}", v), + ty::IntVar(ref v) => write!(f, "{:?}", v), + ty::FloatVar(ref v) => write!(f, "{:?}", v), + ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v) + } } } } @@ -665,406 +899,398 @@ impl fmt::Debug for ty::IntVarValue { } }*/ -impl<'tcx> fmt::Display for ty::Binder<&'tcx ty::Slice>> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder, ty::Region<'tcx>>> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) - } -} - -impl<'tcx> fmt::Display for ty::Binder, - ty::Region<'tcx>>> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) +define_print_multi! { + [ + ('tcx) ty::Binder<&'tcx ty::Slice>>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder>, + ('tcx) ty::Binder, ty::Region<'tcx>>>, + ('tcx) ty::Binder, ty::Region<'tcx>>> + ] + (self, f, cx) { + display { + ty::tls::with(|tcx| cx.in_binder(f, tcx, self, tcx.lift(self))) + } } } -impl<'tcx> fmt::Display for ty::TraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - parameterized(f, self.substs, self.def_id, &[]) +define_print! { + ('tcx) ty::TraitRef<'tcx>, (self, f, cx) { + display { + cx.parameterized(f, self.substs, self.def_id, &[]) + } + debug { + // when printing out the debug representation, we don't need + // to enumerate the `for<...>` etc because the debruijn index + // tells you everything you need to know. + print!(f, cx, + write("<"), + print(self.self_ty()), + write(" as "))?; + cx.parameterized(f, self.substs, self.def_id, &[])?; + write!(f, ">") + } } } -impl<'tcx> fmt::Display for ty::GeneratorInterior<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.witness.fmt(f) +define_print! { + ('tcx) ty::GeneratorInterior<'tcx>, (self, f, cx) { + display { + self.witness.print(f, cx) + } } } -impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - TyBool => write!(f, "bool"), - TyChar => write!(f, "char"), - TyInt(t) => write!(f, "{}", t.ty_to_string()), - TyUint(t) => write!(f, "{}", t.ty_to_string()), - TyFloat(t) => write!(f, "{}", t.ty_to_string()), - TyRawPtr(ref tm) => { - write!(f, "*{} {}", match tm.mutbl { - hir::MutMutable => "mut", - hir::MutImmutable => "const", - }, tm.ty) - } - TyRef(r, ref tm) => { - write!(f, "&")?; - let s = r.to_string(); - write!(f, "{}", s)?; - if !s.is_empty() { - write!(f, " ")?; +define_print! { + ('tcx) ty::TypeVariants<'tcx>, (self, f, cx) { + display { + match *self { + TyBool => write!(f, "bool"), + TyChar => write!(f, "char"), + TyInt(t) => write!(f, "{}", t.ty_to_string()), + TyUint(t) => write!(f, "{}", t.ty_to_string()), + TyFloat(t) => write!(f, "{}", t.ty_to_string()), + TyRawPtr(ref tm) => { + write!(f, "*{} ", match tm.mutbl { + hir::MutMutable => "mut", + hir::MutImmutable => "const", + })?; + tm.ty.print(f, cx) } - write!(f, "{}", tm) - } - TyNever => write!(f, "!"), - TyTuple(ref tys, _) => { - write!(f, "(")?; - let mut tys = tys.iter(); - if let Some(&ty) = tys.next() { - write!(f, "{},", ty)?; + TyRef(r, ref tm) => { + write!(f, "&")?; + let s = r.print_to_string(cx); + write!(f, "{}", s)?; + if !s.is_empty() { + write!(f, " ")?; + } + tm.print(f, cx) + } + TyNever => write!(f, "!"), + TyTuple(ref tys, _) => { + write!(f, "(")?; + let mut tys = tys.iter(); if let Some(&ty) = tys.next() { - write!(f, " {}", ty)?; - for &ty in tys { - write!(f, ", {}", ty)?; + print!(f, cx, print(ty), write(","))?; + if let Some(&ty) = tys.next() { + print!(f, cx, write(" "), print(ty))?; + for &ty in tys { + print!(f, cx, write(", "), print(ty))?; + } } } + write!(f, ")") } - write!(f, ")") - } - TyFnDef(def_id, substs) => { - ty::tls::with(|tcx| { - let mut sig = tcx.fn_sig(def_id); - if let Some(substs) = tcx.lift(&substs) { - sig = sig.subst(tcx, substs); + TyFnDef(def_id, substs) => { + ty::tls::with(|tcx| { + let mut sig = tcx.fn_sig(def_id); + if let Some(substs) = tcx.lift(&substs) { + sig = sig.subst(tcx, substs); + } + print!(f, cx, print(sig), write(" {{")) + })?; + cx.parameterized(f, substs, def_id, &[])?; + write!(f, "}}") + } + TyFnPtr(ref bare_fn) => { + bare_fn.print(f, cx) + } + TyInfer(infer_ty) => write!(f, "{}", infer_ty), + TyError => write!(f, "[type error]"), + TyParam(ref param_ty) => write!(f, "{}", param_ty), + TyAdt(def, substs) => cx.parameterized(f, substs, def.did, &[]), + TyDynamic(data, r) => { + data.print(f, cx)?; + let r = r.print_to_string(cx); + if !r.is_empty() { + write!(f, " + {}", r) + } else { + Ok(()) } - write!(f, "{} {{", sig.0) - })?; - parameterized(f, substs, def_id, &[])?; - write!(f, "}}") - } - TyFnPtr(ref bare_fn) => { - write!(f, "{}", bare_fn.0) - } - TyInfer(infer_ty) => write!(f, "{}", infer_ty), - TyError => write!(f, "[type error]"), - TyParam(ref param_ty) => write!(f, "{}", param_ty), - TyAdt(def, substs) => parameterized(f, substs, def.did, &[]), - TyDynamic(data, r) => { - write!(f, "{}", data)?; - let r = r.to_string(); - if !r.is_empty() { - write!(f, " + {}", r) - } else { - Ok(()) } - } - TyProjection(ref data) => write!(f, "{}", data), - TyAnon(def_id, substs) => { - ty::tls::with(|tcx| { - // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, - // by looking up the projections associated with the def_id. - let predicates_of = tcx.predicates_of(def_id); - let substs = tcx.lift(&substs).unwrap_or_else(|| { - tcx.intern_substs(&[]) - }); - let bounds = predicates_of.instantiate(tcx, substs); - - let mut first = true; - let mut is_sized = false; - write!(f, "impl")?; - for predicate in bounds.predicates { - if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() { - // Don't print +Sized, but rather +?Sized if absent. - if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { - is_sized = true; - continue; - } + TyForeign(def_id) => parameterized(f, subst::Substs::empty(), def_id, &[]), + TyProjection(ref data) => data.print(f, cx), + TyAnon(def_id, substs) => { + if cx.is_verbose { + return write!(f, "TyAnon({:?}, {:?})", def_id, substs); + } - write!(f, "{}{}", if first { " " } else { "+" }, trait_ref)?; - first = false; + ty::tls::with(|tcx| { + // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, + // by looking up the projections associated with the def_id. + let predicates_of = tcx.predicates_of(def_id); + let substs = tcx.lift(&substs).unwrap_or_else(|| { + tcx.intern_substs(&[]) + }); + let bounds = predicates_of.instantiate(tcx, substs); + + let mut first = true; + let mut is_sized = false; + write!(f, "impl")?; + for predicate in bounds.predicates { + if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() { + // Don't print +Sized, but rather +?Sized if absent. + if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { + is_sized = true; + continue; + } + + print!(f, cx, + write("{}", if first { " " } else { "+" }), + print(trait_ref))?; + first = false; + } } - } - if !is_sized { - write!(f, "{}?Sized", if first { " " } else { "+" })?; - } - Ok(()) - }) - } - TyStr => write!(f, "str"), - TyGenerator(did, substs, interior) => ty::tls::with(|tcx| { - let upvar_tys = substs.upvar_tys(did, tcx); - write!(f, "[generator")?; - - if let Some(node_id) = tcx.hir.as_local_node_id(did) { - write!(f, "@{:?}", tcx.hir.span(node_id))?; - let mut sep = " "; - tcx.with_freevars(node_id, |freevars| { - for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) { - write!(f, - "{}{}:{}", - sep, - tcx.hir.name(freevar.var_id()), - upvar_ty)?; - sep = ", "; + if !is_sized { + write!(f, "{}?Sized", if first { " " } else { "+" })?; } Ok(()) - })? - } else { - // cross-crate closure types should only be - // visible in trans bug reports, I imagine. - write!(f, "@{:?}", did)?; - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - write!(f, "{}{}:{}", sep, index, upvar_ty)?; - sep = ", "; - } + }) } + TyStr => write!(f, "str"), + TyGenerator(did, substs, interior) => ty::tls::with(|tcx| { + let upvar_tys = substs.upvar_tys(did, tcx); + write!(f, "[generator")?; - write!(f, " {}", interior)?; - - write!(f, "]") - }), - TyClosure(did, substs) => ty::tls::with(|tcx| { - let upvar_tys = substs.upvar_tys(did, tcx); - write!(f, "[closure")?; - - if let Some(node_id) = tcx.hir.as_local_node_id(did) { - if tcx.sess.opts.debugging_opts.span_free_formats { - write!(f, "@{:?}", node_id)?; - } else { + if let Some(node_id) = tcx.hir.as_local_node_id(did) { write!(f, "@{:?}", tcx.hir.span(node_id))?; - } - let mut sep = " "; - tcx.with_freevars(node_id, |freevars| { - for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) { - write!(f, - "{}{}:{}", - sep, - tcx.hir.name(freevar.var_id()), - upvar_ty)?; + let mut sep = " "; + tcx.with_freevars(node_id, |freevars| { + for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) { + print!(f, cx, + write("{}{}:", + sep, + tcx.hir.name(freevar.var_id())), + print(upvar_ty))?; + sep = ", "; + } + Ok(()) + })? + } else { + // cross-crate closure types should only be + // visible in trans bug reports, I imagine. + write!(f, "@{:?}", did)?; + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + print!(f, cx, + write("{}{}:", sep, index), + print(upvar_ty))?; sep = ", "; } - Ok(()) - })? - } else { - // cross-crate closure types should only be - // visible in trans bug reports, I imagine. - write!(f, "@{:?}", did)?; - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - write!(f, "{}{}:{}", sep, index, upvar_ty)?; - sep = ", "; } - } - write!(f, "]") - }), - TyArray(ty, sz) => { - write!(f, "[{}; ", ty)?; - match sz.val { - ConstVal::Integral(ConstInt::Usize(sz)) => { - write!(f, "{}", sz)?; - } - ConstVal::Unevaluated(_def_id, substs) => { - write!(f, "", &substs[..])?; + print!(f, cx, write(" "), print(interior), write("]")) + }), + TyClosure(did, substs) => ty::tls::with(|tcx| { + let upvar_tys = substs.upvar_tys(did, tcx); + write!(f, "[closure")?; + + if let Some(node_id) = tcx.hir.as_local_node_id(did) { + if tcx.sess.opts.debugging_opts.span_free_formats { + write!(f, "@{:?}", node_id)?; + } else { + write!(f, "@{:?}", tcx.hir.span(node_id))?; + } + let mut sep = " "; + tcx.with_freevars(node_id, |freevars| { + for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) { + print!(f, cx, + write("{}{}:", + sep, + tcx.hir.name(freevar.var_id())), + print(upvar_ty))?; + sep = ", "; + } + Ok(()) + })? + } else { + // cross-crate closure types should only be + // visible in trans bug reports, I imagine. + write!(f, "@{:?}", did)?; + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + print!(f, cx, + write("{}{}:", sep, index), + print(upvar_ty))?; + sep = ", "; + } } - _ => { - write!(f, "{:?}", sz)?; + + write!(f, "]") + }), + TyArray(ty, sz) => { + print!(f, cx, write("["), print(ty), write("; "))?; + match sz.val { + ConstVal::Integral(ConstInt::Usize(sz)) => { + write!(f, "{}", sz)?; + } + ConstVal::Unevaluated(_def_id, substs) => { + write!(f, "", &substs[..])?; + } + _ => { + write!(f, "{:?}", sz)?; + } } + write!(f, "]") + } + TySlice(ty) => { + print!(f, cx, write("["), print(ty), write("]")) } - write!(f, "]") } - TySlice(ty) => write!(f, "[{}]", ty) } } } -impl<'tcx> fmt::Display for ty::TyS<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.sty) - } -} - -impl fmt::Debug for ty::UpvarId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UpvarId({:?};`{}`;{:?})", - self.var_id, - ty::tls::with(|tcx| tcx.hir.name(tcx.hir.hir_to_node_id(self.var_id))), - self.closure_expr_id) - } -} - -impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UpvarBorrow({:?}, {:?})", - self.kind, self.region) - } -} - -impl fmt::Display for ty::InferTy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let print_var_ids = verbose(); - match *self { - ty::TyVar(ref vid) if print_var_ids => write!(f, "{:?}", vid), - ty::IntVar(ref vid) if print_var_ids => write!(f, "{:?}", vid), - ty::FloatVar(ref vid) if print_var_ids => write!(f, "{:?}", vid), - ty::TyVar(_) => write!(f, "_"), - ty::IntVar(_) => write!(f, "{}", "{integer}"), - ty::FloatVar(_) => write!(f, "{}", "{float}"), - ty::FreshTy(v) => write!(f, "FreshTy({})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) +define_print! { + ('tcx) ty::TyS<'tcx>, (self, f, cx) { + display { + self.sty.print(f, cx) + } + debug { + self.sty.print_display(f, cx) } } } -impl fmt::Display for ty::ParamTy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.name) - } -} - -impl fmt::Debug for ty::ParamTy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}/#{}", self, self.idx) - } -} - -impl<'tcx, T, U> fmt::Display for ty::OutlivesPredicate - where T: fmt::Display, U: fmt::Display -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} : {}", self.0, self.1) - } -} - -impl<'tcx> fmt::Display for ty::EquatePredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} == {}", self.0, self.1) +define_print! { + () ty::ParamTy, (self, f, cx) { + display { + write!(f, "{}", self.name) + } + debug { + write!(f, "{}/#{}", self.name, self.idx) + } } } -impl<'tcx> fmt::Display for ty::SubtypePredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} <: {}", self.a, self.b) +define_print! { + ('tcx, T: Print + fmt::Debug, U: Print + fmt::Debug) ty::OutlivesPredicate, + (self, f, cx) { + display { + print!(f, cx, print(self.0), write(" : "), print(self.1)) + } } } -impl<'tcx> fmt::Debug for ty::TraitPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TraitPredicate({:?})", - self.trait_ref) +define_print! { + ('tcx) ty::EquatePredicate<'tcx>, (self, f, cx) { + display { + print!(f, cx, print(self.0), write(" == "), print(self.1)) + } } } -impl<'tcx> fmt::Display for ty::TraitPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.trait_ref.self_ty(), self.trait_ref) +define_print! { + ('tcx) ty::SubtypePredicate<'tcx>, (self, f, cx) { + display { + print!(f, cx, print(self.a), write(" <: "), print(self.b)) + } } } -impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ProjectionPredicate({:?}, {:?})", - self.projection_ty, - self.ty) +define_print! { + ('tcx) ty::TraitPredicate<'tcx>, (self, f, cx) { + debug { + write!(f, "TraitPredicate({:?})", + self.trait_ref) + } + display { + print!(f, cx, print(self.trait_ref.self_ty()), write(": "), print(self.trait_ref)) + } } } -impl<'tcx> fmt::Display for ty::ProjectionPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} == {}", - self.projection_ty, - self.ty) +define_print! { + ('tcx) ty::ProjectionPredicate<'tcx>, (self, f, cx) { + debug { + print!(f, cx, + write("ProjectionPredicate("), + print(self.projection_ty), + write(", "), + print(self.ty), + write(")")) + } + display { + print!(f, cx, print(self.projection_ty), write(" == "), print(self.ty)) + } } } -impl<'tcx> fmt::Display for ty::ProjectionTy<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // FIXME(tschottdorf): use something like - // parameterized(f, self.substs, self.item_def_id, &[]) - // (which currently ICEs). - let (trait_ref, item_name) = ty::tls::with(|tcx| - (self.trait_ref(tcx), tcx.associated_item(self.item_def_id).name) - ); - write!(f, "{:?}::{}", - trait_ref, - item_name) +define_print! { + ('tcx) ty::ProjectionTy<'tcx>, (self, f, cx) { + display { + // FIXME(tschottdorf): use something like + // parameterized(f, self.substs, self.item_def_id, &[]) + // (which currently ICEs). + let (trait_ref, item_name) = ty::tls::with(|tcx| + (self.trait_ref(tcx), tcx.associated_item(self.item_def_id).name) + ); + print!(f, cx, print_debug(trait_ref), write("::{}", item_name)) + } } } -impl fmt::Display for ty::ClosureKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::ClosureKind::Fn => write!(f, "Fn"), - ty::ClosureKind::FnMut => write!(f, "FnMut"), - ty::ClosureKind::FnOnce => write!(f, "FnOnce"), +define_print! { + () ty::ClosureKind, (self, f, cx) { + display { + match *self { + ty::ClosureKind::Fn => write!(f, "Fn"), + ty::ClosureKind::FnMut => write!(f, "FnMut"), + ty::ClosureKind::FnOnce => write!(f, "FnOnce"), + } } } } -impl<'tcx> fmt::Display for ty::Predicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::Predicate::Trait(ref data) => write!(f, "{}", data), - ty::Predicate::Equate(ref predicate) => write!(f, "{}", predicate), - ty::Predicate::Subtype(ref predicate) => write!(f, "{}", predicate), - ty::Predicate::RegionOutlives(ref predicate) => write!(f, "{}", predicate), - ty::Predicate::TypeOutlives(ref predicate) => write!(f, "{}", predicate), - ty::Predicate::Projection(ref predicate) => write!(f, "{}", predicate), - ty::Predicate::WellFormed(ty) => write!(f, "{} well-formed", ty), - ty::Predicate::ObjectSafe(trait_def_id) => - ty::tls::with(|tcx| { - write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id)) - }), - ty::Predicate::ClosureKind(closure_def_id, kind) => - ty::tls::with(|tcx| { - write!(f, "the closure `{}` implements the trait `{}`", - tcx.item_path_str(closure_def_id), kind) - }), - ty::Predicate::ConstEvaluatable(def_id, substs) => { - write!(f, "the constant `")?; - parameterized(f, substs, def_id, &[])?; - write!(f, "` can be evaluated") +define_print! { + ('tcx) ty::Predicate<'tcx>, (self, f, cx) { + display { + match *self { + ty::Predicate::Trait(ref data) => data.print(f, cx), + ty::Predicate::Equate(ref predicate) => predicate.print(f, cx), + ty::Predicate::Subtype(ref predicate) => predicate.print(f, cx), + ty::Predicate::RegionOutlives(ref predicate) => predicate.print(f, cx), + ty::Predicate::TypeOutlives(ref predicate) => predicate.print(f, cx), + ty::Predicate::Projection(ref predicate) => predicate.print(f, cx), + ty::Predicate::WellFormed(ty) => print!(f, cx, print(ty), write(" well-formed")), + ty::Predicate::ObjectSafe(trait_def_id) => + ty::tls::with(|tcx| { + write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id)) + }), + ty::Predicate::ClosureKind(closure_def_id, _closure_substs, kind) => + ty::tls::with(|tcx| { + write!(f, "the closure `{}` implements the trait `{}`", + tcx.item_path_str(closure_def_id), kind) + }), + ty::Predicate::ConstEvaluatable(def_id, substs) => { + write!(f, "the constant `")?; + cx.parameterized(f, substs, def_id, &[])?; + write!(f, "` can be evaluated") + } + } + } + debug { + match *self { + ty::Predicate::Trait(ref a) => a.print(f, cx), + ty::Predicate::Equate(ref pair) => pair.print(f, cx), + ty::Predicate::Subtype(ref pair) => pair.print(f, cx), + ty::Predicate::RegionOutlives(ref pair) => pair.print(f, cx), + ty::Predicate::TypeOutlives(ref pair) => pair.print(f, cx), + ty::Predicate::Projection(ref pair) => pair.print(f, cx), + ty::Predicate::WellFormed(ty) => ty.print(f, cx), + ty::Predicate::ObjectSafe(trait_def_id) => { + write!(f, "ObjectSafe({:?})", trait_def_id) + } + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) + } + ty::Predicate::ConstEvaluatable(def_id, substs) => { + write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) + } } } } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index eafb4c5c80078..352184c1efa76 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -177,9 +177,13 @@ impl<'a> AllocFnFactory<'a> { let no_mangle = Symbol::intern("no_mangle"); let no_mangle = self.cx.meta_word(self.span, no_mangle); + + let special = Symbol::intern("rustc_std_internal_symbol"); + let special = self.cx.meta_word(self.span, special); vec![ self.cx.attribute(self.span, linkage), self.cx.attribute(self.span, no_mangle), + self.cx.attribute(self.span, special), ] } diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 9e3e622e25260..7dea3dae0bcd1 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -49,10 +49,6 @@ #![feature(slice_patterns)] #![feature(try_from)] -#![cfg_attr(stage0, feature(const_fn))] -#![cfg_attr(not(stage0), feature(const_min_value))] -#![cfg_attr(not(stage0), feature(const_max_value))] - // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. #[allow(unused_extern_crates)] extern crate rustc_cratesio_shim; @@ -98,7 +94,7 @@ impl Status { } impl StatusAnd { - fn map U, U>(self, f: F) -> StatusAnd { + pub fn map U, U>(self, f: F) -> StatusAnd { StatusAnd { status: self.status, value: f(self.value), @@ -380,7 +376,7 @@ pub trait Float fn from_bits(input: u128) -> Self; fn from_i128_r(input: i128, round: Round) -> StatusAnd { if input < 0 { - Self::from_u128_r(-input as u128, -round).map(|r| -r) + Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r) } else { Self::from_u128_r(input as u128, round) } diff --git a/src/librustc_back/Cargo.toml b/src/librustc_back/Cargo.toml index 730abc54568e1..92b024b67d4cc 100644 --- a/src/librustc_back/Cargo.toml +++ b/src/librustc_back/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["dylib"] syntax = { path = "../libsyntax" } serialize = { path = "../libserialize" } log = "0.3" +rand = "0.3" [features] jemalloc = [] diff --git a/src/librustc_back/build.rs b/src/librustc_back/build.rs index 16f0872b25ac1..6f6fde1e9e778 100644 --- a/src/librustc_back/build.rs +++ b/src/librustc_back/build.rs @@ -11,5 +11,4 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=CFG_DEFAULT_LINKER"); - println!("cargo:rerun-if-env-changed=CFG_DEFAULT_AR"); } diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 6a9833d3784a4..ccf1db778d296 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -28,21 +28,15 @@ #![feature(box_syntax)] #![feature(const_fn)] -#![feature(libc)] -#![feature(rand)] -#![cfg_attr(test, feature(rand))] extern crate syntax; -extern crate libc; +extern crate rand; extern crate serialize; #[macro_use] extern crate log; extern crate serialize as rustc_serialize; // used by deriving -pub mod tempdir; pub mod target; -pub mod slice; -pub mod dynamic_lib; use std::str::FromStr; @@ -85,6 +79,7 @@ macro_rules! linker_flavor { linker_flavor! { (Em, "em"), + (Binaryen, "binaryen"), (Gcc, "gcc"), (Ld, "ld"), (Msvc, "msvc"), diff --git a/src/librustc_back/target/aarch64_apple_ios.rs b/src/librustc_back/target/aarch64_apple_ios.rs index 802a8c77db05b..cff6eb534b1e5 100644 --- a/src/librustc_back/target/aarch64_apple_ios.rs +++ b/src/librustc_back/target/aarch64_apple_ios.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "arm64-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "ios".to_string(), diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs index 7d8610b4a3684..2c0d6a55ed8e5 100644 --- a/src/librustc_back/target/aarch64_linux_android.rs +++ b/src/librustc_back/target/aarch64_linux_android.rs @@ -24,6 +24,7 @@ pub fn target() -> TargetResult { llvm_target: "aarch64-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "android".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_freebsd.rs b/src/librustc_back/target/aarch64_unknown_freebsd.rs index c5427a13e4c7d..1ce8d600c0331 100644 --- a/src/librustc_back/target/aarch64_unknown_freebsd.rs +++ b/src/librustc_back/target/aarch64_unknown_freebsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "aarch64-unknown-freebsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "freebsd".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_fuchsia.rs b/src/librustc_back/target/aarch64_unknown_fuchsia.rs index 5d680504a02d0..73cd9c9270153 100644 --- a/src/librustc_back/target/aarch64_unknown_fuchsia.rs +++ b/src/librustc_back/target/aarch64_unknown_fuchsia.rs @@ -19,6 +19,7 @@ pub fn target() -> TargetResult { llvm_target: "aarch64-unknown-fuchsia".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "fuchsia".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 7c2c45a2843a7..5c9c9a0c555c2 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "aarch64-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), target_env: "gnu".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_linux_musl.rs b/src/librustc_back/target/aarch64_unknown_linux_musl.rs index 1edac616366dd..d39ad97bbcb87 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_musl.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_musl.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "aarch64-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), target_env: "musl".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), diff --git a/src/librustc_back/target/apple_ios_base.rs b/src/librustc_back/target/apple_ios_base.rs index 4b02d0b60b8b5..1895ab1eb7e78 100644 --- a/src/librustc_back/target/apple_ios_base.rs +++ b/src/librustc_back/target/apple_ios_base.rs @@ -99,6 +99,10 @@ pub fn opts(arch: Arch) -> Result { executables: true, pre_link_args, has_elf_tls: false, + // The following line is a workaround for jemalloc 4.5 being broken on + // ios. jemalloc 5.0 is supposed to fix this. + // see https://github.com/rust-lang/rust/issues/45262 + exe_allocation_crate: None, .. super::apple_base::opts() }) } diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs index 6bfe90af2ca16..ba21b1df032a8 100644 --- a/src/librustc_back/target/arm_linux_androideabi.rs +++ b/src/librustc_back/target/arm_linux_androideabi.rs @@ -14,13 +14,14 @@ use target::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { let mut base = super::android_base::opts(); // https://developer.android.com/ndk/guides/abis.html#armeabi - base.features = "+v5te".to_string(); + base.features = "+strict-align,+v5te".to_string(); base.max_atomic_width = Some(64); Ok(Target { llvm_target: "arm-linux-androideabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "android".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs index 165d34fe6c7ce..e630376a67dd0 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "arm-unknown-linux-gnueabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), @@ -26,7 +27,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+v6".to_string(), + features: "+strict-align,+v6".to_string(), abi_blacklist: super::arm_base::abi_blacklist(), .. base }, diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs index 731021d979bc4..178a948b2b9c8 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "arm-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), @@ -26,7 +27,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+v6,+vfp2".to_string(), + features: "+strict-align,+v6,+vfp2".to_string(), abi_blacklist: super::arm_base::abi_blacklist(), .. base } diff --git a/src/librustc_back/target/arm_unknown_linux_musleabi.rs b/src/librustc_back/target/arm_unknown_linux_musleabi.rs index f81bcd78b03aa..29720ec5efcd7 100644 --- a/src/librustc_back/target/arm_unknown_linux_musleabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_musleabi.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { // Most of these settings are copied from the arm_unknown_linux_gnueabi // target. - base.features = "+v6".to_string(); + base.features = "+strict-align,+v6".to_string(); base.max_atomic_width = Some(64); Ok(Target { // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { llvm_target: "arm-unknown-linux-gnueabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_musleabihf.rs b/src/librustc_back/target/arm_unknown_linux_musleabihf.rs index 6c47678ede6ad..fc8313877f640 100644 --- a/src/librustc_back/target/arm_unknown_linux_musleabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_musleabihf.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { // Most of these settings are copied from the arm_unknown_linux_gnueabihf // target. - base.features = "+v6,+vfp2".to_string(); + base.features = "+strict-align,+v6,+vfp2".to_string(); base.max_atomic_width = Some(64); Ok(Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { llvm_target: "arm-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs b/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs index ef00c9a3278b9..97397ca49622e 100644 --- a/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs @@ -17,6 +17,7 @@ pub fn target() -> TargetResult { llvm_target: "armv5te-unknown-linux-gnueabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), @@ -26,8 +27,12 @@ pub fn target() -> TargetResult { options: TargetOptions { features: "+soft-float,+strict-align".to_string(), - // No atomic instructions on ARMv5 - max_atomic_width: Some(0), + + // Atomic operations provided when linked with libgcc. + // FIXME: If the following PR is merged, the atomic operations would be + // provided by compiler-builtins instead with no change of behavior: + // https://github.com/rust-lang-nursery/compiler-builtins/pull/115/files + max_atomic_width: Some(32), abi_blacklist: super::arm_base::abi_blacklist(), .. base } diff --git a/src/librustc_back/target/armv7_apple_ios.rs b/src/librustc_back/target/armv7_apple_ios.rs index 4d87458283294..67d3d12fb5776 100644 --- a/src/librustc_back/target/armv7_apple_ios.rs +++ b/src/librustc_back/target/armv7_apple_ios.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "armv7-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), target_os: "ios".to_string(), diff --git a/src/librustc_back/target/armv7_linux_androideabi.rs b/src/librustc_back/target/armv7_linux_androideabi.rs index 45654b0f87020..9e3eec13ab763 100644 --- a/src/librustc_back/target/armv7_linux_androideabi.rs +++ b/src/librustc_back/target/armv7_linux_androideabi.rs @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { llvm_target: "armv7-none-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "android".to_string(), diff --git a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs index d3a6a68449c39..569c721473da5 100644 --- a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs @@ -17,6 +17,7 @@ pub fn target() -> TargetResult { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/armv7_unknown_linux_musleabihf.rs b/src/librustc_back/target/armv7_unknown_linux_musleabihf.rs index 5086cd44f7ac9..a36e26c0b7d5f 100644 --- a/src/librustc_back/target/armv7_unknown_linux_musleabihf.rs +++ b/src/librustc_back/target/armv7_unknown_linux_musleabihf.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/armv7s_apple_ios.rs b/src/librustc_back/target/armv7s_apple_ios.rs index 96c89a7ed3bd5..e4cc89ab21140 100644 --- a/src/librustc_back/target/armv7s_apple_ios.rs +++ b/src/librustc_back/target/armv7s_apple_ios.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "armv7s-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), target_os: "ios".to_string(), diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index b884d4e54101e..a54627279b02c 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -20,7 +20,6 @@ pub fn target() -> Result { let opts = TargetOptions { linker: cmd("emcc"), - ar: cmd("emar"), dynamic_linking: false, executables: true, @@ -38,6 +37,7 @@ pub fn target() -> Result { llvm_target: "asmjs-unknown-emscripten".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), target_os: "emscripten".to_string(), target_env: "".to_string(), target_vendor: "unknown".to_string(), diff --git a/src/librustc_back/target/i386_apple_ios.rs b/src/librustc_back/target/i386_apple_ios.rs index 0e4e6900024b0..82eae1a31a9ad 100644 --- a/src/librustc_back/target/i386_apple_ios.rs +++ b/src/librustc_back/target/i386_apple_ios.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "i386-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "ios".to_string(), diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs index 8c931f18411ca..14937f9aa55b6 100644 --- a/src/librustc_back/target/i686_apple_darwin.rs +++ b/src/librustc_back/target/i686_apple_darwin.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-apple-darwin".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "macos".to_string(), diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs index 565fbe37bf89e..bf27bce79acec 100644 --- a/src/librustc_back/target/i686_linux_android.rs +++ b/src/librustc_back/target/i686_linux_android.rs @@ -28,6 +28,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "android".to_string(), diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs index 4a736a93be7d7..5f20a620b6e1c 100644 --- a/src/librustc_back/target/i686_pc_windows_gnu.rs +++ b/src/librustc_back/target/i686_pc_windows_gnu.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-pc-windows-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(), arch: "x86".to_string(), target_os: "windows".to_string(), diff --git a/src/librustc_back/target/i686_pc_windows_msvc.rs b/src/librustc_back/target/i686_pc_windows_msvc.rs index 17fe306804f4a..48cee04457348 100644 --- a/src/librustc_back/target/i686_pc_windows_msvc.rs +++ b/src/librustc_back/target/i686_pc_windows_msvc.rs @@ -30,6 +30,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-pc-windows-msvc".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(), arch: "x86".to_string(), target_os: "windows".to_string(), diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs index 9eda49a3709a5..891127b9d3711 100644 --- a/src/librustc_back/target/i686_unknown_dragonfly.rs +++ b/src/librustc_back/target/i686_unknown_dragonfly.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-dragonfly".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "dragonfly".to_string(), diff --git a/src/librustc_back/target/i686_unknown_freebsd.rs b/src/librustc_back/target/i686_unknown_freebsd.rs index 041f3070c95bb..076acb8ed318c 100644 --- a/src/librustc_back/target/i686_unknown_freebsd.rs +++ b/src/librustc_back/target/i686_unknown_freebsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-freebsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "freebsd".to_string(), diff --git a/src/librustc_back/target/i686_unknown_haiku.rs b/src/librustc_back/target/i686_unknown_haiku.rs index f21c2f8c77ab0..02a15d6445c47 100644 --- a/src/librustc_back/target/i686_unknown_haiku.rs +++ b/src/librustc_back/target/i686_unknown_haiku.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-haiku".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "haiku".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index f7b916816b313..b509e019c7e1a 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs index 00567d70fd6ce..99c0d4c817915 100644 --- a/src/librustc_back/target/i686_unknown_linux_musl.rs +++ b/src/librustc_back/target/i686_unknown_linux_musl.rs @@ -37,6 +37,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/i686_unknown_netbsd.rs b/src/librustc_back/target/i686_unknown_netbsd.rs index 7a9de529566b5..dd21c205106ea 100644 --- a/src/librustc_back/target/i686_unknown_netbsd.rs +++ b/src/librustc_back/target/i686_unknown_netbsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-netbsdelf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "netbsd".to_string(), diff --git a/src/librustc_back/target/i686_unknown_openbsd.rs b/src/librustc_back/target/i686_unknown_openbsd.rs index b19bdbe049bcd..8daa5fcb88b3f 100644 --- a/src/librustc_back/target/i686_unknown_openbsd.rs +++ b/src/librustc_back/target/i686_unknown_openbsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "i686-unknown-openbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "openbsd".to_string(), diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs deleted file mode 100644 index 51eeae50e22ee..0000000000000 --- a/src/librustc_back/target/le32_unknown_nacl.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use LinkerFlavor; -use super::{LinkArgs, Target, TargetOptions, TargetResult}; - -pub fn target() -> TargetResult { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert(LinkerFlavor::Gcc, - vec!["--pnacl-exceptions=sjlj".to_string(), - "--target=le32-unknown-nacl".to_string(), - "-Wl,--start-group".to_string()]); - let mut post_link_args = LinkArgs::new(); - post_link_args.insert(LinkerFlavor::Gcc, - vec!["-Wl,--end-group".to_string()]); - - let opts = TargetOptions { - linker: "pnacl-clang".to_string(), - ar: "pnacl-ar".to_string(), - - pre_link_args, - post_link_args, - dynamic_linking: false, - executables: true, - exe_suffix: ".pexe".to_string(), - linker_is_gnu: true, - allow_asm: false, - max_atomic_width: Some(32), - .. Default::default() - }; - Ok(Target { - llvm_target: "le32-unknown-nacl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_os: "nacl".to_string(), - target_env: "newlib".to_string(), - target_vendor: "unknown".to_string(), - data_layout: "e-i64:64:64-p:32:32:32-v128:32:32".to_string(), - arch: "le32".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: opts, - }) -} diff --git a/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs b/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs index 2d77902046109..5c3cf31b3e45f 100644 --- a/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs +++ b/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mips64-unknown-linux-gnuabi64".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs b/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs index c26780b9e65ce..96988388e811e 100644 --- a/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs +++ b/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mips64el-unknown-linux-gnuabi64".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mips_unknown_linux_gnu.rs b/src/librustc_back/target/mips_unknown_linux_gnu.rs index 24649851d76fd..5a43e1c4c7a0f 100644 --- a/src/librustc_back/target/mips_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mips_unknown_linux_gnu.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mips-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mips_unknown_linux_musl.rs b/src/librustc_back/target/mips_unknown_linux_musl.rs index 6303722945c9f..3f6b984272ed5 100644 --- a/src/librustc_back/target/mips_unknown_linux_musl.rs +++ b/src/librustc_back/target/mips_unknown_linux_musl.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mips-unknown-linux-musl".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mips_unknown_linux_uclibc.rs b/src/librustc_back/target/mips_unknown_linux_uclibc.rs index 1a7a56a977921..c851cab069a1c 100644 --- a/src/librustc_back/target/mips_unknown_linux_uclibc.rs +++ b/src/librustc_back/target/mips_unknown_linux_uclibc.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mips-unknown-linux-uclibc".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs index cbf8339993c86..2c38444d050fb 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mipsel-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mipsel_unknown_linux_musl.rs b/src/librustc_back/target/mipsel_unknown_linux_musl.rs index b367bce75a1d9..464f0bfe48058 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_musl.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_musl.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mipsel-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs b/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs index 686dfbe987d10..5d2ba548769ff 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "mipsel-unknown-linux-uclibc".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 27a0855dc29fe..6f9ba5dada2d8 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -135,6 +135,7 @@ macro_rules! supported_targets { supported_targets! { ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), + ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), ("i686-unknown-linux-gnu", i686_unknown_linux_gnu), ("i586-unknown-linux-gnu", i586_unknown_linux_gnu), ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), @@ -215,9 +216,9 @@ supported_targets! { ("i686-pc-windows-msvc", i686_pc_windows_msvc), ("i586-pc-windows-msvc", i586_pc_windows_msvc), - ("le32-unknown-nacl", le32_unknown_nacl), ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), + ("wasm32-unknown-unknown", wasm32_unknown_unknown), ("wasm32-experimental-emscripten", wasm32_experimental_emscripten), ("thumbv6m-none-eabi", thumbv6m_none_eabi), @@ -239,6 +240,8 @@ pub struct Target { pub target_endian: String, /// String to use as the `target_pointer_width` `cfg` variable. pub target_pointer_width: String, + /// Width of c_int type + pub target_c_int_width: String, /// OS name to use for conditional compilation. pub target_os: String, /// Environment name to use for conditional compilation. @@ -267,8 +270,6 @@ pub struct TargetOptions { /// Linker to invoke. Defaults to "cc". pub linker: String, - /// Archive utility to use when managing archives. Defaults to "ar". - pub ar: String, /// Linker arguments that are unconditionally passed *before* any /// user-defined libraries. @@ -303,6 +304,8 @@ pub struct TargetOptions { pub features: String, /// Whether dynamic linking is available on this target. Defaults to false. pub dynamic_linking: bool, + /// If dynamic linking is available, whether only cdylibs are supported. + pub only_cdylib: bool, /// Whether executables are available on this target. iOS, for example, only allows static /// libraries. Defaults to false. pub executables: bool, @@ -311,6 +314,9 @@ pub struct TargetOptions { pub relocation_model: String, /// Code model to use. Corresponds to `llc -code-model=$code_model`. Defaults to "default". pub code_model: String, + /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" + /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. + pub tls_model: String, /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. pub disable_redzone: bool, /// Eliminate frame pointers from stack frames if possible. Defaults to true. @@ -429,6 +435,28 @@ pub struct TargetOptions { /// The minimum alignment for global symbols. pub min_global_align: Option, + + /// Default number of codegen units to use in debug mode + pub default_codegen_units: Option, + + /// Whether to generate trap instructions in places where optimization would + /// otherwise produce control flow that falls through into unrelated memory. + pub trap_unreachable: bool, + + /// This target requires everything to be compiled with LTO to emit a final + /// executable, aka there is no native linker for this target. + pub requires_lto: bool, + + /// This target has no support for threads. + pub singlethread: bool, + + /// Whether library functions call lowering/optimization is disabled in LLVM + /// for this target unconditionally. + pub no_builtins: bool, + + /// Whether to lower 128-bit operations to compiler_builtins calls. Use if + /// your backend only supports 64-bit and smaller math. + pub i128_lowering: bool, } impl Default for TargetOptions { @@ -438,16 +466,17 @@ impl Default for TargetOptions { TargetOptions { is_builtin: false, linker: option_env!("CFG_DEFAULT_LINKER").unwrap_or("cc").to_string(), - ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(), pre_link_args: LinkArgs::new(), post_link_args: LinkArgs::new(), asm_args: Vec::new(), cpu: "generic".to_string(), features: "".to_string(), dynamic_linking: false, + only_cdylib: false, executables: false, relocation_model: "pic".to_string(), code_model: "default".to_string(), + tls_model: "global-dynamic".to_string(), disable_redzone: false, eliminate_frame_pointer: true, function_sections: true, @@ -491,6 +520,12 @@ impl Default for TargetOptions { crt_static_respected: false, stack_probes: false, min_global_align: None, + default_codegen_units: None, + trap_unreachable: true, + requires_lto: false, + singlethread: false, + no_builtins: false, + i128_lowering: false, } } } @@ -556,6 +591,7 @@ impl Target { llvm_target: get_req_field("llvm-target")?, target_endian: get_req_field("target-endian")?, target_pointer_width: get_req_field("target-pointer-width")?, + target_c_int_width: get_req_field("target-c-int-width")?, data_layout: get_req_field("data-layout")?, arch: get_req_field("arch")?, target_os: get_req_field("os")?, @@ -678,7 +714,6 @@ impl Target { key!(is_builtin, bool); key!(linker); - key!(ar); key!(pre_link_args, link_args); key!(pre_link_objects_exe, list); key!(pre_link_objects_dll, list); @@ -690,9 +725,11 @@ impl Target { key!(cpu); key!(features); key!(dynamic_linking, bool); + key!(only_cdylib, bool); key!(executables, bool); key!(relocation_model); key!(code_model); + key!(tls_model); key!(disable_redzone, bool); key!(eliminate_frame_pointer, bool); key!(function_sections, bool); @@ -730,6 +767,11 @@ impl Target { key!(crt_static_respected, bool); key!(stack_probes, bool); key!(min_global_align, Option); + key!(default_codegen_units, Option); + key!(trap_unreachable, bool); + key!(requires_lto, bool); + key!(singlethread, bool); + key!(no_builtins, bool); if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) { for name in array.iter().filter_map(|abi| abi.as_string()) { @@ -860,6 +902,7 @@ impl ToJson for Target { target_val!(llvm_target); target_val!(target_endian); target_val!(target_pointer_width); + target_val!(target_c_int_width); target_val!(arch); target_val!(target_os, "os"); target_val!(target_env, "env"); @@ -869,7 +912,6 @@ impl ToJson for Target { target_option_val!(is_builtin); target_option_val!(linker); - target_option_val!(ar); target_option_val!(link_args - pre_link_args); target_option_val!(pre_link_objects_exe); target_option_val!(pre_link_objects_dll); @@ -881,9 +923,11 @@ impl ToJson for Target { target_option_val!(cpu); target_option_val!(features); target_option_val!(dynamic_linking); + target_option_val!(only_cdylib); target_option_val!(executables); target_option_val!(relocation_model); target_option_val!(code_model); + target_option_val!(tls_model); target_option_val!(disable_redzone); target_option_val!(eliminate_frame_pointer); target_option_val!(function_sections); @@ -921,6 +965,11 @@ impl ToJson for Target { target_option_val!(crt_static_respected); target_option_val!(stack_probes); target_option_val!(min_global_align); + target_option_val!(default_codegen_units); + target_option_val!(trap_unreachable); + target_option_val!(requires_lto); + target_option_val!(singlethread); + target_option_val!(no_builtins); if default.abi_blacklist != self.options.abi_blacklist { d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter() diff --git a/src/librustc_back/target/msp430_none_elf.rs b/src/librustc_back/target/msp430_none_elf.rs index 588a8bde79b98..509a7cf5e0323 100644 --- a/src/librustc_back/target/msp430_none_elf.rs +++ b/src/librustc_back/target/msp430_none_elf.rs @@ -16,6 +16,7 @@ pub fn target() -> TargetResult { llvm_target: "msp430-none-elf".to_string(), target_endian: "little".to_string(), target_pointer_width: "16".to_string(), + target_c_int_width: "16".to_string(), data_layout: "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16".to_string(), arch: "msp430".to_string(), target_os: "none".to_string(), @@ -47,6 +48,11 @@ pub fn target() -> TargetResult { // code because of the extra costs it involves. relocation_model: "static".to_string(), + // Right now we invoke an external assembler and this isn't + // compatible with multiple codegen units, and plus we probably + // don't want to invoke that many gcc instances. + default_codegen_units: Some(1), + .. Default::default( ) } }) diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index a5f8e7ae5f91b..ab421dec7807f 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -34,6 +34,7 @@ pub fn opts() -> TargetOptions { is_like_openbsd: true, pre_link_args: args, position_independent_executables: true, + eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, .. Default::default() } diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs index 7b038ac007396..1f119c7204b16 100644 --- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs @@ -28,6 +28,7 @@ pub fn target() -> TargetResult { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs index 5b50b96837fbe..13c59785d480b 100644 --- a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs @@ -24,6 +24,7 @@ pub fn target() -> TargetResult { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs index 8d4ad5f0b447f..1797126b3104a 100644 --- a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub fn target() -> TargetResult { llvm_target: "powerpc-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/s390x_unknown_linux_gnu.rs b/src/librustc_back/target/s390x_unknown_linux_gnu.rs index aad9effacd440..d96379547fb0f 100644 --- a/src/librustc_back/target/s390x_unknown_linux_gnu.rs +++ b/src/librustc_back/target/s390x_unknown_linux_gnu.rs @@ -28,6 +28,7 @@ pub fn target() -> TargetResult { llvm_target: "s390x-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".to_string(), arch: "s390x".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/sparc64_unknown_linux_gnu.rs b/src/librustc_back/target/sparc64_unknown_linux_gnu.rs index 7f710ad402053..aed40e9df436e 100644 --- a/src/librustc_back/target/sparc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/sparc64_unknown_linux_gnu.rs @@ -21,6 +21,7 @@ pub fn target() -> TargetResult { llvm_target: "sparc64-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/sparc64_unknown_netbsd.rs b/src/librustc_back/target/sparc64_unknown_netbsd.rs index bc65a17ce6ea9..483c87909455d 100644 --- a/src/librustc_back/target/sparc64_unknown_netbsd.rs +++ b/src/librustc_back/target/sparc64_unknown_netbsd.rs @@ -21,6 +21,7 @@ pub fn target() -> TargetResult { llvm_target: "sparc64-unknown-netbsd".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), target_os: "netbsd".to_string(), diff --git a/src/librustc_back/target/sparcv9_sun_solaris.rs b/src/librustc_back/target/sparcv9_sun_solaris.rs index 122b38968a9c0..1d9027275db82 100644 --- a/src/librustc_back/target/sparcv9_sun_solaris.rs +++ b/src/librustc_back/target/sparcv9_sun_solaris.rs @@ -17,11 +17,13 @@ pub fn target() -> TargetResult { // llvm calls this "v9" base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "sparcv9-sun-solaris".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), // Use "sparc64" instead of "sparcv9" here, since the former is already // used widely in the source base. If we ever needed ABI diff --git a/src/librustc_back/target/thumbv6m_none_eabi.rs b/src/librustc_back/target/thumbv6m_none_eabi.rs index 08bf145e5518a..d164fbf9d96f9 100644 --- a/src/librustc_back/target/thumbv6m_none_eabi.rs +++ b/src/librustc_back/target/thumbv6m_none_eabi.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "thumbv6m-none-eabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "none".to_string(), diff --git a/src/librustc_back/target/thumbv7em_none_eabi.rs b/src/librustc_back/target/thumbv7em_none_eabi.rs index 13f9cc5f65fb9..7e66ddf7b06a7 100644 --- a/src/librustc_back/target/thumbv7em_none_eabi.rs +++ b/src/librustc_back/target/thumbv7em_none_eabi.rs @@ -27,6 +27,7 @@ pub fn target() -> TargetResult { llvm_target: "thumbv7em-none-eabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "none".to_string(), diff --git a/src/librustc_back/target/thumbv7em_none_eabihf.rs b/src/librustc_back/target/thumbv7em_none_eabihf.rs index 929b6db6fb2c6..31835de36d6a4 100644 --- a/src/librustc_back/target/thumbv7em_none_eabihf.rs +++ b/src/librustc_back/target/thumbv7em_none_eabihf.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { llvm_target: "thumbv7em-none-eabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "none".to_string(), diff --git a/src/librustc_back/target/thumbv7m_none_eabi.rs b/src/librustc_back/target/thumbv7m_none_eabi.rs index 8d46e7cb90760..8f16ae4ea12df 100644 --- a/src/librustc_back/target/thumbv7m_none_eabi.rs +++ b/src/librustc_back/target/thumbv7m_none_eabi.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "thumbv7m-none-eabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "none".to_string(), diff --git a/src/librustc_back/target/wasm32_experimental_emscripten.rs b/src/librustc_back/target/wasm32_experimental_emscripten.rs index 42ab19404049e..a261c982b3f24 100644 --- a/src/librustc_back/target/wasm32_experimental_emscripten.rs +++ b/src/librustc_back/target/wasm32_experimental_emscripten.rs @@ -25,7 +25,6 @@ pub fn target() -> Result { let opts = TargetOptions { linker: cmd("emcc"), - ar: cmd("emar"), dynamic_linking: false, executables: true, @@ -46,6 +45,7 @@ pub fn target() -> Result { llvm_target: "wasm32-unknown-unknown".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), target_os: "emscripten".to_string(), target_env: "".to_string(), target_vendor: "unknown".to_string(), diff --git a/src/librustc_back/target/wasm32_unknown_emscripten.rs b/src/librustc_back/target/wasm32_unknown_emscripten.rs index a0a2699d8f951..197c1f7a4da49 100644 --- a/src/librustc_back/target/wasm32_unknown_emscripten.rs +++ b/src/librustc_back/target/wasm32_unknown_emscripten.rs @@ -22,7 +22,6 @@ pub fn target() -> Result { let opts = TargetOptions { linker: cmd("emcc"), - ar: cmd("emar"), dynamic_linking: false, executables: true, @@ -42,6 +41,7 @@ pub fn target() -> Result { llvm_target: "asmjs-unknown-emscripten".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), target_os: "emscripten".to_string(), target_env: "".to_string(), target_vendor: "unknown".to_string(), diff --git a/src/librustc_back/target/wasm32_unknown_unknown.rs b/src/librustc_back/target/wasm32_unknown_unknown.rs new file mode 100644 index 0000000000000..7e1011ab8af96 --- /dev/null +++ b/src/librustc_back/target/wasm32_unknown_unknown.rs @@ -0,0 +1,104 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// The wasm32-unknown-unknown target is currently a highly experimental version +// of a wasm-based target which does *not* use the Emscripten toolchain. Instead +// this is a pretty flavorful (aka hacked up) target right now. The definition +// and semantics of this target are likely to change and so this shouldn't be +// relied on just yet. +// +// In general everyone is currently waiting on a linker for wasm code. In the +// meantime we have no means of actually making use of the traditional separate +// compilation model. At a high level this means that assembling Rust programs +// into a WebAssembly program looks like: +// +// 1. All intermediate artifacts are LLVM bytecode. We'll be using LLVM as +// a linker later on. +// 2. For the final artifact we emit one giant assembly file (WebAssembly +// doesn't have an object file format). To do this we force LTO to be turned +// on (`requires_lto` below) to ensure all Rust code is in one module. Any +// "linked" C library is basically just ignored. +// 3. Using LLVM we emit a `foo.s` file (assembly) with some... what I can only +// describe as arcane syntax. From there we need to actually change this +// into a wasm module. For this step we use the `binaryen` project. This +// project is mostly intended as a WebAssembly code generator, but for now +// we're just using its LLVM-assembly-to-wasm-module conversion utilities. +// +// And voila, out comes a web assembly module! There's some various tweaks here +// and there, but that's the high level at least. Note that this will be +// rethought from the ground up once a linker (lld) is available, so this is all +// temporary and should improve in the future. + +use LinkerFlavor; +use super::{Target, TargetOptions, PanicStrategy}; + +pub fn target() -> Result { + let opts = TargetOptions { + linker: "not-used".to_string(), + + // we allow dynamic linking, but only cdylibs. Basically we allow a + // final library artifact that exports some symbols (a wasm module) but + // we don't allow intermediate `dylib` crate types + dynamic_linking: true, + only_cdylib: true, + + // This means we'll just embed a `start` function in the wasm module + executables: true, + + // relatively self-explanatory! + exe_suffix: ".wasm".to_string(), + dll_prefix: "".to_string(), + dll_suffix: ".wasm".to_string(), + linker_is_gnu: false, + + // We're storing bitcode for now in all the rlibs + obj_is_bitcode: true, + + // A bit of a lie, but "eh" + max_atomic_width: Some(32), + + // Unwinding doesn't work right now, so the whole target unconditionally + // defaults to panic=abort. Note that this is guaranteed to change in + // the future once unwinding is implemented. Don't rely on this. + panic_strategy: PanicStrategy::Abort, + + // There's no linker yet so we're forced to use LLVM as a linker. This + // means that we must always enable LTO for final artifacts. + requires_lto: true, + + // Wasm doesn't have atomics yet, so tell LLVM that we're in a single + // threaded model which will legalize atomics to normal operations. + singlethread: true, + + // Because we're always enabling LTO we can't enable builtin lowering as + // otherwise we'll lower the definition of the `memcpy` function to + // memcpy itself. Note that this is specifically because we're + // performing LTO with compiler-builtins. + no_builtins: true, + + .. Default::default() + }; + Ok(Target { + llvm_target: "wasm32-unknown-unknown".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + // This is basically guaranteed to change in the future, don't rely on + // this. Use `not(target_os = "emscripten")` for now. + target_os: "unknown".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + arch: "wasm32".to_string(), + // A bit of a lie, but it gets the job done + linker_flavor: LinkerFlavor::Binaryen, + options: opts, + }) +} diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index 42a4e6f5f1188..64df6624dd1c2 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -21,37 +21,6 @@ pub fn opts() -> TargetOptions { TargetOptions { function_sections: true, linker: "link.exe".to_string(), - // When taking a look at the value of this `ar` field, one might expect - // `lib.exe` to be the value here! The `lib.exe` program is the default - // tool for managing `.lib` archives on Windows, but unfortunately the - // compiler cannot use it. - // - // To recap, we use `ar` here to manage rlibs (which are just archives). - // LLVM does not expose bindings for modifying archives so we have to - // invoke this utility for write operations (e.g. deleting files, adding - // files, etc). Normally archives only have object files within them, - // but the compiler also uses archives for storing metadata and - // compressed bytecode, so we don't exactly fall within "normal use - // cases". - // - // MSVC's `lib.exe` tool by default will choke when adding a non-object - // file to an archive, which we do on a regular basis, making it - // inoperable for us. Luckily, however, LLVM has already rewritten `ar` - // in the form of `llvm-ar` which is built by default when we build - // LLVM. This tool, unlike `lib.exe`, works just fine with non-object - // files, so we use it instead. - // - // Note that there's a few caveats associated with this: - // - // * This still requires that the *linker* (the consumer of rlibs) will - // ignore non-object files. Thankfully `link.exe` on Windows does - // indeed ignore non-object files in archives. - // * This requires `llvm-ar.exe` to be distributed with the compiler - // itself, but we already make sure of this elsewhere. - // - // Perhaps one day we won't even need this tool at all and we'll just be - // able to make library calls into LLVM! - ar: "llvm-ar.exe".to_string(), dynamic_linking: true, executables: true, dll_prefix: "".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs index 8ac76679008e9..71ac360eb9906 100644 --- a/src/librustc_back/target/x86_64_apple_darwin.rs +++ b/src/librustc_back/target/x86_64_apple_darwin.rs @@ -23,6 +23,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-apple-darwin".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "macos".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_ios.rs b/src/librustc_back/target/x86_64_apple_ios.rs index 61a71da2162a0..eed99e3784ce0 100644 --- a/src/librustc_back/target/x86_64_apple_ios.rs +++ b/src/librustc_back/target/x86_64_apple_ios.rs @@ -18,6 +18,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "ios".to_string(), diff --git a/src/librustc_back/target/x86_64_linux_android.rs b/src/librustc_back/target/x86_64_linux_android.rs index 158e2b13604ec..2aae73941539f 100644 --- a/src/librustc_back/target/x86_64_linux_android.rs +++ b/src/librustc_back/target/x86_64_linux_android.rs @@ -24,6 +24,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "android".to_string(), diff --git a/src/librustc_back/target/x86_64_pc_windows_gnu.rs b/src/librustc_back/target/x86_64_pc_windows_gnu.rs index 10e88d88ee372..70062d1363841 100644 --- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs +++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs @@ -21,6 +21,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-pc-windows-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "windows".to_string(), diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs index 7eb673d8b363c..813d0f1bad947 100644 --- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -21,6 +21,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-pc-windows-msvc".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "windows".to_string(), diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index c7e5edde63db3..18f6380b6eedf 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -16,7 +16,6 @@ pub fn target() -> TargetResult { base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.linker = "x86_64-rumprun-netbsd-gcc".to_string(); - base.ar = "x86_64-rumprun-netbsd-ar".to_string(); base.max_atomic_width = Some(64); base.dynamic_linking = false; @@ -31,6 +30,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-rumprun-netbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "netbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_sun_solaris.rs b/src/librustc_back/target/x86_64_sun_solaris.rs index 38a38ed68bc92..d554138539083 100644 --- a/src/librustc_back/target/x86_64_sun_solaris.rs +++ b/src/librustc_back/target/x86_64_sun_solaris.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-pc-solaris".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "solaris".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_bitrig.rs b/src/librustc_back/target/x86_64_unknown_bitrig.rs index cf4b019dce2df..1ea985d70eaca 100644 --- a/src/librustc_back/target/x86_64_unknown_bitrig.rs +++ b/src/librustc_back/target/x86_64_unknown_bitrig.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-bitrig".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "bitrig".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_dragonfly.rs b/src/librustc_back/target/x86_64_unknown_dragonfly.rs index 8885d89c6f7a8..56e4685fed5b4 100644 --- a/src/librustc_back/target/x86_64_unknown_dragonfly.rs +++ b/src/librustc_back/target/x86_64_unknown_dragonfly.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-dragonfly".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "dragonfly".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_freebsd.rs b/src/librustc_back/target/x86_64_unknown_freebsd.rs index 95870f2be5fc0..3d26592530abf 100644 --- a/src/librustc_back/target/x86_64_unknown_freebsd.rs +++ b/src/librustc_back/target/x86_64_unknown_freebsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-freebsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "freebsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_fuchsia.rs b/src/librustc_back/target/x86_64_unknown_fuchsia.rs index 1aebb885595e2..6e97d53cfad6a 100644 --- a/src/librustc_back/target/x86_64_unknown_fuchsia.rs +++ b/src/librustc_back/target/x86_64_unknown_fuchsia.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-fuchsia".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "fuchsia".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_haiku.rs b/src/librustc_back/target/x86_64_unknown_haiku.rs index 3794a516ec4a8..f42c480e7a117 100644 --- a/src/librustc_back/target/x86_64_unknown_haiku.rs +++ b/src/librustc_back/target/x86_64_unknown_haiku.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-haiku".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "haiku".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_l4re_uclibc.rs b/src/librustc_back/target/x86_64_unknown_l4re_uclibc.rs index 99d3171e1c0e0..6e849f19cf20f 100644 --- a/src/librustc_back/target/x86_64_unknown_l4re_uclibc.rs +++ b/src/librustc_back/target/x86_64_unknown_l4re_uclibc.rs @@ -20,6 +20,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-l4re-uclibc".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "l4re".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs index d2135f8a0bdce..cfe80c96732bf 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnux32.rs b/src/librustc_back/target/x86_64_unknown_linux_gnux32.rs new file mode 100644 index 0000000000000..7038203283643 --- /dev/null +++ b/src/librustc_back/target/x86_64_unknown_linux_gnux32.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use LinkerFlavor; +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string()); + base.stack_probes = true; + base.has_elf_tls = false; + + Ok(Target { + llvm_target: "x86_64-unknown-linux-gnux32".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128".to_string(), + arch: "x86_64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index 7d542b4d3cb5a..7e304748e3207 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "linux".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs index 5d49fcbd64ab1..7afb446f5dc97 100644 --- a/src/librustc_back/target/x86_64_unknown_netbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-netbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "netbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_openbsd.rs b/src/librustc_back/target/x86_64_unknown_openbsd.rs index aa289fb577501..e4bbdbec4c850 100644 --- a/src/librustc_back/target/x86_64_unknown_openbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_openbsd.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-openbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "openbsd".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_redox.rs b/src/librustc_back/target/x86_64_unknown_redox.rs index 8d2a7afeeacf0..401fa4ca2cbdd 100644 --- a/src/librustc_back/target/x86_64_unknown_redox.rs +++ b/src/librustc_back/target/x86_64_unknown_redox.rs @@ -22,6 +22,7 @@ pub fn target() -> TargetResult { llvm_target: "x86_64-unknown-redox".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "redox".to_string(), diff --git a/src/librustc_back/tempdir.rs b/src/librustc_back/tempdir.rs deleted file mode 100644 index 8ffaddd7c29f2..0000000000000 --- a/src/librustc_back/tempdir.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::env; -use std::io::{self, Error, ErrorKind}; -use std::fs; -use std::path::{self, PathBuf, Path}; -use std::__rand::{thread_rng, Rng}; - -/// A wrapper for a path to temporary directory implementing automatic -/// scope-based deletion. -pub struct TempDir { - path: Option, -} - -// How many times should we (re)try finding an unused random name? It should be -// enough that an attacker will run out of luck before we run out of patience. -const NUM_RETRIES: u32 = 1 << 31; -// How many characters should we include in a random file name? It needs to -// be enough to dissuade an attacker from trying to preemptively create names -// of that length, but not so huge that we unnecessarily drain the random number -// generator of entropy. -const NUM_RAND_CHARS: usize = 12; - -impl TempDir { - /// Attempts to make a temporary directory inside of `tmpdir` whose name - /// will have the prefix `prefix`. The directory will be automatically - /// deleted once the returned wrapper is destroyed. - /// - /// If no directory can be created, `Err` is returned. - #[allow(deprecated)] // rand usage - pub fn new_in>(tmpdir: P, prefix: &str) - -> io::Result { - Self::_new_in(tmpdir.as_ref(), prefix) - } - - fn _new_in(tmpdir: &Path, prefix: &str) -> io::Result { - let storage; - let mut tmpdir = tmpdir; - if !tmpdir.is_absolute() { - let cur_dir = env::current_dir()?; - storage = cur_dir.join(tmpdir); - tmpdir = &storage; - // return TempDir::new_in(&cur_dir.join(tmpdir), prefix); - } - - let mut rng = thread_rng(); - for _ in 0..NUM_RETRIES { - let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect(); - let leaf = if !prefix.is_empty() { - format!("{}.{}", prefix, suffix) - } else { - // If we're given an empty string for a prefix, then creating a - // directory starting with "." would lead to it being - // semi-invisible on some systems. - suffix - }; - let path = tmpdir.join(&leaf); - match fs::create_dir(&path) { - Ok(_) => return Ok(TempDir { path: Some(path) }), - Err(ref e) if e.kind() == ErrorKind::AlreadyExists => {} - Err(e) => return Err(e) - } - } - - Err(Error::new(ErrorKind::AlreadyExists, - "too many temporary directories already exist")) - } - - /// Attempts to make a temporary directory inside of `env::temp_dir()` whose - /// name will have the prefix `prefix`. The directory will be automatically - /// deleted once the returned wrapper is destroyed. - /// - /// If no directory can be created, `Err` is returned. - pub fn new(prefix: &str) -> io::Result { - TempDir::new_in(&env::temp_dir(), prefix) - } - - /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper. - /// This discards the wrapper so that the automatic deletion of the - /// temporary directory is prevented. - pub fn into_path(mut self) -> PathBuf { - self.path.take().unwrap() - } - - /// Access the wrapped `std::path::Path` to the temporary directory. - pub fn path(&self) -> &path::Path { - self.path.as_ref().unwrap() - } - - fn cleanup_dir(&mut self) -> io::Result<()> { - match self.path { - Some(ref p) => fs::remove_dir_all(p), - None => Ok(()) - } - } -} - -impl Drop for TempDir { - fn drop(&mut self) { - let _ = self.cleanup_dir(); - } -} - -// the tests for this module need to change the path using change_dir, -// and this doesn't play nicely with other tests so these unit tests are located -// in src/test/run-pass/tempfile.rs diff --git a/src/librustc_binaryen/BinaryenWrapper.cpp b/src/librustc_binaryen/BinaryenWrapper.cpp new file mode 100644 index 0000000000000..d1095a7819d4a --- /dev/null +++ b/src/librustc_binaryen/BinaryenWrapper.cpp @@ -0,0 +1,132 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is a small C API inserted on top of the Binaryen C++ API which we use +// from Rust. Once we have a real linker for we'll be able to remove all this, +// and otherwise this is just all on a "as we need it" basis for now. + +#include +#include +#include + +#include "s2wasm.h" +#include "wasm-binary.h" +#include "wasm-linker.h" + +using namespace wasm; + +struct BinaryenRustModule { + BufferWithRandomAccess buffer; +}; + +struct BinaryenRustModuleOptions { + uint64_t globalBase; + bool debug; + uint64_t stackAllocation; + uint64_t initialMem; + uint64_t maxMem; + bool importMemory; + bool ignoreUnknownSymbols; + bool debugInfo; + std::string startFunction; + + BinaryenRustModuleOptions() : + globalBase(0), + debug(false), + stackAllocation(0), + initialMem(0), + maxMem(0), + importMemory(false), + ignoreUnknownSymbols(false), + debugInfo(false), + startFunction("") + {} + +}; + +extern "C" BinaryenRustModuleOptions* +BinaryenRustModuleOptionsCreate() { + return new BinaryenRustModuleOptions; +} + +extern "C" void +BinaryenRustModuleOptionsFree(BinaryenRustModuleOptions *options) { + delete options; +} + +extern "C" void +BinaryenRustModuleOptionsSetDebugInfo(BinaryenRustModuleOptions *options, + bool debugInfo) { + options->debugInfo = debugInfo; +} + +extern "C" void +BinaryenRustModuleOptionsSetStart(BinaryenRustModuleOptions *options, + char *start) { + options->startFunction = start; +} + +extern "C" void +BinaryenRustModuleOptionsSetStackAllocation(BinaryenRustModuleOptions *options, + uint64_t stack) { + options->stackAllocation = stack; +} + +extern "C" void +BinaryenRustModuleOptionsSetImportMemory(BinaryenRustModuleOptions *options, + bool import) { + options->importMemory = import; +} + +extern "C" BinaryenRustModule* +BinaryenRustModuleCreate(const BinaryenRustModuleOptions *options, + const char *assembly) { + Linker linker( + options->globalBase, + options->stackAllocation, + options->initialMem, + options->maxMem, + options->importMemory, + options->ignoreUnknownSymbols, + options->startFunction, + options->debug); + + S2WasmBuilder mainbuilder(assembly, options->debug); + linker.linkObject(mainbuilder); + linker.layout(); + + auto ret = make_unique(); + { + WasmBinaryWriter writer(&linker.getOutput().wasm, ret->buffer, options->debug); + writer.setNamesSection(options->debugInfo); + // FIXME: support source maps? + // writer.setSourceMap(sourceMapStream.get(), sourceMapUrl); + + // FIXME: support symbol maps? + // writer.setSymbolMap(symbolMap); + writer.write(); + } + return ret.release(); +} + +extern "C" const uint8_t* +BinaryenRustModulePtr(const BinaryenRustModule *M) { + return M->buffer.data(); +} + +extern "C" size_t +BinaryenRustModuleLen(const BinaryenRustModule *M) { + return M->buffer.size(); +} + +extern "C" void +BinaryenRustModuleFree(BinaryenRustModule *M) { + delete M; +} diff --git a/src/librustc_binaryen/Cargo.toml b/src/librustc_binaryen/Cargo.toml new file mode 100644 index 0000000000000..9573c89471404 --- /dev/null +++ b/src/librustc_binaryen/Cargo.toml @@ -0,0 +1,16 @@ +# Wondering what this crate is? Take a look at the `lib.rs`! + +[package] +name = "rustc_binaryen" +version = "0.0.0" +authors = ["The Rust Project Developers"] + +[lib] +path = "lib.rs" + +[dependencies] +libc = "0.2" + +[build-dependencies] +cmake = "0.1" +cc = "1.0" diff --git a/src/librustc_binaryen/build.rs b/src/librustc_binaryen/build.rs new file mode 100644 index 0000000000000..f23ff3cee555b --- /dev/null +++ b/src/librustc_binaryen/build.rs @@ -0,0 +1,60 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate cc; +extern crate cmake; + +use std::env; + +use cmake::Config; + +fn main() { + let target = env::var("TARGET").unwrap(); + + // Bring in `__emutls_get_address` which is apparently needed for now + if target.contains("pc-windows-gnu") { + println!("cargo:rustc-link-lib=gcc_eh"); + println!("cargo:rustc-link-lib=pthread"); + } + + Config::new("../binaryen") + .define("BUILD_STATIC_LIB", "ON") + .build_target("binaryen") + .build(); + + // I couldn't figure out how to link just one of these, so link everything. + println!("cargo:rustc-link-lib=static=asmjs"); + println!("cargo:rustc-link-lib=static=binaryen"); + println!("cargo:rustc-link-lib=static=cfg"); + println!("cargo:rustc-link-lib=static=emscripten-optimizer"); + println!("cargo:rustc-link-lib=static=ir"); + println!("cargo:rustc-link-lib=static=passes"); + println!("cargo:rustc-link-lib=static=support"); + println!("cargo:rustc-link-lib=static=wasm"); + + let out_dir = env::var("OUT_DIR").unwrap(); + println!("cargo:rustc-link-search=native={}/build/lib", out_dir); + + // Add in our own little shim along with some extra files that weren't + // included in the main build. + let mut cfg = cc::Build::new(); + cfg.file("BinaryenWrapper.cpp") + .file("../binaryen/src/wasm-linker.cpp") + .file("../binaryen/src/wasm-emscripten.cpp") + .include("../binaryen/src") + .cpp_link_stdlib(None) + .warnings(false) + .cpp(true); + + if !target.contains("msvc") { + cfg.flag("-std=c++11"); + } + cfg.compile("binaryen_wrapper"); +} diff --git a/src/librustc_binaryen/lib.rs b/src/librustc_binaryen/lib.rs new file mode 100644 index 0000000000000..6c7feb6a7a9d3 --- /dev/null +++ b/src/librustc_binaryen/lib.rs @@ -0,0 +1,150 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Rustc bindings to the binaryen project. +//! +//! This crate is a small shim around the binaryen project which provides us the +//! ability to take LLVM's output and generate a wasm module. Specifically this +//! only supports one operation, creating a module from LLVM's assembly format +//! and then serializing that module to a wasm module. + +extern crate libc; + +use std::slice; +use std::ffi::{CString, CStr}; + +/// In-memory representation of a serialized wasm module. +pub struct Module { + ptr: *mut BinaryenRustModule, +} + +impl Module { + /// Creates a new wasm module from the LLVM-assembly provided (in a C string + /// format). + /// + /// The actual module creation can be tweaked through the various options in + /// `ModuleOptions` as well. Any errors are just returned as a bland string. + pub fn new(assembly: &CStr, opts: &ModuleOptions) -> Result { + unsafe { + let ptr = BinaryenRustModuleCreate(opts.ptr, assembly.as_ptr()); + if ptr.is_null() { + Err(format!("failed to create binaryen module")) + } else { + Ok(Module { ptr }) + } + } + } + + /// Returns the data of the serialized wasm module. This is a `foo.wasm` + /// file contents. + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = BinaryenRustModulePtr(self.ptr); + let len = BinaryenRustModuleLen(self.ptr); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for Module { + fn drop(&mut self) { + unsafe { + BinaryenRustModuleFree(self.ptr); + } + } +} + +pub struct ModuleOptions { + ptr: *mut BinaryenRustModuleOptions, +} + +impl ModuleOptions { + pub fn new() -> ModuleOptions { + unsafe { + let ptr = BinaryenRustModuleOptionsCreate(); + ModuleOptions { ptr } + } + } + + /// Turns on or off debug info. + /// + /// From what I can tell this just creates a "names" section of the wasm + /// module which contains a table of the original function names. + pub fn debuginfo(&mut self, debug: bool) -> &mut Self { + unsafe { + BinaryenRustModuleOptionsSetDebugInfo(self.ptr, debug); + } + self + } + + /// Configures a `start` function for the module, to be executed when it's + /// loaded. + pub fn start(&mut self, func: &str) -> &mut Self { + let func = CString::new(func).unwrap(); + unsafe { + BinaryenRustModuleOptionsSetStart(self.ptr, func.as_ptr()); + } + self + } + + /// Configures how much stack is initially allocated for the module. 1MB is + /// probably good enough for now. + pub fn stack(&mut self, amt: u64) -> &mut Self { + unsafe { + BinaryenRustModuleOptionsSetStackAllocation(self.ptr, amt); + } + self + } + + /// Flags whether the initial memory should be imported or exported. So far + /// we export it by default. + pub fn import_memory(&mut self, import: bool) -> &mut Self { + unsafe { + BinaryenRustModuleOptionsSetImportMemory(self.ptr, import); + } + self + } +} + +impl Drop for ModuleOptions { + fn drop(&mut self) { + unsafe { + BinaryenRustModuleOptionsFree(self.ptr); + } + } +} + +enum BinaryenRustModule {} +enum BinaryenRustModuleOptions {} + +extern { + fn BinaryenRustModuleCreate(opts: *const BinaryenRustModuleOptions, + assembly: *const libc::c_char) + -> *mut BinaryenRustModule; + fn BinaryenRustModulePtr(module: *const BinaryenRustModule) -> *const u8; + fn BinaryenRustModuleLen(module: *const BinaryenRustModule) -> usize; + fn BinaryenRustModuleFree(module: *mut BinaryenRustModule); + + fn BinaryenRustModuleOptionsCreate() + -> *mut BinaryenRustModuleOptions; + fn BinaryenRustModuleOptionsSetDebugInfo(module: *mut BinaryenRustModuleOptions, + debuginfo: bool); + fn BinaryenRustModuleOptionsSetStart(module: *mut BinaryenRustModuleOptions, + start: *const libc::c_char); + fn BinaryenRustModuleOptionsSetStackAllocation( + module: *mut BinaryenRustModuleOptions, + stack: u64, + ); + fn BinaryenRustModuleOptionsSetImportMemory( + module: *mut BinaryenRustModuleOptions, + import: bool, + ); + fn BinaryenRustModuleOptionsFree(module: *mut BinaryenRustModuleOptions); +} diff --git a/src/librustc_borrowck/borrowck/README.md b/src/librustc_borrowck/borrowck/README.md index 034b7cbadd9c6..b877c5a9cbcbc 100644 --- a/src/librustc_borrowck/borrowck/README.md +++ b/src/librustc_borrowck/borrowck/README.md @@ -781,8 +781,9 @@ the base path, it will still be considered freezable. -**FIXME #10520: Restrictions against mutating the base pointer.** When -an `&mut` pointer is frozen or claimed, we currently pass along the +**FIXME [RFC 1751](https://github.com/rust-lang/rfcs/issues/1751) +Restrictions against mutating the base pointer.** +When an `&mut` pointer is frozen or claimed, we currently pass along the restriction against MUTATE to the base pointer. I do not believe this restriction is needed. It dates from the days when we had a way to mutate that preserved the value being mutated (i.e., swap). Nowadays diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 985257c28104c..908737669c5cb 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -206,7 +206,13 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, all_loans, param_env, }; - euv::ExprUseVisitor::new(&mut clcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables) + let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id); + euv::ExprUseVisitor::new(&mut clcx, + bccx.tcx, + param_env, + &bccx.region_scope_tree, + bccx.tables, + Some(rvalue_promotable_map)) .consume_body(body); } @@ -389,10 +395,21 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { assert!(self.bccx.region_scope_tree.scopes_intersect(old_loan.kill_scope, new_loan.kill_scope)); - self.report_error_if_loan_conflicts_with_restriction( - old_loan, new_loan, old_loan, new_loan) && - self.report_error_if_loan_conflicts_with_restriction( - new_loan, old_loan, old_loan, new_loan) + let err_old_new = self.report_error_if_loan_conflicts_with_restriction( + old_loan, new_loan, old_loan, new_loan).err(); + let err_new_old = self.report_error_if_loan_conflicts_with_restriction( + new_loan, old_loan, old_loan, new_loan).err(); + + match (err_old_new, err_new_old) { + (Some(mut err), None) | (None, Some(mut err)) => err.emit(), + (Some(mut err_old), Some(mut err_new)) => { + err_old.emit(); + err_new.cancel(); + } + (None, None) => return true, + } + + false } pub fn report_error_if_loan_conflicts_with_restriction(&self, @@ -400,7 +417,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { loan2: &Loan<'tcx>, old_loan: &Loan<'tcx>, new_loan: &Loan<'tcx>) - -> bool { + -> Result<(), DiagnosticBuilder<'a>> { //! Checks whether the restrictions introduced by `loan1` would //! prohibit `loan2`. Returns false if an error is reported. @@ -410,7 +427,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { loan2); if compatible_borrow_kinds(loan1.kind, loan2.kind) { - return true; + return Ok(()); } let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path); @@ -467,105 +484,33 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { // 3. Where does old loan expire. let previous_end_span = - old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree).end_point(); + Some(old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree) + .end_point()); let mut err = match (new_loan.kind, old_loan.kind) { - (ty::MutBorrow, ty::MutBorrow) => { - let mut err = self.bccx.cannot_mutably_borrow_multiply( - new_loan.span, &nl, &new_loan_msg, Origin::Ast); - - if new_loan.span == old_loan.span { - // Both borrows are happening in the same place - // Meaning the borrow is occurring in a loop - err.span_label( - new_loan.span, - format!("mutable borrow starts here in previous \ - iteration of loop{}", new_loan_msg)); - err.span_label( - previous_end_span, - "mutable borrow ends here"); - err - } else { - err.span_label( - old_loan.span, - format!("first mutable borrow occurs here{}", old_loan_msg)); - err.span_label( - new_loan.span, - format!("second mutable borrow occurs here{}", new_loan_msg)); - err.span_label( - previous_end_span, - "first borrow ends here"); - err - } - } - - (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => { - let mut err = self.bccx.cannot_uniquely_borrow_by_two_closures( - new_loan.span, &nl, Origin::Ast); - err.span_label( - old_loan.span, - "first closure is constructed here"); - err.span_label( - new_loan.span, - "second closure is constructed here"); - err.span_label( - previous_end_span, - "borrow from first closure ends here"); - err - } - - (ty::UniqueImmBorrow, _) => { - let mut err = self.bccx.cannot_uniquely_borrow_by_one_closure( - new_loan.span, &nl, &ol_pronoun, &old_loan_msg, Origin::Ast); - err.span_label( - new_loan.span, - format!("closure construction occurs here{}", new_loan_msg)); - err.span_label( - old_loan.span, - format!("borrow occurs here{}", old_loan_msg)); - err.span_label( - previous_end_span, - "borrow ends here"); - err - } - + (ty::MutBorrow, ty::MutBorrow) => + self.bccx.cannot_mutably_borrow_multiply( + new_loan.span, &nl, &new_loan_msg, old_loan.span, &old_loan_msg, + previous_end_span, Origin::Ast), + (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => + self.bccx.cannot_uniquely_borrow_by_two_closures( + new_loan.span, &nl, old_loan.span, previous_end_span, Origin::Ast), + (ty::UniqueImmBorrow, _) => + self.bccx.cannot_uniquely_borrow_by_one_closure( + new_loan.span, &nl, &new_loan_msg, + old_loan.span, &ol_pronoun, &old_loan_msg, previous_end_span, Origin::Ast), (_, ty::UniqueImmBorrow) => { let new_loan_str = &new_loan.kind.to_user_str(); - let mut err = self.bccx.cannot_reborrow_already_uniquely_borrowed( - new_loan.span, &nl, &new_loan_msg, new_loan_str, Origin::Ast); - err.span_label( - new_loan.span, - format!("borrow occurs here{}", new_loan_msg)); - err.span_label( - old_loan.span, - format!("closure construction occurs here{}", old_loan_msg)); - err.span_label( - previous_end_span, - "borrow from closure ends here"); - err + self.bccx.cannot_reborrow_already_uniquely_borrowed( + new_loan.span, &nl, &new_loan_msg, new_loan_str, + old_loan.span, &old_loan_msg, previous_end_span, Origin::Ast) } - - (..) => { - let mut err = self.bccx.cannot_reborrow_already_borrowed( + (..) => + self.bccx.cannot_reborrow_already_borrowed( new_loan.span, &nl, &new_loan_msg, &new_loan.kind.to_user_str(), - &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, Origin::Ast); - err.span_label( - new_loan.span, - format!("{} borrow occurs here{}", - new_loan.kind.to_user_str(), - new_loan_msg)); - err.span_label( - old_loan.span, - format!("{} borrow occurs here{}", - old_loan.kind.to_user_str(), - old_loan_msg)); - err.span_label( - previous_end_span, - format!("{} borrow ends here", - old_loan.kind.to_user_str())); - err - } + old_loan.span, &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, + previous_end_span, Origin::Ast) }; match new_loan.cause { @@ -587,11 +532,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { _ => { } } - err.emit(); - return false; + return Err(err); } - true + Ok(()) } fn consume_common(&self, @@ -640,14 +584,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { UseOk => { } UseWhileBorrowed(loan_path, loan_span) => { let desc = self.bccx.loan_path_to_string(copy_path); - self.bccx.cannot_use_when_mutably_borrowed(span, &desc, Origin::Ast) - .span_label(loan_span, - format!("borrow of `{}` occurs here", - &self.bccx.loan_path_to_string(&loan_path)) - ) - .span_label(span, - format!("use of borrowed `{}`", - &self.bccx.loan_path_to_string(&loan_path))) + self.bccx.cannot_use_when_mutably_borrowed( + span, &desc, + loan_span, &self.bccx.loan_path_to_string(&loan_path), + Origin::Ast) .emit(); } } @@ -736,7 +676,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={:?})", id, use_kind, lp); - // FIXME (22079): if you find yourself tempted to cut and paste + // FIXME: if you find yourself tempted to cut and paste // the body below and then specializing the error reporting, // consider refactoring this instead! @@ -797,7 +737,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { // the path must be initialized to prevent a case of // partial reinitialization // - // FIXME (22079): could refactor via hypothetical + // FIXME: could refactor via hypothetical // generalized check_if_path_is_moved let loan_path = owned_ptr_base_path_rc(lp_base); self.move_data.each_move_of(id, &loan_path, |_, _| { @@ -847,7 +787,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { let lp = opt_loan_path(&assignee_cmt).unwrap(); self.move_data.each_assignment_of(assignment_id, &lp, |assign| { if assignee_cmt.mutbl.is_mutable() { - self.tcx().used_mut_nodes.borrow_mut().insert(local_id); + let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id); + self.bccx.used_mut_nodes.borrow_mut().insert(hir_id); } else { self.bccx.report_reassigned_immutable_variable( assignment_span, @@ -865,13 +806,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { loan_path: &LoanPath<'tcx>, loan: &Loan) { self.bccx.cannot_assign_to_borrowed( - span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast) - .span_label(loan.span, - format!("borrow of `{}` occurs here", - self.bccx.loan_path_to_string(loan_path))) - .span_label(span, - format!("assignment to borrowed `{}` occurs here", - self.bccx.loan_path_to_string(loan_path))) + span, loan.span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast) .emit(); } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index a58b62ba2a709..8654f2a50e46b 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -48,7 +48,13 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, move_error_collector: move_error::MoveErrorCollector::new(), }; - euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables) + let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id); + euv::ExprUseVisitor::new(&mut glcx, + bccx.tcx, + param_env, + &bccx.region_scope_tree, + bccx.tables, + Some(rvalue_promotable_map)) .consume_body(bccx.body); glcx.report_potential_errors(); @@ -406,7 +412,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { self.all_loans.push(loan); // if loan_gen_scope != borrow_id { - // FIXME(#6268) Nested method calls + // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls // // Typically, the scope of the loan includes the point at // which the loan is originated. This @@ -417,9 +423,8 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { //let restr = restrictions::compute_restrictions( // self.bccx, borrow_span, cmt, RESTR_EMPTY); //let loan = { - // let all_loans = &mut *self.all_loans; // FIXME(#5074) // Loan { - // index: all_loans.len(), + // index: self.all_loans.len(), // loan_path, // cmt, // mutbl: ConstMutability, @@ -442,13 +447,13 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { wrapped_path = match current_path.kind { LpVar(local_id) => { if !through_borrow { - self.tcx().used_mut_nodes.borrow_mut().insert(local_id); + let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id); + self.bccx.used_mut_nodes.borrow_mut().insert(hir_id); } None } LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { - let local_id = self.tcx().hir.hir_to_node_id(var_id); - self.tcx().used_mut_nodes.borrow_mut().insert(local_id); + self.bccx.used_mut_nodes.borrow_mut().insert(var_id); None } LpExtend(ref base, mc::McInherited, LpDeref(pointer_kind)) | diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index de3f6f083256d..1f2b917bdb994 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -14,6 +14,7 @@ use rustc::middle::mem_categorization::Categorization; use rustc::middle::mem_categorization::NoteClosureEnv; use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; use rustc::ty; +use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; use syntax::ast; use syntax_pos; use errors::DiagnosticBuilder; @@ -134,7 +135,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec>) } // (keep in sync with gather_moves::check_and_get_illegal_move_origin ) -fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, +fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>, move_from: mc::cmt<'tcx>) -> DiagnosticBuilder<'a> { match move_from.cat { @@ -142,43 +143,21 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, Categorization::Deref(_, mc::Implicit(..)) | Categorization::Deref(_, mc::UnsafePtr(..)) | Categorization::StaticItem => { - let mut err = struct_span_err!(bccx, move_from.span, E0507, - "cannot move out of {}", - move_from.descriptive_string(bccx.tcx)); - err.span_label( - move_from.span, - format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx)) - ); - err + bccx.cannot_move_out_of( + move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast) } - Categorization::Interior(ref b, mc::InteriorElement(ik)) => { - let type_name = match (&b.ty.sty, ik) { - (&ty::TyArray(_, _), Kind::Index) => "array", - (&ty::TySlice(_), _) => "slice", - _ => { - span_bug!(move_from.span, "this path should not cause illegal move"); - }, - }; - let mut err = struct_span_err!(bccx, move_from.span, E0508, - "cannot move out of type `{}`, \ - a non-copy {}", - b.ty, type_name); - err.span_label(move_from.span, "cannot move out of here"); - err + bccx.cannot_move_out_of_interior_noncopy( + move_from.span, b.ty, ik == Kind::Index, Origin::Ast) } Categorization::Downcast(ref b, _) | Categorization::Interior(ref b, mc::InteriorField(_)) => { match b.ty.sty { ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => { - let mut err = struct_span_err!(bccx, move_from.span, E0509, - "cannot move out of type `{}`, \ - which implements the `Drop` trait", - b.ty); - err.span_label(move_from.span, "cannot move out of here"); - err - }, + bccx.cannot_move_out_of_interior_of_drop( + move_from.span, b.ty, Origin::Ast) + } _ => { span_bug!(move_from.span, "this path should not cause illegal move"); } diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index ef93e0365e66d..40837c5e8d699 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -20,6 +20,7 @@ pub use self::MovedValueUseKind::*; use self::InteriorKind::*; +use rustc::hir::HirId; use rustc::hir::map as hir_map; use rustc::hir::map::blocks::FnLikeNode; use rustc::cfg; @@ -27,7 +28,8 @@ use rustc::middle::dataflow::DataFlowContext; use rustc::middle::dataflow::BitwiseOperator; use rustc::middle::dataflow::DataFlowOperator; use rustc::middle::dataflow::KillFrom; -use rustc::hir::def_id::{DefId, DefIndex}; +use rustc::middle::borrowck::BorrowCheckResult; +use rustc::hir::def_id::{DefId, LocalDefId}; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; @@ -37,13 +39,15 @@ use rustc::middle::free_region::RegionRelations; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; +use rustc::util::nodemap::FxHashSet; +use std::cell::RefCell; use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; use syntax::ast; use syntax_pos::{MultiSpan, Span}; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, DiagnosticId}; use rustc::hir; use rustc::hir::intravisit::{self, Visitor}; @@ -54,6 +58,8 @@ pub mod gather_loans; pub mod move_data; +mod unused; + #[derive(Clone, Copy)] pub struct LoanDataFlowOperator; @@ -79,7 +85,9 @@ pub struct AnalysisData<'a, 'tcx: 'a> { pub move_data: move_data::FlowedMoveData<'a, 'tcx>, } -fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { +fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) + -> Rc +{ debug!("borrowck(body_owner_def_id={:?})", owner_def_id); let owner_id = tcx.hir.as_local_node_id(owner_def_id).unwrap(); @@ -91,7 +99,9 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { // those things (notably the synthesized constructors from // tuple structs/variants) do not have an associated body // and do not need borrowchecking. - return; + return Rc::new(BorrowCheckResult { + used_mut_nodes: FxHashSet(), + }) } _ => { } } @@ -100,7 +110,14 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { let tables = tcx.typeck_tables_of(owner_def_id); let region_scope_tree = tcx.region_scope_tree(owner_def_id); let body = tcx.hir.body(body_id); - let bccx = &mut BorrowckCtxt { tcx, tables, region_scope_tree, owner_def_id, body }; + let mut bccx = BorrowckCtxt { + tcx, + tables, + region_scope_tree, + owner_def_id, + body, + used_mut_nodes: RefCell::new(FxHashSet()), + }; // Eventually, borrowck will always read the MIR, but at the // moment we do not. So, for now, we always force MIR to be @@ -118,14 +135,19 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { if let Some(AnalysisData { all_loans, loans: loan_dfcx, move_data: flowed_moves }) = - build_borrowck_dataflow_data(bccx, false, body_id, + build_borrowck_dataflow_data(&mut bccx, false, body_id, |bccx| { cfg = Some(cfg::CFG::new(bccx.tcx, &body)); cfg.as_mut().unwrap() }) { - check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans, body); + check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body); } + unused::check(&mut bccx, body); + + Rc::new(BorrowCheckResult { + used_mut_nodes: bccx.used_mut_nodes.into_inner(), + }) } fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>, @@ -198,7 +220,14 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( let tables = tcx.typeck_tables_of(owner_def_id); let region_scope_tree = tcx.region_scope_tree(owner_def_id); let body = tcx.hir.body(body_id); - let mut bccx = BorrowckCtxt { tcx, tables, region_scope_tree, owner_def_id, body }; + let mut bccx = BorrowckCtxt { + tcx, + tables, + region_scope_tree, + owner_def_id, + body, + used_mut_nodes: RefCell::new(FxHashSet()), + }; let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg); (bccx, dataflow_data.unwrap()) @@ -219,13 +248,15 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> { owner_def_id: DefId, body: &'tcx hir::Body, + + used_mut_nodes: RefCell>, } impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.tcx.sess.struct_span_err_with_code(sp, msg, code) @@ -238,6 +269,17 @@ impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { { self.tcx.sess.struct_span_err(sp, msg) } + + fn cancel_if_wrong_origin<'a>(&'a self, + mut diag: DiagnosticBuilder<'a>, + o: Origin) + -> DiagnosticBuilder<'a> + { + if !o.should_emit_errors(self.tcx.sess.opts.borrowck_mode) { + self.tcx.sess.diagnostic().cancel(&mut diag); + } + diag + } } /////////////////////////////////////////////////////////////////////////// @@ -345,9 +387,9 @@ pub enum LoanPathElem<'tcx> { LpInterior(Option, InteriorKind), } -fn closure_to_block(closure_id: DefIndex, +fn closure_to_block(closure_id: LocalDefId, tcx: TyCtxt) -> ast::NodeId { - let closure_id = tcx.hir.def_index_to_node_id(closure_id); + let closure_id = tcx.hir.local_def_id_to_node_id(closure_id); match tcx.hir.get(closure_id) { hir_map::NodeExpr(expr) => match expr.node { hir::ExprClosure(.., body_id, _, _) => { @@ -614,19 +656,18 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let partial = moved_lp.depth() > lp.depth(); let msg = if !has_fork && partial { "partially " } else if has_fork && !has_common { "collaterally "} - else { "" }; - let mut err = struct_span_err!( - self.tcx.sess, use_span, E0382, - "{} of {}moved value: `{}`", - verb, msg, nl); + else { "" }; + let mut err = self.cannot_act_on_moved_value(use_span, + verb, + msg, + &format!("{}", nl), + Origin::Ast); let need_note = match lp.ty.sty { ty::TypeVariants::TyClosure(id, _) => { let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); let hir_id = self.tcx.hir.node_to_hir_id(node_id); - if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) = - self.tables.closure_kinds().get(hir_id) - { - err.span_note(span, &format!( + if let Some((span, name)) = self.tables.closure_kind_origins().get(hir_id) { + err.span_note(*span, &format!( "closure cannot be invoked more than once because \ it moves the variable `{}` out of its environment", name @@ -698,10 +739,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { &self, span: Span, lp: &LoanPath<'tcx>) { - span_err!( - self.tcx.sess, span, E0383, - "partial reinitialization of uninitialized structure `{}`", - self.loan_path_to_string(lp)); + self.cannot_partially_reinit_an_uninit_struct(span, + &self.loan_path_to_string(lp), + Origin::Ast) + .emit(); } pub fn report_reassigned_immutable_variable(&self, @@ -712,7 +753,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let mut err = self.cannot_reassign_immutable(span, &self.loan_path_to_string(lp), Origin::Ast); - err.span_label(span, "re-assignment of immutable variable"); + err.span_label(span, "cannot assign twice to immutable variable"); if span != assign.span { err.span_label(assign.span, format!("first assignment to `{}`", self.loan_path_to_string(lp))); @@ -723,12 +764,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pub fn struct_span_err_with_code>(&self, s: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.tcx.sess.struct_span_err_with_code(s, msg, code) } - pub fn span_err_with_code>(&self, s: S, msg: &str, code: &str) { + pub fn span_err_with_code>( + &self, + s: S, + msg: &str, + code: DiagnosticId, + ) { self.tcx.sess.span_err_with_code(s, msg, code); } @@ -759,15 +805,24 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let mut db = match err.cause { MutabilityViolation => { - struct_span_err!(self.tcx.sess, - error_span, - E0594, - "cannot assign to {}", - descr) + let mut db = self.cannot_assign(error_span, &descr, Origin::Ast); + if let mc::NoteClosureEnv(upvar_id) = err.cmt.note { + let node_id = self.tcx.hir.hir_to_node_id(upvar_id.var_id); + let sp = self.tcx.hir.span(node_id); + match self.tcx.sess.codemap().span_to_snippet(sp) { + Ok(snippet) => { + let msg = &format!("consider making `{}` mutable", snippet); + db.span_suggestion(sp, msg, format!("mut {}", snippet)); + } + _ => { + db.span_help(sp, "consider making this binding mutable"); + } + } + } + db } BorrowViolation(euv::ClosureCapture(_)) => { - struct_span_err!(self.tcx.sess, error_span, E0595, - "closure cannot assign to {}", descr) + self.closure_cannot_assign_to_borrowed(error_span, &descr, Origin::Ast) } BorrowViolation(euv::OverloadedOperator) | BorrowViolation(euv::AddrOf) | @@ -776,8 +831,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { BorrowViolation(euv::AutoUnsafe) | BorrowViolation(euv::ForLoop) | BorrowViolation(euv::MatchDiscriminant) => { - struct_span_err!(self.tcx.sess, error_span, E0596, - "cannot borrow {} as mutable", descr) + self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast) } BorrowViolation(euv::ClosureInvocation) => { span_bug!(err.span, @@ -859,21 +913,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Some((yield_span, _)) = maybe_borrow_across_yield { debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span); - struct_span_err!(self.tcx.sess, - error_span, - E0626, - "borrow may still be in use when generator yields") - .span_label(yield_span, "possible yield occurs here") + self.cannot_borrow_across_generator_yield(error_span, yield_span, Origin::Ast) .emit(); return; } - let mut db = struct_span_err!(self.tcx.sess, - error_span, - E0597, - "{} does not live long enough", - msg); - + let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast); let (value_kind, value_msg) = match err.cmt.cat { mc::Categorization::Rvalue(..) => ("temporary value", "temporary value created here"), @@ -982,11 +1027,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { let descr = self.cmt_to_path_or_string(&err.cmt); - let mut db = struct_span_err!(self.tcx.sess, error_span, E0598, - "lifetime of {} is too short to guarantee \ - its contents can be safely reborrowed", - descr); - + let mut db = self.lifetime_too_short_for_reborrow(error_span, &descr, Origin::Ast); let descr = match opt_loan_path(&err.cmt) { Some(lp) => { format!("`{}`", self.loan_path_to_string(&lp)) @@ -1058,12 +1099,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let blame = cmt.immutability_blame(); let mut err = match blame { Some(ImmutabilityBlame::ClosureEnv(id)) => { - let mut err = struct_span_err!( - self.tcx.sess, span, E0387, - "{} in a captured outer variable in an `Fn` closure", prefix); - // FIXME: the distinction between these 2 messages looks wrong. - let help = if let BorrowViolation(euv::ClosureCapture(_)) = kind { + let help_msg = if let BorrowViolation(euv::ClosureCapture(_)) = kind { // The aliasability violation with closure captures can // happen for nested closures, so we know the enclosing // closure incorrectly accepts an `Fn` while it needs to @@ -1073,16 +1110,16 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } else { "consider changing this closure to take self by mutable reference" }; - let node_id = self.tcx.hir.def_index_to_node_id(id); - err.span_help(self.tcx.hir.span(node_id), help); - err + let node_id = self.tcx.hir.local_def_id_to_node_id(id); + let help_span = self.tcx.hir.span(node_id); + self.cannot_act_on_capture_in_sharable_fn(span, + prefix, + (help_span, help_msg), + Origin::Ast) } _ => { - let mut err = struct_span_err!( - self.tcx.sess, span, E0389, - "{} in a `&` reference", prefix); - err.span_label(span, "assignment into an immutable reference"); - err + self.cannot_assign_into_immutable_reference(span, prefix, + Origin::Ast) } }; self.note_immutability_blame(&mut err, blame); @@ -1234,17 +1271,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { Err(_) => format!("move || ") }; - struct_span_err!(self.tcx.sess, err.span, E0373, - "closure may outlive the current function, \ - but it borrows {}, \ - which is owned by the current function", - cmt_path_or_string) - .span_label(capture_span, - format!("{} is borrowed here", - cmt_path_or_string)) - .span_label(err.span, - format!("may outlive borrowed value {}", - cmt_path_or_string)) + self.cannot_capture_in_long_lived_closure(err.span, + &cmt_path_or_string, + capture_span, + Origin::Ast) .span_suggestion(err.span, &format!("to force the closure to take ownership of {} \ (and any other referenced variables), \ @@ -1276,7 +1306,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { }; if kind == ty::ClosureKind::Fn { let closure_node_id = - self.tcx.hir.def_index_to_node_id(upvar_id.closure_expr_id); + self.tcx.hir.local_def_id_to_node_id(upvar_id.closure_expr_id); db.span_help(self.tcx.hir.span(closure_node_id), "consider changing this closure to take \ self by mutable reference"); diff --git a/src/librustc_borrowck/borrowck/unused.rs b/src/librustc_borrowck/borrowck/unused.rs new file mode 100644 index 0000000000000..ddee122d0a6bd --- /dev/null +++ b/src/librustc_borrowck/borrowck/unused.rs @@ -0,0 +1,118 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::intravisit::{Visitor, NestedVisitorMap}; +use rustc::hir::{self, HirId}; +use rustc::lint::builtin::UNUSED_MUT; +use rustc::ty; +use rustc::util::nodemap::{FxHashMap, FxHashSet}; +use std::slice; +use syntax::ptr::P; + +use borrowck::BorrowckCtxt; + +pub fn check<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, body: &'tcx hir::Body) { + let mut used_mut = bccx.used_mut_nodes.borrow().clone(); + UsedMutFinder { + bccx, + set: &mut used_mut, + }.visit_expr(&body.value); + let mut cx = UnusedMutCx { bccx, used_mut }; + for arg in body.arguments.iter() { + cx.check_unused_mut_pat(slice::from_ref(&arg.pat)); + } + cx.visit_expr(&body.value); +} + +struct UsedMutFinder<'a, 'tcx: 'a> { + bccx: &'a BorrowckCtxt<'a, 'tcx>, + set: &'a mut FxHashSet, +} + +struct UnusedMutCx<'a, 'tcx: 'a> { + bccx: &'a BorrowckCtxt<'a, 'tcx>, + used_mut: FxHashSet, +} + +impl<'a, 'tcx> UnusedMutCx<'a, 'tcx> { + fn check_unused_mut_pat(&self, pats: &[P]) { + let tcx = self.bccx.tcx; + let mut mutables = FxHashMap(); + for p in pats { + p.each_binding(|_, id, span, path1| { + let name = path1.node; + + // Skip anything that looks like `_foo` + if name.as_str().starts_with("_") { + return + } + + // Skip anything that looks like `&foo` or `&mut foo`, only look + // for by-value bindings + let hir_id = tcx.hir.node_to_hir_id(id); + let bm = match self.bccx.tables.pat_binding_modes().get(hir_id) { + Some(&bm) => bm, + None => span_bug!(span, "missing binding mode"), + }; + match bm { + ty::BindByValue(hir::MutMutable) => {} + _ => return, + } + + mutables.entry(name).or_insert(Vec::new()).push((id, hir_id, span)); + }); + } + + for (_name, ids) in mutables { + // If any id for this name was used mutably then consider them all + // ok, so move on to the next + if ids.iter().any(|&(_, ref id, _)| self.used_mut.contains(id)) { + continue + } + + let mut_span = tcx.sess.codemap().span_until_char(ids[0].2, ' '); + + // Ok, every name wasn't used mutably, so issue a warning that this + // didn't need to be mutable. + tcx.struct_span_lint_node(UNUSED_MUT, + ids[0].0, + ids[0].2, + "variable does not need to be mutable") + .span_suggestion_short(mut_span, "remove this `mut`", "".to_owned()) + .emit(); + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for UnusedMutCx<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir) + } + + fn visit_arm(&mut self, arm: &hir::Arm) { + self.check_unused_mut_pat(&arm.pats) + } + + fn visit_local(&mut self, local: &hir::Local) { + self.check_unused_mut_pat(slice::from_ref(&local.pat)); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedMutFinder<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir) + } + + fn visit_nested_body(&mut self, id: hir::BodyId) { + let def_id = self.bccx.tcx.hir.body_owner_def_id(id); + self.set.extend(self.bccx.tcx.borrowck(def_id).used_mut_nodes.iter().cloned()); + self.visit_body(self.bccx.tcx.hir.body(id)); + } +} diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 74133c821f0fa..3fea01443be4b 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -9,739 +9,3 @@ // except according to those terms. #![allow(non_snake_case)] - -register_long_diagnostics! { - -E0373: r##" -This error occurs when an attempt is made to use data captured by a closure, -when that data may no longer exist. It's most commonly seen when attempting to -return a closure: - -```compile_fail,E0373 -fn foo() -> Box u32> { - let x = 0u32; - Box::new(|y| x + y) -} -``` - -Notice that `x` is stack-allocated by `foo()`. By default, Rust captures -closed-over data by reference. This means that once `foo()` returns, `x` no -longer exists. An attempt to access `x` within the closure would thus be -unsafe. - -Another situation where this might be encountered is when spawning threads: - -```compile_fail,E0373 -fn foo() { - let x = 0u32; - let y = 1u32; - - let thr = std::thread::spawn(|| { - x + y - }); -} -``` - -Since our new thread runs in parallel, the stack frame containing `x` and `y` -may well have disappeared by the time we try to use them. Even if we call -`thr.join()` within foo (which blocks until `thr` has completed, ensuring the -stack frame won't disappear), we will not succeed: the compiler cannot prove -that this behaviour is safe, and so won't let us do it. - -The solution to this problem is usually to switch to using a `move` closure. -This approach moves (or copies, where possible) data into the closure, rather -than taking references to it. For example: - -``` -fn foo() -> Box u32> { - let x = 0u32; - Box::new(move |y| x + y) -} -``` - -Now that the closure has its own copy of the data, there's no need to worry -about safety. -"##, - -E0382: r##" -This error occurs when an attempt is made to use a variable after its contents -have been moved elsewhere. For example: - -```compile_fail,E0382 -struct MyStruct { s: u32 } - -fn main() { - let mut x = MyStruct{ s: 5u32 }; - let y = x; - x.s = 6; - println!("{}", x.s); -} -``` - -Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out -of `x` when we set `y`. This is fundamental to Rust's ownership system: outside -of workarounds like `Rc`, a value cannot be owned by more than one variable. - -If we own the type, the easiest way to address this problem is to implement -`Copy` and `Clone` on it, as shown below. This allows `y` to copy the -information in `x`, while leaving the original version owned by `x`. Subsequent -changes to `x` will not be reflected when accessing `y`. - -``` -#[derive(Copy, Clone)] -struct MyStruct { s: u32 } - -fn main() { - let mut x = MyStruct{ s: 5u32 }; - let y = x; - x.s = 6; - println!("{}", x.s); -} -``` - -Alternatively, if we don't control the struct's definition, or mutable shared -ownership is truly required, we can use `Rc` and `RefCell`: - -``` -use std::cell::RefCell; -use std::rc::Rc; - -struct MyStruct { s: u32 } - -fn main() { - let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 })); - let y = x.clone(); - x.borrow_mut().s = 6; - println!("{}", x.borrow().s); -} -``` - -With this approach, x and y share ownership of the data via the `Rc` (reference -count type). `RefCell` essentially performs runtime borrow checking: ensuring -that at most one writer or multiple readers can access the data at any one time. - -If you wish to learn more about ownership in Rust, start with the chapter in the -Book: - -https://doc.rust-lang.org/book/first-edition/ownership.html -"##, - -E0383: r##" -This error occurs when an attempt is made to partially reinitialize a -structure that is currently uninitialized. - -For example, this can happen when a drop has taken place: - -```compile_fail,E0383 -struct Foo { - a: u32, -} -impl Drop for Foo { - fn drop(&mut self) { /* ... */ } -} - -let mut x = Foo { a: 1 }; -drop(x); // `x` is now uninitialized -x.a = 2; // error, partial reinitialization of uninitialized structure `t` -``` - -This error can be fixed by fully reinitializing the structure in question: - -``` -struct Foo { - a: u32, -} -impl Drop for Foo { - fn drop(&mut self) { /* ... */ } -} - -let mut x = Foo { a: 1 }; -drop(x); -x = Foo { a: 2 }; -``` -"##, - -/*E0386: r##" -This error occurs when an attempt is made to mutate the target of a mutable -reference stored inside an immutable container. - -For example, this can happen when storing a `&mut` inside an immutable `Box`: - -```compile_fail,E0386 -let mut x: i64 = 1; -let y: Box<_> = Box::new(&mut x); -**y = 2; // error, cannot assign to data in an immutable container -``` - -This error can be fixed by making the container mutable: - -``` -let mut x: i64 = 1; -let mut y: Box<_> = Box::new(&mut x); -**y = 2; -``` - -It can also be fixed by using a type with interior mutability, such as `Cell` -or `RefCell`: - -``` -use std::cell::Cell; - -let x: i64 = 1; -let y: Box> = Box::new(Cell::new(x)); -y.set(2); -``` -"##,*/ - -E0387: r##" -This error occurs when an attempt is made to mutate or mutably reference data -that a closure has captured immutably. Examples of this error are shown below: - -```compile_fail,E0387 -// Accepts a function or a closure that captures its environment immutably. -// Closures passed to foo will not be able to mutate their closed-over state. -fn foo(f: F) { } - -// Attempts to mutate closed-over data. Error message reads: -// `cannot assign to data in a captured outer variable...` -fn mutable() { - let mut x = 0u32; - foo(|| x = 2); -} - -// Attempts to take a mutable reference to closed-over data. Error message -// reads: `cannot borrow data mutably in a captured outer variable...` -fn mut_addr() { - let mut x = 0u32; - foo(|| { let y = &mut x; }); -} -``` - -The problem here is that foo is defined as accepting a parameter of type `Fn`. -Closures passed into foo will thus be inferred to be of type `Fn`, meaning that -they capture their context immutably. - -If the definition of `foo` is under your control, the simplest solution is to -capture the data mutably. This can be done by defining `foo` to take FnMut -rather than Fn: - -``` -fn foo(f: F) { } -``` - -Alternatively, we can consider using the `Cell` and `RefCell` types to achieve -interior mutability through a shared reference. Our example's `mutable` -function could be redefined as below: - -``` -use std::cell::Cell; - -fn foo(f: F) { } - -fn mutable() { - let x = Cell::new(0u32); - foo(|| x.set(2)); -} -``` - -You can read more about cell types in the API documentation: - -https://doc.rust-lang.org/std/cell/ -"##, - -E0388: r##" -E0388 was removed and is no longer issued. -"##, - -E0389: r##" -An attempt was made to mutate data using a non-mutable reference. This -commonly occurs when attempting to assign to a non-mutable reference of a -mutable reference (`&(&mut T)`). - -Example of erroneous code: - -```compile_fail,E0389 -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - let fancy_ref = &(&mut fancy); - fancy_ref.num = 6; // error: cannot assign to data in a `&` reference - println!("{}", fancy_ref.num); -} -``` - -Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an -immutable reference to a value borrows it immutably. There can be multiple -references of type `&(&mut T)` that point to the same value, so they must be -immutable to prevent multiple mutable references to the same value. - -To fix this, either remove the outer reference: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - - let fancy_ref = &mut fancy; - // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum) - - fancy_ref.num = 6; // No error! - - println!("{}", fancy_ref.num); -} -``` - -Or make the outer reference mutable: - -``` -struct FancyNum { - num: u8 -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - - let fancy_ref = &mut (&mut fancy); - // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum) - - fancy_ref.num = 6; // No error! - - println!("{}", fancy_ref.num); -} -``` -"##, - -E0507: r##" -You tried to move out of a value which was borrowed. Erroneous code example: - -```compile_fail,E0507 -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // error: cannot move out of borrowed content -} -``` - -Here, the `nothing_is_true` method takes the ownership of `self`. However, -`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`, -which is a borrow of the content owned by the `RefCell`. To fix this error, -you have three choices: - -* Try to avoid moving the variable. -* Somehow reclaim the ownership. -* Implement the `Copy` trait on the type. - -Examples: - -``` -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(&self) {} // First case, we don't take ownership -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // ok! -} -``` - -Or: - -``` -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - let x = x.into_inner(); // we get back ownership - - x.nothing_is_true(); // ok! -} -``` - -Or: - -``` -use std::cell::RefCell; - -#[derive(Clone, Copy)] // we implement the Copy trait -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // ok! -} -``` - -Moving a member out of a mutably borrowed struct will also cause E0507 error: - -```compile_fail,E0507 -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -struct Batcave { - knight: TheDarkKnight -} - -fn main() { - let mut cave = Batcave { - knight: TheDarkKnight - }; - let borrowed = &mut cave; - - borrowed.knight.nothing_is_true(); // E0507 -} -``` - -It is fine only if you put something back. `mem::replace` can be used for that: - -``` -# struct TheDarkKnight; -# impl TheDarkKnight { fn nothing_is_true(self) {} } -# struct Batcave { knight: TheDarkKnight } -use std::mem; - -let mut cave = Batcave { - knight: TheDarkKnight -}; -let borrowed = &mut cave; - -mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! -``` - -You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html -"##, - -E0508: r##" -A value was moved out of a non-copy fixed-size array. - -Example of erroneous code: - -```compile_fail,E0508 -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`, - // a non-copy fixed-size array -} -``` - -The first element was moved out of the array, but this is not -possible because `NonCopy` does not implement the `Copy` trait. - -Consider borrowing the element instead of moving it: - -``` -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - let _value = &array[0]; // Borrowing is allowed, unlike moving. -} -``` - -Alternatively, if your type implements `Clone` and you need to own the value, -consider borrowing and then cloning: - -``` -#[derive(Clone)] -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - // Now you can clone the array element. - let _value = array[0].clone(); -} -``` -"##, - -E0509: r##" -This error occurs when an attempt is made to move out of a value whose type -implements the `Drop` trait. - -Example of erroneous code: - -```compile_fail,E0509 -struct FancyNum { - num: usize -} - -struct DropStruct { - fancy: FancyNum -} - -impl Drop for DropStruct { - fn drop(&mut self) { - // Destruct DropStruct, possibly using FancyNum - } -} - -fn main() { - let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; - let fancy_field = drop_struct.fancy; // Error E0509 - println!("Fancy: {}", fancy_field.num); - // implicit call to `drop_struct.drop()` as drop_struct goes out of scope -} -``` - -Here, we tried to move a field out of a struct of type `DropStruct` which -implements the `Drop` trait. However, a struct cannot be dropped if one or -more of its fields have been moved. - -Structs implementing the `Drop` trait have an implicit destructor that gets -called when they go out of scope. This destructor may use the fields of the -struct, so moving out of the struct could make it impossible to run the -destructor. Therefore, we must think of all values whose type implements the -`Drop` trait as single units whose fields cannot be moved. - -This error can be fixed by creating a reference to the fields of a struct, -enum, or tuple using the `ref` keyword: - -``` -struct FancyNum { - num: usize -} - -struct DropStruct { - fancy: FancyNum -} - -impl Drop for DropStruct { - fn drop(&mut self) { - // Destruct DropStruct, possibly using FancyNum - } -} - -fn main() { - let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; - let ref fancy_field = drop_struct.fancy; // No more errors! - println!("Fancy: {}", fancy_field.num); - // implicit call to `drop_struct.drop()` as drop_struct goes out of scope -} -``` - -Note that this technique can also be used in the arms of a match expression: - -``` -struct FancyNum { - num: usize -} - -enum DropEnum { - Fancy(FancyNum) -} - -impl Drop for DropEnum { - fn drop(&mut self) { - // Destruct DropEnum, possibly using FancyNum - } -} - -fn main() { - // Creates and enum of type `DropEnum`, which implements `Drop` - let drop_enum = DropEnum::Fancy(FancyNum{num: 10}); - match drop_enum { - // Creates a reference to the inside of `DropEnum::Fancy` - DropEnum::Fancy(ref fancy_field) => // No error! - println!("It was fancy-- {}!", fancy_field.num), - } - // implicit call to `drop_enum.drop()` as drop_enum goes out of scope -} -``` -"##, - -E0595: r##" -Closures cannot mutate immutable captured variables. - -Erroneous code example: - -```compile_fail,E0595 -let x = 3; // error: closure cannot assign to immutable local variable `x` -let mut c = || { x += 1 }; -``` - -Make the variable binding mutable: - -``` -let mut x = 3; // ok! -let mut c = || { x += 1 }; -``` -"##, - -E0596: r##" -This error occurs because you tried to mutably borrow a non-mutable variable. - -Example of erroneous code: - -```compile_fail,E0596 -let x = 1; -let y = &mut x; // error: cannot borrow mutably -``` - -In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it -fails. To fix this error, you need to make `x` mutable: - -``` -let mut x = 1; -let y = &mut x; // ok! -``` -"##, - -E0597: r##" -This error occurs because a borrow was made inside a variable which has a -greater lifetime than the borrowed one. - -Example of erroneous code: - -```compile_fail,E0597 -struct Foo<'a> { - x: Option<&'a u32>, -} - -let mut x = Foo { x: None }; -let y = 0; -x.x = Some(&y); // error: `y` does not live long enough -``` - -In here, `x` is created before `y` and therefore has a greater lifetime. Always -keep in mind that values in a scope are dropped in the opposite order they are -created. So to fix the previous example, just make the `y` lifetime greater than -the `x`'s one: - -``` -struct Foo<'a> { - x: Option<&'a u32>, -} - -let y = 0; -let mut x = Foo { x: None }; -x.x = Some(&y); -``` -"##, - -E0626: r##" -This error occurs because a borrow in a generator persists across a -yield point. - -```compile_fail,E0626 -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let a = &String::new(); // <-- This borrow... - yield (); // ...is still in scope here, when the yield occurs. - println!("{}", a); -}; -b.resume(); -``` - -At present, it is not permitted to have a yield that occurs while a -borrow is still in scope. To resolve this error, the borrow must -either be "contained" to a smaller scope that does not overlap the -yield or else eliminated in another way. So, for example, we might -resolve the previous example by removing the borrow and just storing -the integer by value: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let a = 3; - yield (); - println!("{}", a); -}; -b.resume(); -``` - -This is a very simple case, of course. In more complex cases, we may -wish to have more than one reference to the value that was borrowed -- -in those cases, something like the `Rc` or `Arc` types may be useful. - -This error also frequently arises with iteration: - -```compile_fail,E0626 -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - for &x in &v { // <-- borrow of `v` is still in scope... - yield x; // ...when this yield occurs. - } -}; -b.resume(); -``` - -Such cases can sometimes be resolved by iterating "by value" (or using -`into_iter()`) to avoid borrowing: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - for x in v { // <-- Take ownership of the values instead! - yield x; // <-- Now yield is OK. - } -}; -b.resume(); -``` - -If taking ownership is not an option, using indices can work too: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - let len = v.len(); // (*) - for i in 0..len { - let x = v[i]; // (*) - yield x; // <-- Now yield is OK. - } -}; -b.resume(); - -// (*) -- Unfortunately, these temporaries are currently required. -// See . -``` -"##, - -} - -register_diagnostics! { -// E0385, // {} in an aliasable location - E0594, // cannot assign to {} - E0598, // lifetime of {} is too short to guarantee its contents can be... -} diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 9bedbfed5db6d..be173db23a52a 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -15,11 +15,12 @@ #![allow(non_camel_case_types)] +#![feature(from_ref)] +#![feature(match_default_bindings)] #![feature(quote)] -#![feature(rustc_diagnostic_macros)] #[macro_use] extern crate log; -#[macro_use] extern crate syntax; +extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; @@ -33,14 +34,8 @@ extern crate rustc_mir; pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; -// NB: This module needs to be declared first so diagnostics are -// registered before they are used. -mod diagnostics; - mod borrowck; pub mod graphviz; pub use borrowck::provide; - -__build_diagnostic_array! { librustc_borrowck, DIAGNOSTICS } diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml index bbc6148082494..e8d404af4defa 100644 --- a/src/librustc_const_eval/Cargo.toml +++ b/src/librustc_const_eval/Cargo.toml @@ -12,7 +12,6 @@ crate-type = ["dylib"] arena = { path = "../libarena" } log = "0.3" rustc = { path = "../librustc" } -rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index b836b71e74bf6..33d9bfa6e6b9c 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -25,7 +25,7 @@ use pattern::{PatternFoldable, PatternFolder}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; -use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; use rustc::util::common::ErrorReported; @@ -202,21 +202,33 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { if self.tcx.sess.features.borrow().never_type { - ty.is_uninhabited_from(self.module, self.tcx) + self.tcx.is_ty_uninhabited_from(self.module, ty) } else { false } } + fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::TyAdt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(), + _ => false, + } + } + + fn is_local(&self, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::TyAdt(adt_def, ..) => adt_def.did.is_local(), + _ => false, + } + } + fn is_variant_uninhabited(&self, variant: &'tcx ty::VariantDef, - substs: &'tcx ty::subst::Substs<'tcx>) -> bool + substs: &'tcx ty::subst::Substs<'tcx>) + -> bool { if self.tcx.sess.features.borrow().never_type { - let forest = variant.uninhabited_from( - &mut FxHashMap::default(), self.tcx, substs, AdtKind::Enum - ); - forest.contains(self.tcx, self.module) + self.tcx.is_enum_variant_uninhabited_from(self.module, variant, substs) } else { false } @@ -243,7 +255,7 @@ impl<'tcx> Constructor<'tcx> { match self { &Variant(vid) => adt.variant_index_with_id(vid), &Single => { - assert_eq!(adt.variants.len(), 1); + assert!(!adt.is_enum()); 0 } _ => bug!("bad constructor {:?} for adt {:?}", self, adt) @@ -344,7 +356,7 @@ impl<'tcx> Witness<'tcx> { }).collect(); if let ty::TyAdt(adt, substs) = ty.sty { - if adt.variants.len() > 1 { + if adt.is_enum() { PatternKind::Variant { adt_def: adt, substs, @@ -432,7 +444,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect() } } - ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { + ty::TyAdt(def, substs) if def.is_enum() => { def.variants.iter() .filter(|v| !cx.is_variant_uninhabited(v, substs)) .map(|v| Variant(v.did)) @@ -630,9 +642,16 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); - debug!("missing_ctors={:?} is_privately_empty={:?}", missing_ctors, - is_privately_empty); - if missing_ctors.is_empty() && !is_privately_empty { + let is_declared_nonexhaustive = + cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); + debug!("missing_ctors={:?} is_privately_empty={:?} is_declared_nonexhaustive={:?}", + missing_ctors, is_privately_empty, is_declared_nonexhaustive); + + // For privately empty and non-exhaustive enums, we work as if there were an "extra" + // `_` constructor for the type, so we can never match over all constructors. + let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive; + + if missing_ctors.is_empty() && !is_non_exhaustive { 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) @@ -647,7 +666,51 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, match is_useful(cx, &matrix, &v[1..], witness) { UsefulWithWitness(pats) => { let cx = &*cx; - let new_witnesses = if used_ctors.is_empty() { + // In this case, there's at least one "free" + // constructor that is only matched against by + // wildcard patterns. + // + // There are 2 ways we can report a witness here. + // Commonly, we can report all the "free" + // constructors as witnesses, e.g. if we have: + // + // ``` + // enum Direction { N, S, E, W } + // let Direction::N = ...; + // ``` + // + // we can report 3 witnesses: `S`, `E`, and `W`. + // + // However, there are 2 cases where we don't want + // to do this and instead report a single `_` witness: + // + // 1) If the user is matching against a non-exhaustive + // enum, there is no point in enumerating all possible + // variants, because the user can't actually match + // against them himself, e.g. in an example like: + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // we don't want to show every possible IO error, + // but instead have `_` as the witness (this is + // actually *required* if the user specified *all* + // IO errors, but is probably what we want in every + // case). + // + // 2) If the user didn't actually specify a constructor + // in this arm, e.g. in + // ``` + // let x: (Direction, Direction, bool) = ...; + // let (_, _, false) = x; + // ``` + // we don't want to show all 16 possible witnesses + // `(, , true)` - we are + // satisfied with `(_, _, true)`. In this case, + // `used_ctors` is empty. + let new_witnesses = if is_non_exhaustive || used_ctors.is_empty() { // All constructors are unused. Add wild patterns // rather than each individual constructor pats.into_iter().map(|mut witness| { diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 0339969f2b45a..e22f7f141642d 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -24,12 +24,14 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::lint; use rustc_errors::DiagnosticBuilder; +use rustc::util::common::ErrorReported; use rustc::hir::def::*; -use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, Pat, PatKind}; -use rustc_back::slice; +use std::slice; use syntax::ast; use syntax::ptr::P; @@ -42,19 +44,10 @@ impl<'a, 'tcx> Visitor<'tcx> for OuterVisitor<'a, 'tcx> { NestedVisitorMap::OnlyBodies(&self.tcx.hir) } - fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl, - b: hir::BodyId, s: Span, id: ast::NodeId) { - intravisit::walk_fn(self, fk, fd, b, s, id); - - let def_id = self.tcx.hir.local_def_id(id); - - MatchVisitor { - tcx: self.tcx, - tables: self.tcx.body_tables(b), - region_scope_tree: &self.tcx.region_scope_tree(def_id), - param_env: self.tcx.param_env(def_id), - identity_substs: Substs::identity_for_item(self.tcx, def_id), - }.visit_body(self.tcx.hir.body(b)); + fn visit_body(&mut self, body: &'tcx hir::Body) { + intravisit::walk_body(self, body); + let def_id = self.tcx.hir.body_owner_def_id(body.id()); + let _ = self.tcx.check_match(def_id); } } @@ -63,6 +56,27 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.sess.abort_if_errors(); } +pub(crate) fn check_match<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, +) -> Result<(), ErrorReported> { + let body_id = if let Some(id) = tcx.hir.as_local_node_id(def_id) { + tcx.hir.body_owned_by(id) + } else { + return Ok(()); + }; + + tcx.sess.track_errors(|| { + MatchVisitor { + tcx, + tables: tcx.body_tables(body_id), + region_scope_tree: &tcx.region_scope_tree(def_id), + param_env: tcx.param_env(def_id), + identity_substs: Substs::identity_for_item(tcx, def_id), + }.visit_body(tcx.hir.body(body_id)); + }) +} + fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> DiagnosticBuilder<'a> { struct_span_err!(sess, sp, E0004, "{}", &error_message) } @@ -100,7 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { }); // Check legality of move bindings and `@` patterns. - self.check_patterns(false, slice::ref_slice(&loc.pat)); + self.check_patterns(false, slice::from_ref(&loc.pat)); } fn visit_body(&mut self, body: &'tcx hir::Body) { @@ -108,7 +122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { for arg in &body.arguments { self.check_irrefutable(&arg.pat, "function argument"); - self.check_patterns(false, slice::ref_slice(&arg.pat)); + self.check_patterns(false, slice::from_ref(&arg.pat)); } } } @@ -192,7 +206,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let module = self.tcx.hir.get_module_parent(scrut.id); if inlined_arms.is_empty() { let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type { - pat_ty.is_uninhabited_from(module, self.tcx) + self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { self.conservative_is_uninhabited(pat_ty) }; @@ -260,7 +274,14 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "refutable pattern in {}: `{}` not covered", origin, pattern_string ); - diag.span_label(pat.span, format!("pattern `{}` not covered", pattern_string)); + let label_msg = match pat.node { + PatKind::Path(hir::QPath::Resolved(None, ref path)) + if path.segments.len() == 1 && path.segments[0].parameters.is_none() => { + format!("interpreted as a {} pattern, not new variable", path.def.kind_name()) + } + _ => format!("pattern `{}` not covered", pattern_string), + }; + diag.span_label(pat.span, label_msg); diag.emit(); }); } @@ -526,7 +547,7 @@ fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) { let mut checker = MutationChecker { cx, }; - ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables) + ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables, None) .walk_expr(guard); } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 20745c27838b9..7181c2868cfa2 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -13,18 +13,22 @@ use rustc::middle::const_val::ConstAggregate::*; use rustc::middle::const_val::ErrKind::*; use rustc::middle::const_val::{ByteArray, ConstVal, ConstEvalErr, EvalResult, ErrKind}; -use rustc::hir::map as hir_map; use rustc::hir::map::blocks::FnLikeNode; -use rustc::traits; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::maps::Providers; +use rustc::ty::layout::LayoutOf; use rustc::ty::util::IntTypeExt; use rustc::ty::subst::{Substs, Subst}; use rustc::util::common::ErrorReported; use rustc::util::nodemap::NodeMap; +use rustc::mir::interpret::{PrimVal, Value, PtrAndAlign, HasMemory, EvalError}; +use rustc::mir::interpret::{CompileTimeFunctionEvaluator, EvalContext, Machine}; +use rustc::mir::Field; +use rustc::mir::interpret::{Place, PlaceExtra}; +use rustc_data_structures::indexed_vec::Idx; + use syntax::abi::Abi; use syntax::ast; use syntax::attr; @@ -54,33 +58,12 @@ macro_rules! math { pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> Option<(DefId, &'tcx Substs<'tcx>)> { - let (def_id, _) = key.value; - if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - match tcx.hir.find(node_id) { - Some(hir_map::NodeTraitItem(_)) => { - // If we have a trait item and the substitutions for it, - // `resolve_trait_associated_const` will select an impl - // or the default. - resolve_trait_associated_const(tcx, key) - } - _ => Some(key.value) - } - } else { - match tcx.describe_def(def_id) { - Some(Def::AssociatedConst(_)) => { - // As mentioned in the comments above for in-crate - // constants, we only try to find the expression for a - // trait-associated const if the caller gives us the - // substitutions for the reference to it. - if tcx.trait_of_item(def_id).is_some() { - resolve_trait_associated_const(tcx, key) - } else { - Some(key.value) - } - } - _ => Some(key.value) - } - } + ty::Instance::resolve( + tcx, + key.param_env, + key.value.0, + key.value.1, + ).map(|instance| (instance.def_id(), instance.substs)) } pub struct ConstContext<'a, 'tcx: 'a> { @@ -119,6 +102,7 @@ type CastResult<'tcx> = Result, ErrKind<'tcx>>; fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, e: &'tcx Expr) -> EvalResult<'tcx> { + trace!("eval_const_expr_partial: {:?}", e); let tcx = cx.tcx; let ty = cx.tables.expr_ty(e).subst(tcx, cx.substs); let mk_const = |val| tcx.mk_const(ty::Const { val, ty }); @@ -289,6 +273,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, match cx.tables.qpath_def(qpath, e.hir_id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { + let substs = tcx.normalize_associated_type_in_env(&substs, cx.param_env); match tcx.at(e.span).const_eval(cx.param_env.and((def_id, substs))) { Ok(val) => val, Err(ConstEvalErr { kind: TypeckError, .. }) => { @@ -334,18 +319,18 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic { let layout_of = |ty: Ty<'tcx>| { let ty = tcx.erase_regions(&ty); - tcx.at(e.span).layout_raw(cx.param_env.reveal_all().and(ty)).map_err(|err| { + (tcx.at(e.span), cx.param_env).layout_of(ty).map_err(|err| { ConstEvalErr { span: e.span, kind: LayoutError(err) } }) }; match &tcx.item_name(def_id)[..] { "size_of" => { - let size = layout_of(substs.type_at(0))?.size(tcx).bytes(); + let size = layout_of(substs.type_at(0))?.size.bytes(); return Ok(mk_const(Integral(Usize(ConstUsize::new(size, tcx.sess.target.usize_ty).unwrap())))); } "min_align_of" => { - let align = layout_of(substs.type_at(0))?.align(tcx).abi(); + let align = layout_of(substs.type_at(0))?.align.abi(); return Ok(mk_const(Integral(Usize(ConstUsize::new(align, tcx.sess.target.usize_ty).unwrap())))); } @@ -486,67 +471,6 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, Ok(result) } -fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { - let param_env = key.param_env; - let (def_id, substs) = key.value; - let trait_item = tcx.associated_item(def_id); - let trait_id = trait_item.container.id(); - let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); - debug!("resolve_trait_associated_const: trait_ref={:?}", - trait_ref); - - tcx.infer_ctxt().enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), - param_env, - trait_ref.to_poly_trait_predicate()); - let selection = match selcx.select(&obligation) { - Ok(Some(vtable)) => vtable, - // Still ambiguous, so give up and let the caller decide whether this - // expression is really needed yet. Some associated constant values - // can't be evaluated until monomorphization is done in trans. - Ok(None) => { - return None - } - Err(_) => { - return None - } - }; - - // NOTE: this code does not currently account for specialization, but when - // it does so, it should hook into the param_env.reveal to determine when the - // constant should resolve. - match selection { - traits::VtableImpl(ref impl_data) => { - let name = trait_item.name; - let ac = tcx.associated_items(impl_data.impl_def_id) - .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); - match ac { - // FIXME(eddyb) Use proper Instance resolution to - // get the correct Substs returned from here. - Some(ic) => { - let substs = Substs::identity_for_item(tcx, ic.def_id); - Some((ic.def_id, substs)) - } - None => { - if trait_item.defaultness.has_value() { - Some(key.value) - } else { - None - } - } - } - } - traits::VtableParam(_) => None, - _ => { - bug!("resolve_trait_associated_const: unexpected vtable type {:?}", selection) - } - } - }) -} - fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: Ty<'tcx>) @@ -765,16 +689,10 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> { } } -pub fn provide(providers: &mut Providers) { - *providers = Providers { - const_eval, - ..*providers - }; -} - -fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx> { + trace!("const eval: {:?}", key); let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) { resolved } else { @@ -786,81 +704,271 @@ fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let tables = tcx.typeck_tables_of(def_id); let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) { + let body_id = tcx.hir.body_owned_by(id); + + // Do match-check before building MIR + if tcx.check_match(def_id).is_err() { + return Err(ConstEvalErr { + span: tcx.def_span(key.value.0), + kind: CheckMatchError, + }); + } + tcx.mir_const_qualif(def_id); - tcx.hir.body(tcx.hir.body_owned_by(id)) + tcx.hir.body(body_id) } else { tcx.extern_const_body(def_id).body }; - let instance = ty::Instance::new(def_id, substs); - let miri_result = ::rustc::mir::interpret::eval_body_as_primval(tcx, instance); + // do not continue into miri if typeck errors occurred + // it will fail horribly + if tables.tainted_by_errors { + signal!(&body.value, TypeckError); + } + + trace!("running old const eval"); let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value); + trace!("old const eval produced {:?}", old_result); + let instance = ty::Instance::new(def_id, substs); + trace!("const eval instance: {:?}, {:?}", instance, key.param_env); + let miri_result = ::rustc::mir::interpret::eval_body(tcx, instance, key.param_env); match (miri_result, old_result) { - (Err(err), Ok(ok)) => { - panic!("miri fails to eval {:?} to {:?} with error {:?}", key, ok, err); - //Ok(ok) + ((Err(err), ecx), Ok(ok)) => { + trace!("miri failed, ctfe returned {:?}", ok); + tcx.sess.span_warn( + tcx.def_span(key.value.0), + "miri failed to eval, while ctfe succeeded", + ); + let () = unwrap_miri(&ecx, Err(err)); + Ok(ok) }, - (Ok(ok), Err(err)) => { - panic!("miri can eval {:?} to {:?}, while old ctfe fails with {:?}", key, ok, err); - //Err(err) + ((Ok(_), _), Err(err)) => { + Err(err) }, - (Err(_), Err(err)) => Err(err), - (Ok((miri_val, miri_ty)), Ok(ctfe)) => { - use rustc::ty::TypeVariants::*; - use rustc::mir::interpret::PrimVal; - match (miri_val, &miri_ty.sty, ctfe.val) { - (PrimVal::Undef, _, _) => { - panic!("miri produced an undef, while old ctfe produced {:?}", ctfe); - }, - (PrimVal::Ptr(_), _, _) => { - panic!("miri produced a pointer, which isn't implemented yet"); - }, - (PrimVal::Bytes(b), &TyInt(int_ty), ConstVal::Integral(ci)) => { - let c = ConstInt::new_signed_truncating(b as i128, - int_ty, - tcx.sess.target.isize_ty); - if c != ci { - panic!("miri evaluated to {}, but ctfe yielded {}", b as i128, ci); - } - } - (PrimVal::Bytes(b), &TyUint(int_ty), ConstVal::Integral(ci)) => { - let c = ConstInt::new_unsigned_truncating(b, int_ty, tcx.sess.target.usize_ty); - if c != ci { - panic!("miri evaluated to {}, but ctfe yielded {}", b, ci); - } - } - (PrimVal::Bytes(bits), &TyFloat(ty), ConstVal::Float(cf)) => { - let f = ConstFloat { bits, ty }; - if f != cf { - panic!("miri evaluated to {}, but ctfe yielded {}", f, cf); - } - } - (PrimVal::Bytes(bits), &TyBool, ConstVal::Bool(b)) => { - if bits == 0 && b { - panic!("miri evaluated to {}, but ctfe yielded {}", bits == 0, b); - } else if bits == 1 && !b { - panic!("miri evaluated to {}, but ctfe yielded {}", bits == 1, b); - } else { - panic!("miri evaluated to {}, but expected a bool {}", bits, b); - } - } - (PrimVal::Bytes(bits), &TyChar, ConstVal::Char(c)) => { - if let Some(cm) = ::std::char::from_u32(bits as u32) { - if cm != c { - panic!("miri evaluated to {:?}, but expected {:?}", cm, c); - } + ((Err(_), _), Err(err)) => Err(err), + ((Ok((miri_val, miri_ty)), mut ecx), Ok(ctfe)) => { + check_ctfe_against_miri(&mut ecx, miri_val, miri_ty, ctfe.val); + Ok(ctfe) + } + } +} + +fn check_ctfe_against_miri<'a, 'tcx>( + ecx: &mut EvalContext<'a, 'tcx, CompileTimeFunctionEvaluator>, + miri_val: PtrAndAlign, + miri_ty: Ty<'tcx>, + ctfe: ConstVal<'tcx>, +) { + use rustc::ty::TypeVariants::*; + match miri_ty.sty { + TyInt(int_ty) => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let prim = get_prim(ecx, value); + let c = ConstInt::new_signed_truncating(prim as i128, + int_ty, + ecx.tcx.sess.target.isize_ty); + let c = ConstVal::Integral(c); + assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); + }, + TyUint(uint_ty) => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let prim = get_prim(ecx, value); + let c = ConstInt::new_unsigned_truncating(prim, + uint_ty, + ecx.tcx.sess.target.usize_ty); + let c = ConstVal::Integral(c); + assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe); + }, + TyFloat(ty) => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let prim = get_prim(ecx, value); + let f = ConstVal::Float(ConstFloat { bits: prim, ty }); + assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe); + }, + TyBool => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let bits = get_prim(ecx, value); + if bits > 1 { + bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe); + } + let b = ConstVal::Bool(bits == 1); + assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe); + }, + TyChar => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let bits = get_prim(ecx, value); + if let Some(cm) = ::std::char::from_u32(bits as u32) { + assert_eq!( + ConstVal::Char(cm), ctfe, + "miri evaluated to {:?}, but expected {:?}", cm, ctfe, + ); + } else { + bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe); + } + }, + TyStr => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + if let Ok(Some(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)))) = value { + let bytes = ecx + .memory + .read_bytes(ptr.into(), len as u64) + .expect("bad miri memory for str"); + if let Ok(s) = ::std::str::from_utf8(bytes) { + if let ConstVal::Str(s2) = ctfe { + assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2); } else { - panic!("miri evaluated to {}, but expected a char {:?}", bits, c); + bug!("miri produced {:?}, but expected {:?}", s, ctfe); } + } else { + bug!( + "miri failed to produce valid utf8 {:?}, while ctfe produced {:?}", + bytes, + ctfe, + ); } - _ => { - panic!("can't check whether miri's {:?} ({}) makes sense when ctfe yields {:?}", - miri_val, - miri_ty, - ctfe) - } + } else { + bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe); } - Ok(ctfe) + }, + TyArray(elem_ty, n) => { + let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + let size = ecx.type_size(elem_ty).unwrap().unwrap(); + let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe { + ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| { + (ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8) + }).collect(), + ConstVal::Aggregate(Array(v)) => { + v.iter().map(|c| (c.val, c.ty)).collect() + }, + ConstVal::Aggregate(Repeat(v, n)) => { + vec![(v.val, v.ty); n as usize] + }, + _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), + }; + for (i, elem) in vec.into_iter().enumerate() { + assert!((i as u64) < n); + let ptr = miri_val.offset(size * i as u64, &ecx).unwrap(); + check_ctfe_against_miri(ecx, ptr, elem_ty, elem.0); + } + }, + TyTuple(..) => { + let vec = match ctfe { + ConstVal::Aggregate(Tuple(v)) => v, + _ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), + }; + for (i, elem) in vec.into_iter().enumerate() { + let offset = ecx.get_field_offset(miri_ty, i).unwrap(); + let ptr = miri_val.offset(offset.bytes(), &ecx).unwrap(); + check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val); + } + }, + TyAdt(def, _) => { + let (struct_variant, extra) = if def.is_enum() { + let discr = ecx.read_discriminant_value( + Place::Ptr { ptr: miri_val, extra: PlaceExtra::None }, + miri_ty).unwrap(); + let variant = def.discriminants(ecx.tcx).position(|variant_discr| { + variant_discr.to_u128_unchecked() == discr + }).expect("miri produced invalid enum discriminant"); + (&def.variants[variant], PlaceExtra::DowncastVariant(variant)) + } else { + (def.struct_variant(), PlaceExtra::None) + }; + let vec = match ctfe { + ConstVal::Aggregate(Struct(v)) => v, + ConstVal::Variant(did) => { + assert_eq!(struct_variant.fields.len(), 0); + assert_eq!(did, struct_variant.did); + return; + }, + ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe), + }; + let layout = ecx.type_layout(miri_ty).unwrap(); + for &(name, elem) in vec.into_iter() { + let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap(); + let (place, _) = ecx.place_field( + Place::Ptr { ptr: miri_val, extra }, + Field::new(field), + layout, + ).unwrap(); + let ptr = place.to_ptr_extra_aligned().0; + check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val); + } + }, + TySlice(_) => bug!("miri produced a slice?"), + // not supported by ctfe + TyRawPtr(_) | + TyRef(..) => {} + TyDynamic(..) => bug!("miri produced a trait object"), + TyClosure(..) => bug!("miri produced a closure"), + TyGenerator(..) => bug!("miri produced a generator"), + TyNever => bug!("miri produced a value of the never type"), + TyProjection(_) => bug!("miri produced a projection"), + TyAnon(..) => bug!("miri produced an impl Trait type"), + TyParam(_) => bug!("miri produced an unmonomorphized type"), + TyInfer(_) => bug!("miri produced an uninferred type"), + TyError => bug!("miri produced a type error"), + TyForeign(_) => bug!("miri produced an extern type"), + // should be fine + TyFnDef(..) => {} + TyFnPtr(_) => { + let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| { + ectx.try_read_value(miri_val.ptr, miri_ty) + }); + let ptr = match value { + Ok(Some(Value::ByVal(PrimVal::Ptr(ptr)))) => ptr, + value => bug!("expected fn ptr, got {:?}", value), + }; + let inst = ecx.memory.get_fn(ptr).unwrap(); + match ctfe { + ConstVal::Function(did, substs) => { + let ctfe = ty::Instance::resolve( + ecx.tcx, + CompileTimeFunctionEvaluator::param_env(&ecx), + did, + substs, + ).unwrap(); + assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst); + }, + _ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst), + } + }, + } +} + +fn get_prim<'a, 'tcx>( + ecx: &mut EvalContext<'a, 'tcx, CompileTimeFunctionEvaluator>, + res: Result, EvalError<'tcx>>, +) -> u128 { + match res { + Ok(Some(Value::ByVal(prim))) => unwrap_miri(ecx, prim.to_bytes()), + Err(err) => unwrap_miri(ecx, Err(err)), + val => bug!("got {:?}", val), + } +} + +fn unwrap_miri<'a, 'tcx, T>( + ecx: &EvalContext<'a, 'tcx, CompileTimeFunctionEvaluator>, + res: Result>, +) -> T { + match res { + Ok(val) => val, + Err(mut err) => { + ecx.report(&mut err); + ecx.tcx.sess.abort_if_errors(); + bug!("{:#?}", err); } } } diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 0c3606cab10e1..d4110f0091aeb 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -24,15 +24,12 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(i128_type)] - -#![cfg_attr(stage0, feature(const_fn))] -#![cfg_attr(not(stage0), feature(const_min_value))] +#![feature(from_ref)] extern crate arena; #[macro_use] extern crate syntax; #[macro_use] extern crate log; #[macro_use] extern crate rustc; -extern crate rustc_back; extern crate rustc_const_math; extern crate rustc_data_structures; extern crate rustc_errors; @@ -49,5 +46,15 @@ pub mod pattern; pub use eval::*; +use rustc::ty::maps::Providers; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + const_eval: eval::const_eval, + check_match: check_match::check_match, + ..*providers + }; +} + // Build the diagnostics array at the end so that the metadata includes error use sites. __build_diagnostic_array! { librustc_const_eval, DIAGNOSTICS } diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 7586ad5a75f81..cfbb9623f7dc9 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -150,7 +150,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { Some(&adt_def.variants[variant_index]) } _ => if let ty::TyAdt(adt, _) = self.ty.sty { - if adt.is_univariant() { + if !adt.is_enum() { Some(&adt.variants[0]) } else { None @@ -301,6 +301,44 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { + // When implicit dereferences have been inserted in this pattern, the unadjusted lowered + // pattern has the type that results *after* dereferencing. For example, in this code: + // + // ``` + // match &&Some(0i32) { + // Some(n) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is + // determined in rustc_typeck::check::match). The adjustments would be + // + // `vec![&&Option, &Option]`. + // + // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So + // we wrap the unadjusted pattern in `PatternKind::Deref` repeatedly, consuming the + // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted + // gets the least-dereferenced type). + let unadjusted_pat = self.lower_pattern_unadjusted(pat); + self.tables + .pat_adjustments() + .get(pat.hir_id) + .unwrap_or(&vec![]) + .iter() + .rev() + .fold(unadjusted_pat, |pat, ref_ty| { + debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); + Pattern { + span: pat.span, + ty: ref_ty, + kind: Box::new(PatternKind::Deref { subpattern: pat }), + } + }, + ) + } + + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { let mut ty = self.tables.node_id_to_type(pat.hir_id); let kind = match pat.node { @@ -560,7 +598,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); let adt_def = self.tcx.adt_def(enum_id); - if adt_def.variants.len() > 1 { + if adt_def.is_enum() { let substs = match ty.sty { ty::TyAdt(_, substs) | ty::TyFnDef(_, substs) => substs, diff --git a/src/librustc_const_math/float.rs b/src/librustc_const_math/float.rs index b67048939e43e..9d820ea8cbed2 100644 --- a/src/librustc_const_math/float.rs +++ b/src/librustc_const_math/float.rs @@ -203,3 +203,11 @@ impl ::std::ops::Neg for ConstFloat { ConstFloat { bits, ty: self.ty } } } + +/// This is `f32::MAX + (0.5 ULP)` as an integer. Numbers greater or equal to this +/// are rounded to infinity when converted to `f32`. +/// +/// NB: Computed as maximum significand with an extra 1 bit added (for the half ULP) +/// shifted by the maximum exponent (accounting for normalization). +pub const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1) + << (Single::MAX_EXP - Single::PRECISION as i16); diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 0533f10104a5a..095b8bb50dc6c 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -22,10 +22,6 @@ #![feature(i128)] #![feature(i128_type)] -#![cfg_attr(stage0, feature(const_fn))] -#![cfg_attr(not(stage0), feature(const_min_value))] -#![cfg_attr(not(stage0), feature(const_max_value))] - extern crate rustc_apfloat; extern crate syntax; diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 1e67461e0556d..1b39e029604d5 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -111,7 +111,7 @@ impl ArrayVec { // 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 + // the source vector to make sure no uninitialized 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. diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index e8f9a6720872d..94edaa746f915 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -145,7 +145,7 @@ pub struct BitMatrix { } impl BitMatrix { - // Create a new `rows x columns` matrix, initially empty. + /// Create a new `rows x columns` matrix, initially empty. pub fn new(rows: usize, columns: usize) -> BitMatrix { // For every element, we need one bit for every other // element. Round up to an even number of u64s. @@ -163,9 +163,13 @@ impl BitMatrix { (start, start + u64s_per_row) } - pub fn add(&mut self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); + /// Sets the cell at `(row, column)` to true. Put another way, add + /// `column` to the bitset for `row`. + /// + /// Returns true if this changed the matrix, and false otherwies. + pub fn add(&mut self, row: usize, column: usize) -> bool { + let (start, _) = self.range(row); + let (word, mask) = word_mask(column); let vector = &mut self.vector[..]; let v1 = vector[start + word]; let v2 = v1 | mask; @@ -173,19 +177,19 @@ impl BitMatrix { v1 != v2 } - /// Do the bits from `source` contain `target`? - /// - /// Put another way, if the matrix represents (transitive) - /// reachability, can `source` reach `target`? - pub fn contains(&self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); + /// Do the bits from `row` contain `column`? Put another way, is + /// the matrix cell at `(row, column)` true? Put yet another way, + /// if the matrix represents (transitive) reachability, can + /// `row` reach `column`? + pub fn contains(&self, row: usize, column: usize) -> bool { + let (start, _) = self.range(row); + let (word, mask) = word_mask(column); (self.vector[start + word] & mask) != 0 } - /// Returns those indices that are reachable from both `a` and - /// `b`. This is an O(n) operation where `n` is the number of - /// elements (somewhat independent from the actual size of the + /// Returns those indices that are true in rows `a` and `b`. This + /// is an O(n) operation where `n` is the number of elements + /// (somewhat independent from the actual size of the /// intersection, in particular). pub fn intersection(&self, a: usize, b: usize) -> Vec { let (a_start, a_end) = self.range(a); @@ -206,7 +210,7 @@ impl BitMatrix { result } - /// Add the bits from `read` to the bits from `write`, + /// Add the bits from row `read` to the bits from row `write`, /// return true if anything changed. /// /// This is used when computing transitive reachability because if @@ -227,6 +231,8 @@ impl BitMatrix { changed } + /// Iterates through all the columns set to true in a given row of + /// the matrix. pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> { let (start, end) = self.range(row); BitVectorIter { diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 474622f366913..56d5f5ffa3f6c 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -31,7 +31,7 @@ //! be indexed by the direction (see the type `Direction`). use bitvec::BitVector; -use std::fmt::{Formatter, Error, Debug}; +use std::fmt::Debug; use std::usize; use snapshot_vec::{SnapshotVec, SnapshotVecDelegate}; @@ -48,6 +48,7 @@ pub struct Node { pub data: N, } +#[derive(Debug)] pub struct Edge { next_edge: [EdgeIndex; 2], // see module comment source: NodeIndex, @@ -69,18 +70,6 @@ impl SnapshotVecDelegate for Edge { fn reverse(_: &mut Vec>, _: ()) {} } -impl Debug for Edge { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - write!(f, - "Edge {{ next_edge: [{:?}, {:?}], source: {:?}, target: {:?}, data: {:?} }}", - self.next_edge[0], - self.next_edge[1], - self.source, - self.target, - self.data) - } -} - #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct NodeIndex(pub usize); diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs index c790463e47adb..cb80f602a1c7f 100644 --- a/src/librustc_data_structures/indexed_set.rs +++ b/src/librustc_data_structures/indexed_set.rs @@ -17,6 +17,7 @@ use std::slice; use bitslice::{BitSlice, Word}; use bitslice::{bitwise, Union, Subtract, Intersect}; use indexed_vec::Idx; +use rustc_serialize; /// Represents a set (or packed family of sets), of some element type /// E, where each E is identified by some unique index type `T`. @@ -35,6 +36,26 @@ impl Clone for IdxSetBuf { } } +impl rustc_serialize::Encodable for IdxSetBuf { + fn encode(&self, + encoder: &mut E) + -> Result<(), E::Error> { + self.bits.encode(encoder) + } +} + +impl rustc_serialize::Decodable for IdxSetBuf { + fn decode(d: &mut D) -> Result, D::Error> { + let words: Vec = rustc_serialize::Decodable::decode(d)?; + + Ok(IdxSetBuf { + _pd: PhantomData, + bits: words, + }) + } +} + + // pnkfelix wants to have this be `IdxSet([Word]) and then pass // around `&mut IdxSet` or `&IdxSet`. // @@ -53,11 +74,19 @@ pub struct IdxSet { } impl fmt::Debug for IdxSetBuf { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + w.debug_list() + .entries(self.iter()) + .finish() + } } impl fmt::Debug for IdxSet { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + w.debug_list() + .entries(self.iter()) + .finish() + } } impl IdxSetBuf { diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 1d0e88ee32855..e2f50c8c8891b 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -38,6 +38,292 @@ impl Idx for u32 { fn index(self) -> usize { self as usize } } +#[macro_export] +macro_rules! newtype_index { + // ---- public rules ---- + + // Use default constants + ($name:ident) => ( + newtype_index!( + // Leave out derives marker so we can use its absence to ensure it comes first + @type [$name] + @max [::std::u32::MAX] + @debug_format ["{}"]); + ); + + // Define any constants + ($name:ident { $($tokens:tt)+ }) => ( + newtype_index!( + // Leave out derives marker so we can use its absence to ensure it comes first + @type [$name] + @max [::std::u32::MAX] + @debug_format ["{}"] + $($tokens)+); + ); + + // ---- private rules ---- + + // Base case, user-defined constants (if any) have already been defined + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt]) => ( + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)] + pub struct $type($($pub)* u32); + + impl Idx for $type { + fn new(value: usize) -> Self { + assert!(value < ($max) as usize); + $type(value as u32) + } + + fn index(self) -> usize { + self.0 as usize + } + } + + newtype_index!( + @handle_debug + @derives [$($derives,)*] + @type [$type] + @debug_format [$debug_format]); + ); + + // base case for handle_debug where format is custom. No Debug implementation is emitted. + (@handle_debug + @derives [$($_derives:ident,)*] + @type [$type:ident] + @debug_format [custom]) => (); + + // base case for handle_debug, no debug overrides found, so use default + (@handle_debug + @derives [] + @type [$type:ident] + @debug_format [$debug_format:tt]) => ( + impl ::std::fmt::Debug for $type { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(fmt, $debug_format, self.0) + } + } + ); + + // Debug is requested for derive, don't generate any Debug implementation. + (@handle_debug + @derives [Debug, $($derives:ident,)*] + @type [$type:ident] + @debug_format [$debug_format:tt]) => (); + + // It's not Debug, so just pop it off the front of the derives stack and check the rest. + (@handle_debug + @derives [$_derive:ident, $($derives:ident,)*] + @type [$type:ident] + @debug_format [$debug_format:tt]) => ( + newtype_index!( + @handle_debug + @derives [$($derives,)*] + @type [$type] + @debug_format [$debug_format]); + ); + + // Handle the case where someone wants to make the internal field public + (@type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + pub idx + $($tokens:tt)*) => ( + newtype_index!( + @pub [pub] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // The default case is that the internal field is private + (@type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + $($tokens:tt)*) => ( + newtype_index!( + @pub [] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // Append comma to end of derives list if it's missing + (@pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + derive [$($derives:ident),*] + $($tokens:tt)*) => ( + newtype_index!( + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + derive [$($derives,)*] + $($tokens)*); + ); + + // By not including the @derives marker in this list nor in the default args, we can force it + // to come first if it exists. When encodable is custom, just use the derives list as-is. + (@pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + derive [$($derives:ident,)+] + ENCODABLE = custom + $($tokens:tt)*) => ( + newtype_index!( + @derives [$($derives,)+] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // By not including the @derives marker in this list nor in the default args, we can force it + // to come first if it exists. When encodable isn't custom, add serialization traits by default. + (@pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + derive [$($derives:ident,)+] + $($tokens:tt)*) => ( + newtype_index!( + @derives [$($derives,)+ RustcDecodable, RustcEncodable,] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // The case where no derives are added, but encodable is overriden. Don't + // derive serialization traits + (@pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + ENCODABLE = custom + $($tokens:tt)*) => ( + newtype_index!( + @derives [] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // The case where no derives are added, add serialization derives by default + (@pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + $($tokens:tt)*) => ( + newtype_index!( + @derives [RustcDecodable, RustcEncodable,] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // Rewrite final without comma to one that includes comma + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + $name:ident = $constant:expr) => ( + newtype_index!( + @derives [$($derives,)*] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $name = $constant,); + ); + + // Rewrite final const without comma to one that includes comma + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$_max:expr] + @debug_format [$debug_format:tt] + $(#[doc = $doc:expr])* + const $name:ident = $constant:expr) => ( + newtype_index!( + @derives [$($derives,)*] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $(#[doc = $doc])* const $name = $constant,); + ); + + // Replace existing default for max + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$_max:expr] + @debug_format [$debug_format:tt] + MAX = $max:expr, + $($tokens:tt)*) => ( + newtype_index!( + @derives [$($derives,)*] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // Replace existing default for debug_format + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$_debug_format:tt] + DEBUG_FORMAT = $debug_format:tt, + $($tokens:tt)*) => ( + newtype_index!( + @derives [$($derives,)*] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); + + // Assign a user-defined constant + (@derives [$($derives:ident,)*] + @pub [$($pub:tt)*] + @type [$type:ident] + @max [$max:expr] + @debug_format [$debug_format:tt] + $(#[doc = $doc:expr])* + const $name:ident = $constant:expr, + $($tokens:tt)*) => ( + $(#[doc = $doc])* + pub const $name: $type = $type($constant); + newtype_index!( + @derives [$($derives,)*] + @pub [$($pub)*] + @type [$type] + @max [$max] + @debug_format [$debug_format] + $($tokens)*); + ); +} + #[derive(Clone, PartialEq, Eq)] pub struct IndexVec { pub raw: Vec, @@ -98,6 +384,11 @@ impl IndexVec { idx } + #[inline] + pub fn pop(&mut self) -> Option { + self.raw.pop() + } + #[inline] pub fn len(&self) -> usize { self.raw.len() @@ -125,7 +416,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated(&self) -> Enumerated> + pub fn iter_enumerated(&self) -> Enumerated> { self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData }) } @@ -141,7 +432,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated_mut(&mut self) -> Enumerated> + pub fn iter_enumerated_mut(&mut self) -> Enumerated> { self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData }) } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 47061883425e2..8862ba3545eba 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -28,8 +28,10 @@ #![feature(fn_traits)] #![feature(unsize)] #![feature(i128_type)] +#![feature(i128)] #![feature(conservative_impl_trait)] #![feature(specialization)] +#![feature(underscore_lifetimes)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] @@ -54,6 +56,7 @@ pub mod graph; pub mod indexed_set; pub mod indexed_vec; pub mod obligation_forest; +pub mod sip128; pub mod snapshot_map; pub mod snapshot_vec; pub mod stable_hasher; diff --git a/src/librustc_data_structures/sip128.rs b/src/librustc_data_structures/sip128.rs new file mode 100644 index 0000000000000..1f0b0d9cbfb0b --- /dev/null +++ b/src/librustc_data_structures/sip128.rs @@ -0,0 +1,533 @@ +// 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. + +//! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes. + +use std::cmp; +use std::hash::Hasher; +use std::slice; +use std::ptr; +use std::mem; + +#[derive(Debug, Clone)] +pub struct SipHasher128 { + k0: u64, + k1: u64, + length: usize, // how many bytes we've processed + state: State, // hash State + tail: u64, // unprocessed bytes le + ntail: usize, // how many bytes in tail are valid +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct State { + // v0, v2 and v1, v3 show up in pairs in the algorithm, + // and simd implementations of SipHash will use vectors + // of v02 and v13. By placing them in this order in the struct, + // the compiler can pick up on just a few simd optimizations by itself. + v0: u64, + v2: u64, + v1: u64, + v3: u64, +} + +macro_rules! compress { + ($state:expr) => ({ + compress!($state.v0, $state.v1, $state.v2, $state.v3) + }); + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => + ({ + $v0 = $v0.wrapping_add($v1); $v1 = $v1.rotate_left(13); $v1 ^= $v0; + $v0 = $v0.rotate_left(32); + $v2 = $v2.wrapping_add($v3); $v3 = $v3.rotate_left(16); $v3 ^= $v2; + $v0 = $v0.wrapping_add($v3); $v3 = $v3.rotate_left(21); $v3 ^= $v0; + $v2 = $v2.wrapping_add($v1); $v1 = $v1.rotate_left(17); $v1 ^= $v2; + $v2 = $v2.rotate_left(32); + }); +} + +/// Load an integer of the desired type from a byte stream, in LE order. Uses +/// `copy_nonoverlapping` to let the compiler generate the most efficient way +/// to load it from a possibly unaligned address. +/// +/// Unsafe because: unchecked indexing at i..i+size_of(int_ty) +macro_rules! load_int_le { + ($buf:expr, $i:expr, $int_ty:ident) => + ({ + debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); + let mut data = 0 as $int_ty; + ptr::copy_nonoverlapping($buf.get_unchecked($i), + &mut data as *mut _ as *mut u8, + mem::size_of::<$int_ty>()); + data.to_le() + }); +} + +/// Load an u64 using up to 7 bytes of a byte slice. +/// +/// Unsafe because: unchecked indexing at start..start+len +#[inline] +unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { + debug_assert!(len < 8); + let mut i = 0; // current byte index (from LSB) in the output u64 + let mut out = 0; + if i + 3 < len { + out = load_int_le!(buf, start + i, u32) as u64; + i += 4; + } + if i + 1 < len { + out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8); + i += 2 + } + if i < len { + out |= (*buf.get_unchecked(start + i) as u64) << (i * 8); + i += 1; + } + debug_assert_eq!(i, len); + out +} + + +impl SipHasher128 { + #[inline] + pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 { + let mut state = SipHasher128 { + k0: key0, + k1: key1, + length: 0, + state: State { + v0: 0, + v1: 0, + v2: 0, + v3: 0, + }, + tail: 0, + ntail: 0, + }; + state.reset(); + state + } + + #[inline] + fn reset(&mut self) { + self.length = 0; + self.state.v0 = self.k0 ^ 0x736f6d6570736575; + self.state.v1 = self.k1 ^ 0x646f72616e646f6d; + self.state.v2 = self.k0 ^ 0x6c7967656e657261; + self.state.v3 = self.k1 ^ 0x7465646279746573; + self.ntail = 0; + + // This is only done in the 128 bit version: + self.state.v1 ^= 0xee; + } + + // Specialized write function that is only valid for buffers with len <= 8. + // It's used to force inlining of write_u8 and write_usize, those would normally be inlined + // except for composite types (that includes slices and str hashing because of delimiter). + // Without this extra push the compiler is very reluctant to inline delimiter writes, + // degrading performance substantially for the most common use cases. + #[inline] + fn short_write(&mut self, msg: &[u8]) { + debug_assert!(msg.len() <= 8); + let length = msg.len(); + self.length += length; + + let needed = 8 - self.ntail; + let fill = cmp::min(length, needed); + if fill == 8 { + self.tail = unsafe { load_int_le!(msg, 0, u64) }; + } else { + self.tail |= unsafe { u8to64_le(msg, 0, fill) } << (8 * self.ntail); + if length < needed { + self.ntail += length; + return; + } + } + self.state.v3 ^= self.tail; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= self.tail; + + // Buffered tail is now flushed, process new input. + self.ntail = length - needed; + self.tail = unsafe { u8to64_le(msg, needed, self.ntail) }; + } + + #[inline(always)] + fn short_write_gen(&mut self, x: T) { + let bytes = unsafe { + slice::from_raw_parts(&x as *const T as *const u8, mem::size_of::()) + }; + self.short_write(bytes); + } + + #[inline] + pub fn finish128(mut self) -> (u64, u64) { + let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; + + self.state.v3 ^= b; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= b; + + self.state.v2 ^= 0xee; + Sip24Rounds::d_rounds(&mut self.state); + let _0 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3; + + self.state.v1 ^= 0xdd; + Sip24Rounds::d_rounds(&mut self.state); + let _1 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3; + (_0, _1) + } +} + +impl Hasher for SipHasher128 { + #[inline] + fn write_u8(&mut self, i: u8) { + self.short_write_gen(i); + } + + #[inline] + fn write_u16(&mut self, i: u16) { + self.short_write_gen(i); + } + + #[inline] + fn write_u32(&mut self, i: u32) { + self.short_write_gen(i); + } + + #[inline] + fn write_u64(&mut self, i: u64) { + self.short_write_gen(i); + } + + #[inline] + fn write_usize(&mut self, i: usize) { + self.short_write_gen(i); + } + + #[inline] + fn write_i8(&mut self, i: i8) { + self.short_write_gen(i); + } + + #[inline] + fn write_i16(&mut self, i: i16) { + self.short_write_gen(i); + } + + #[inline] + fn write_i32(&mut self, i: i32) { + self.short_write_gen(i); + } + + #[inline] + fn write_i64(&mut self, i: i64) { + self.short_write_gen(i); + } + + #[inline] + fn write_isize(&mut self, i: isize) { + self.short_write_gen(i); + } + + #[inline] + fn write(&mut self, msg: &[u8]) { + let length = msg.len(); + self.length += length; + + let mut needed = 0; + + if self.ntail != 0 { + needed = 8 - self.ntail; + self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << 8 * self.ntail; + if length < needed { + self.ntail += length; + return + } else { + self.state.v3 ^= self.tail; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= self.tail; + self.ntail = 0; + } + } + + // Buffered tail is now flushed, process new input. + let len = length - needed; + let left = len & 0x7; + + let mut i = needed; + while i < len - left { + let mi = unsafe { load_int_le!(msg, i, u64) }; + + self.state.v3 ^= mi; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= mi; + + i += 8; + } + + self.tail = unsafe { u8to64_le(msg, i, left) }; + self.ntail = left; + } + + fn finish(&self) -> u64 { + panic!("SipHasher128 cannot provide valid 64 bit hashes") + } +} + +#[derive(Debug, Clone, Default)] +struct Sip24Rounds; + +impl Sip24Rounds { + #[inline] + fn c_rounds(state: &mut State) { + compress!(state); + compress!(state); + } + + #[inline] + fn d_rounds(state: &mut State) { + compress!(state); + compress!(state); + compress!(state); + compress!(state); + } +} + +#[cfg(test)] +mod test { + use std::hash::{Hash, Hasher}; + use std::{slice, mem}; + use super::SipHasher128; + + // Hash just the bytes of the slice, without length prefix + struct Bytes<'a>(&'a [u8]); + + impl<'a> Hash for Bytes<'a> { + #[allow(unused_must_use)] + fn hash(&self, state: &mut H) { + for byte in self.0 { + state.write_u8(*byte); + } + } + } + + fn hash_with(mut st: SipHasher128, x: &T) -> (u64, u64) { + x.hash(&mut st); + st.finish128() + } + + fn hash(x: &T) -> (u64, u64) { + hash_with(SipHasher128::new_with_keys(0, 0), x) + } + + const TEST_VECTOR : [[u8; 16]; 64] = [ + [0xa3,0x81,0x7f,0x04,0xba,0x25,0xa8,0xe6,0x6d,0xf6,0x72,0x14,0xc7,0x55,0x02,0x93], + [0xda,0x87,0xc1,0xd8,0x6b,0x99,0xaf,0x44,0x34,0x76,0x59,0x11,0x9b,0x22,0xfc,0x45], + [0x81,0x77,0x22,0x8d,0xa4,0xa4,0x5d,0xc7,0xfc,0xa3,0x8b,0xde,0xf6,0x0a,0xff,0xe4], + [0x9c,0x70,0xb6,0x0c,0x52,0x67,0xa9,0x4e,0x5f,0x33,0xb6,0xb0,0x29,0x85,0xed,0x51], + [0xf8,0x81,0x64,0xc1,0x2d,0x9c,0x8f,0xaf,0x7d,0x0f,0x6e,0x7c,0x7b,0xcd,0x55,0x79], + [0x13,0x68,0x87,0x59,0x80,0x77,0x6f,0x88,0x54,0x52,0x7a,0x07,0x69,0x0e,0x96,0x27], + [0x14,0xee,0xca,0x33,0x8b,0x20,0x86,0x13,0x48,0x5e,0xa0,0x30,0x8f,0xd7,0xa1,0x5e], + [0xa1,0xf1,0xeb,0xbe,0xd8,0xdb,0xc1,0x53,0xc0,0xb8,0x4a,0xa6,0x1f,0xf0,0x82,0x39], + [0x3b,0x62,0xa9,0xba,0x62,0x58,0xf5,0x61,0x0f,0x83,0xe2,0x64,0xf3,0x14,0x97,0xb4], + [0x26,0x44,0x99,0x06,0x0a,0xd9,0xba,0xab,0xc4,0x7f,0x8b,0x02,0xbb,0x6d,0x71,0xed], + [0x00,0x11,0x0d,0xc3,0x78,0x14,0x69,0x56,0xc9,0x54,0x47,0xd3,0xf3,0xd0,0xfb,0xba], + [0x01,0x51,0xc5,0x68,0x38,0x6b,0x66,0x77,0xa2,0xb4,0xdc,0x6f,0x81,0xe5,0xdc,0x18], + [0xd6,0x26,0xb2,0x66,0x90,0x5e,0xf3,0x58,0x82,0x63,0x4d,0xf6,0x85,0x32,0xc1,0x25], + [0x98,0x69,0xe2,0x47,0xe9,0xc0,0x8b,0x10,0xd0,0x29,0x93,0x4f,0xc4,0xb9,0x52,0xf7], + [0x31,0xfc,0xef,0xac,0x66,0xd7,0xde,0x9c,0x7e,0xc7,0x48,0x5f,0xe4,0x49,0x49,0x02], + [0x54,0x93,0xe9,0x99,0x33,0xb0,0xa8,0x11,0x7e,0x08,0xec,0x0f,0x97,0xcf,0xc3,0xd9], + [0x6e,0xe2,0xa4,0xca,0x67,0xb0,0x54,0xbb,0xfd,0x33,0x15,0xbf,0x85,0x23,0x05,0x77], + [0x47,0x3d,0x06,0xe8,0x73,0x8d,0xb8,0x98,0x54,0xc0,0x66,0xc4,0x7a,0xe4,0x77,0x40], + [0xa4,0x26,0xe5,0xe4,0x23,0xbf,0x48,0x85,0x29,0x4d,0xa4,0x81,0xfe,0xae,0xf7,0x23], + [0x78,0x01,0x77,0x31,0xcf,0x65,0xfa,0xb0,0x74,0xd5,0x20,0x89,0x52,0x51,0x2e,0xb1], + [0x9e,0x25,0xfc,0x83,0x3f,0x22,0x90,0x73,0x3e,0x93,0x44,0xa5,0xe8,0x38,0x39,0xeb], + [0x56,0x8e,0x49,0x5a,0xbe,0x52,0x5a,0x21,0x8a,0x22,0x14,0xcd,0x3e,0x07,0x1d,0x12], + [0x4a,0x29,0xb5,0x45,0x52,0xd1,0x6b,0x9a,0x46,0x9c,0x10,0x52,0x8e,0xff,0x0a,0xae], + [0xc9,0xd1,0x84,0xdd,0xd5,0xa9,0xf5,0xe0,0xcf,0x8c,0xe2,0x9a,0x9a,0xbf,0x69,0x1c], + [0x2d,0xb4,0x79,0xae,0x78,0xbd,0x50,0xd8,0x88,0x2a,0x8a,0x17,0x8a,0x61,0x32,0xad], + [0x8e,0xce,0x5f,0x04,0x2d,0x5e,0x44,0x7b,0x50,0x51,0xb9,0xea,0xcb,0x8d,0x8f,0x6f], + [0x9c,0x0b,0x53,0xb4,0xb3,0xc3,0x07,0xe8,0x7e,0xae,0xe0,0x86,0x78,0x14,0x1f,0x66], + [0xab,0xf2,0x48,0xaf,0x69,0xa6,0xea,0xe4,0xbf,0xd3,0xeb,0x2f,0x12,0x9e,0xeb,0x94], + [0x06,0x64,0xda,0x16,0x68,0x57,0x4b,0x88,0xb9,0x35,0xf3,0x02,0x73,0x58,0xae,0xf4], + [0xaa,0x4b,0x9d,0xc4,0xbf,0x33,0x7d,0xe9,0x0c,0xd4,0xfd,0x3c,0x46,0x7c,0x6a,0xb7], + [0xea,0x5c,0x7f,0x47,0x1f,0xaf,0x6b,0xde,0x2b,0x1a,0xd7,0xd4,0x68,0x6d,0x22,0x87], + [0x29,0x39,0xb0,0x18,0x32,0x23,0xfa,0xfc,0x17,0x23,0xde,0x4f,0x52,0xc4,0x3d,0x35], + [0x7c,0x39,0x56,0xca,0x5e,0xea,0xfc,0x3e,0x36,0x3e,0x9d,0x55,0x65,0x46,0xeb,0x68], + [0x77,0xc6,0x07,0x71,0x46,0xf0,0x1c,0x32,0xb6,0xb6,0x9d,0x5f,0x4e,0xa9,0xff,0xcf], + [0x37,0xa6,0x98,0x6c,0xb8,0x84,0x7e,0xdf,0x09,0x25,0xf0,0xf1,0x30,0x9b,0x54,0xde], + [0xa7,0x05,0xf0,0xe6,0x9d,0xa9,0xa8,0xf9,0x07,0x24,0x1a,0x2e,0x92,0x3c,0x8c,0xc8], + [0x3d,0xc4,0x7d,0x1f,0x29,0xc4,0x48,0x46,0x1e,0x9e,0x76,0xed,0x90,0x4f,0x67,0x11], + [0x0d,0x62,0xbf,0x01,0xe6,0xfc,0x0e,0x1a,0x0d,0x3c,0x47,0x51,0xc5,0xd3,0x69,0x2b], + [0x8c,0x03,0x46,0x8b,0xca,0x7c,0x66,0x9e,0xe4,0xfd,0x5e,0x08,0x4b,0xbe,0xe7,0xb5], + [0x52,0x8a,0x5b,0xb9,0x3b,0xaf,0x2c,0x9c,0x44,0x73,0xcc,0xe5,0xd0,0xd2,0x2b,0xd9], + [0xdf,0x6a,0x30,0x1e,0x95,0xc9,0x5d,0xad,0x97,0xae,0x0c,0xc8,0xc6,0x91,0x3b,0xd8], + [0x80,0x11,0x89,0x90,0x2c,0x85,0x7f,0x39,0xe7,0x35,0x91,0x28,0x5e,0x70,0xb6,0xdb], + [0xe6,0x17,0x34,0x6a,0xc9,0xc2,0x31,0xbb,0x36,0x50,0xae,0x34,0xcc,0xca,0x0c,0x5b], + [0x27,0xd9,0x34,0x37,0xef,0xb7,0x21,0xaa,0x40,0x18,0x21,0xdc,0xec,0x5a,0xdf,0x89], + [0x89,0x23,0x7d,0x9d,0xed,0x9c,0x5e,0x78,0xd8,0xb1,0xc9,0xb1,0x66,0xcc,0x73,0x42], + [0x4a,0x6d,0x80,0x91,0xbf,0x5e,0x7d,0x65,0x11,0x89,0xfa,0x94,0xa2,0x50,0xb1,0x4c], + [0x0e,0x33,0xf9,0x60,0x55,0xe7,0xae,0x89,0x3f,0xfc,0x0e,0x3d,0xcf,0x49,0x29,0x02], + [0xe6,0x1c,0x43,0x2b,0x72,0x0b,0x19,0xd1,0x8e,0xc8,0xd8,0x4b,0xdc,0x63,0x15,0x1b], + [0xf7,0xe5,0xae,0xf5,0x49,0xf7,0x82,0xcf,0x37,0x90,0x55,0xa6,0x08,0x26,0x9b,0x16], + [0x43,0x8d,0x03,0x0f,0xd0,0xb7,0xa5,0x4f,0xa8,0x37,0xf2,0xad,0x20,0x1a,0x64,0x03], + [0xa5,0x90,0xd3,0xee,0x4f,0xbf,0x04,0xe3,0x24,0x7e,0x0d,0x27,0xf2,0x86,0x42,0x3f], + [0x5f,0xe2,0xc1,0xa1,0x72,0xfe,0x93,0xc4,0xb1,0x5c,0xd3,0x7c,0xae,0xf9,0xf5,0x38], + [0x2c,0x97,0x32,0x5c,0xbd,0x06,0xb3,0x6e,0xb2,0x13,0x3d,0xd0,0x8b,0x3a,0x01,0x7c], + [0x92,0xc8,0x14,0x22,0x7a,0x6b,0xca,0x94,0x9f,0xf0,0x65,0x9f,0x00,0x2a,0xd3,0x9e], + [0xdc,0xe8,0x50,0x11,0x0b,0xd8,0x32,0x8c,0xfb,0xd5,0x08,0x41,0xd6,0x91,0x1d,0x87], + [0x67,0xf1,0x49,0x84,0xc7,0xda,0x79,0x12,0x48,0xe3,0x2b,0xb5,0x92,0x25,0x83,0xda], + [0x19,0x38,0xf2,0xcf,0x72,0xd5,0x4e,0xe9,0x7e,0x94,0x16,0x6f,0xa9,0x1d,0x2a,0x36], + [0x74,0x48,0x1e,0x96,0x46,0xed,0x49,0xfe,0x0f,0x62,0x24,0x30,0x16,0x04,0x69,0x8e], + [0x57,0xfc,0xa5,0xde,0x98,0xa9,0xd6,0xd8,0x00,0x64,0x38,0xd0,0x58,0x3d,0x8a,0x1d], + [0x9f,0xec,0xde,0x1c,0xef,0xdc,0x1c,0xbe,0xd4,0x76,0x36,0x74,0xd9,0x57,0x53,0x59], + [0xe3,0x04,0x0c,0x00,0xeb,0x28,0xf1,0x53,0x66,0xca,0x73,0xcb,0xd8,0x72,0xe7,0x40], + [0x76,0x97,0x00,0x9a,0x6a,0x83,0x1d,0xfe,0xcc,0xa9,0x1c,0x59,0x93,0x67,0x0f,0x7a], + [0x58,0x53,0x54,0x23,0x21,0xf5,0x67,0xa0,0x05,0xd5,0x47,0xa4,0xf0,0x47,0x59,0xbd], + [0x51,0x50,0xd1,0x77,0x2f,0x50,0x83,0x4a,0x50,0x3e,0x06,0x9a,0x97,0x3f,0xbd,0x7c], + ]; + + // Test vector from reference implementation + #[test] + fn test_siphash_2_4_test_vector() { + let k0 = 0x_07_06_05_04_03_02_01_00; + let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08; + + let mut input: Vec = Vec::new(); + + for i in 0 .. 64 { + let out = hash_with(SipHasher128::new_with_keys(k0, k1), + &Bytes(&input[..])); + let expected = ( + ((TEST_VECTOR[i][0] as u64) << 0) | + ((TEST_VECTOR[i][1] as u64) << 8) | + ((TEST_VECTOR[i][2] as u64) << 16) | + ((TEST_VECTOR[i][3] as u64) << 24) | + ((TEST_VECTOR[i][4] as u64) << 32) | + ((TEST_VECTOR[i][5] as u64) << 40) | + ((TEST_VECTOR[i][6] as u64) << 48) | + ((TEST_VECTOR[i][7] as u64) << 56), + + ((TEST_VECTOR[i][8] as u64) << 0) | + ((TEST_VECTOR[i][9] as u64) << 8) | + ((TEST_VECTOR[i][10] as u64) << 16) | + ((TEST_VECTOR[i][11] as u64) << 24) | + ((TEST_VECTOR[i][12] as u64) << 32) | + ((TEST_VECTOR[i][13] as u64) << 40) | + ((TEST_VECTOR[i][14] as u64) << 48) | + ((TEST_VECTOR[i][15] as u64) << 56), + ); + + assert_eq!(out, expected); + input.push(i as u8); + } + } + + #[test] #[cfg(target_arch = "arm")] + fn test_hash_usize() { + let val = 0xdeadbeef_deadbeef_u64; + assert!(hash(&(val as u64)) != hash(&(val as usize))); + assert_eq!(hash(&(val as u32)), hash(&(val as usize))); + } + #[test] #[cfg(target_arch = "x86_64")] + fn test_hash_usize() { + let val = 0xdeadbeef_deadbeef_u64; + assert_eq!(hash(&(val as u64)), hash(&(val as usize))); + assert!(hash(&(val as u32)) != hash(&(val as usize))); + } + #[test] #[cfg(target_arch = "x86")] + fn test_hash_usize() { + let val = 0xdeadbeef_deadbeef_u64; + assert!(hash(&(val as u64)) != hash(&(val as usize))); + assert_eq!(hash(&(val as u32)), hash(&(val as usize))); + } + + #[test] + fn test_hash_idempotent() { + let val64 = 0xdeadbeef_deadbeef_u64; + assert_eq!(hash(&val64), hash(&val64)); + let val32 = 0xdeadbeef_u32; + assert_eq!(hash(&val32), hash(&val32)); + } + + #[test] + fn test_hash_no_bytes_dropped_64() { + let val = 0xdeadbeef_deadbeef_u64; + + assert!(hash(&val) != hash(&zero_byte(val, 0))); + assert!(hash(&val) != hash(&zero_byte(val, 1))); + assert!(hash(&val) != hash(&zero_byte(val, 2))); + assert!(hash(&val) != hash(&zero_byte(val, 3))); + assert!(hash(&val) != hash(&zero_byte(val, 4))); + assert!(hash(&val) != hash(&zero_byte(val, 5))); + assert!(hash(&val) != hash(&zero_byte(val, 6))); + assert!(hash(&val) != hash(&zero_byte(val, 7))); + + fn zero_byte(val: u64, byte: usize) -> u64 { + assert!(byte < 8); + val & !(0xff << (byte * 8)) + } + } + + #[test] + fn test_hash_no_bytes_dropped_32() { + let val = 0xdeadbeef_u32; + + assert!(hash(&val) != hash(&zero_byte(val, 0))); + assert!(hash(&val) != hash(&zero_byte(val, 1))); + assert!(hash(&val) != hash(&zero_byte(val, 2))); + assert!(hash(&val) != hash(&zero_byte(val, 3))); + + fn zero_byte(val: u32, byte: usize) -> u32 { + assert!(byte < 4); + val & !(0xff << (byte * 8)) + } + } + + #[test] + fn test_hash_no_concat_alias() { + let s = ("aa", "bb"); + let t = ("aabb", ""); + let u = ("a", "abb"); + + assert!(s != t && t != u); + assert!(hash(&s) != hash(&t) && hash(&s) != hash(&u)); + + let u = [1, 0, 0, 0]; + let v = (&u[..1], &u[1..3], &u[3..]); + let w = (&u[..], &u[4..4], &u[4..4]); + + assert!(v != w); + assert!(hash(&v) != hash(&w)); + } + + #[test] + fn test_write_short_works() { + let test_usize = 0xd0c0b0a0usize; + let mut h1 = SipHasher128::new_with_keys(0, 0); + h1.write_usize(test_usize); + h1.write(b"bytes"); + h1.write(b"string"); + h1.write_u8(0xFFu8); + h1.write_u8(0x01u8); + let mut h2 = SipHasher128::new_with_keys(0, 0); + h2.write(unsafe { + slice::from_raw_parts(&test_usize as *const _ as *const u8, + mem::size_of::()) + }); + h2.write(b"bytes"); + h2.write(b"string"); + h2.write(&[0xFFu8, 0x01u8]); + assert_eq!(h1.finish128(), h2.finish128()); + } + +} diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 9aba48c5bef07..d82b712b5b14b 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -11,37 +11,17 @@ use std::hash::{Hash, Hasher, BuildHasher}; use std::marker::PhantomData; use std::mem; -use blake2b::Blake2bHasher; -use rustc_serialize::leb128; +use sip128::SipHasher128; -fn write_unsigned_leb128_to_buf(buf: &mut [u8; 16], value: u64) -> usize { - leb128::write_unsigned_leb128_to(value as u128, |i, v| buf[i] = v) -} - -fn write_signed_leb128_to_buf(buf: &mut [u8; 16], value: i64) -> usize { - leb128::write_signed_leb128_to(value as i128, |i, v| buf[i] = v) -} - -/// When hashing something that ends up affecting properties like symbol names. We -/// want these symbol names to be calculated independent of other factors like -/// what architecture you're compiling *from*. -/// -/// The hashing just uses the standard `Hash` trait, but the implementations of -/// `Hash` for the `usize` and `isize` types are *not* architecture independent -/// (e.g. they has 4 or 8 bytes). As a result we want to avoid `usize` and -/// `isize` completely when hashing. -/// -/// To do that, we encode all integers to be hashed with some -/// arch-independent encoding. +/// When hashing something that ends up affecting properties like symbol names, +/// we want these symbol names to be calculated independently of other factors +/// like what architecture you're compiling *from*. /// -/// At the moment, we pass i8/u8 straight through and encode -/// all other integers using leb128. -/// -/// This hasher currently always uses the stable Blake2b algorithm -/// and allows for variable output lengths through its type -/// parameter. +/// To that end we always convert integers to little-endian format before +/// hashing and the architecture dependent `isize` and `usize` types are +/// extended to 64 bits if needed. pub struct StableHasher { - state: Blake2bHasher, + state: SipHasher128, bytes_hashed: u64, width: PhantomData, } @@ -59,7 +39,7 @@ pub trait StableHasherResult: Sized { impl StableHasher { pub fn new() -> Self { StableHasher { - state: Blake2bHasher::new(mem::size_of::(), &[]), + state: SipHasher128::new_with_keys(0, 0), bytes_hashed: 0, width: PhantomData, } @@ -70,66 +50,34 @@ impl StableHasher { } } -impl StableHasherResult for [u8; 20] { - fn finish(mut hasher: StableHasher) -> Self { - let mut result: [u8; 20] = [0; 20]; - result.copy_from_slice(hasher.state.finalize()); - result - } -} - impl StableHasherResult for u128 { - fn finish(mut hasher: StableHasher) -> Self { - let hash_bytes: &[u8] = hasher.finalize(); - assert!(hash_bytes.len() >= mem::size_of::()); - - unsafe { - ::std::ptr::read_unaligned(hash_bytes.as_ptr() as *const u128) - } + fn finish(hasher: StableHasher) -> Self { + let (_0, _1) = hasher.finalize(); + (_0 as u128) | ((_1 as u128) << 64) } } impl StableHasherResult for u64 { - fn finish(mut hasher: StableHasher) -> Self { - hasher.state.finalize(); - hasher.state.finish() + fn finish(hasher: StableHasher) -> Self { + hasher.finalize().0 } } impl StableHasher { #[inline] - pub fn finalize(&mut self) -> &[u8] { - self.state.finalize() + pub fn finalize(self) -> (u64, u64) { + self.state.finish128() } #[inline] pub fn bytes_hashed(&self) -> u64 { self.bytes_hashed } - - #[inline] - fn write_uleb128(&mut self, value: u64) { - let mut buf = [0; 16]; - let len = write_unsigned_leb128_to_buf(&mut buf, value); - self.state.write(&buf[..len]); - self.bytes_hashed += len as u64; - } - - #[inline] - fn write_ileb128(&mut self, value: i64) { - let mut buf = [0; 16]; - let len = write_signed_leb128_to_buf(&mut buf, value); - self.state.write(&buf[..len]); - self.bytes_hashed += len as u64; - } } -// For the non-u8 integer cases we leb128 encode them first. Because small -// integers dominate, this significantly and cheaply reduces the number of -// bytes hashed, which is good because blake2b is expensive. impl Hasher for StableHasher { fn finish(&self) -> u64 { - panic!("use StableHasher::finish instead"); + panic!("use StableHasher::finalize instead"); } #[inline] @@ -146,22 +94,35 @@ impl Hasher for StableHasher { #[inline] fn write_u16(&mut self, i: u16) { - self.write_uleb128(i as u64); + self.state.write_u16(i.to_le()); + self.bytes_hashed += 2; } #[inline] fn write_u32(&mut self, i: u32) { - self.write_uleb128(i as u64); + self.state.write_u32(i.to_le()); + self.bytes_hashed += 4; } #[inline] fn write_u64(&mut self, i: u64) { - self.write_uleb128(i); + self.state.write_u64(i.to_le()); + self.bytes_hashed += 8; + } + + #[inline] + fn write_u128(&mut self, i: u128) { + self.state.write_u128(i.to_le()); + self.bytes_hashed += 16; } #[inline] fn write_usize(&mut self, i: usize) { - self.write_uleb128(i as u64); + // Always treat usize as u64 so we get the same results on 32 and 64 bit + // platforms. This is important for symbol hashes when cross compiling, + // for example. + self.state.write_u64((i as u64).to_le()); + self.bytes_hashed += 8; } #[inline] @@ -172,22 +133,35 @@ impl Hasher for StableHasher { #[inline] fn write_i16(&mut self, i: i16) { - self.write_ileb128(i as i64); + self.state.write_i16(i.to_le()); + self.bytes_hashed += 2; } #[inline] fn write_i32(&mut self, i: i32) { - self.write_ileb128(i as i64); + self.state.write_i32(i.to_le()); + self.bytes_hashed += 4; } #[inline] fn write_i64(&mut self, i: i64) { - self.write_ileb128(i); + self.state.write_i64(i.to_le()); + self.bytes_hashed += 8; + } + + #[inline] + fn write_i128(&mut self, i: i128) { + self.state.write_i128(i.to_le()); + self.bytes_hashed += 16; } #[inline] fn write_isize(&mut self, i: isize) { - self.write_ileb128(i as i64); + // Always treat isize as i64 so we get the same results on 32 and 64 bit + // platforms. This is important for symbol hashes when cross compiling, + // for example. + self.state.write_i64((i as i64).to_le()); + self.bytes_hashed += 8; } } diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 7cb386b019798..ba7ab0c07c66a 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -134,12 +134,13 @@ impl TransitiveRelation { } } - /// Returns a vector of all things less than `a`. + /// Thinking of `x R y` as an edge `x -> y` in a graph, this + /// returns all things reachable from `a`. /// /// Really this probably ought to be `impl Iterator`, but /// I'm too lazy to make that work, and -- given the caching /// strategy -- it'd be a touch tricky anyhow. - pub fn less_than(&self, a: &T) -> Vec<&T> { + pub fn reachable_from(&self, a: &T) -> Vec<&T> { match self.index(a) { Some(a) => self.with_closure(|closure| { closure.iter(a.0).map(|i| &self.elements[i]).collect() @@ -184,7 +185,14 @@ impl TransitiveRelation { /// b -> b1 /// ``` pub fn postdom_upper_bound(&self, a: &T, b: &T) -> Option<&T> { - let mut mubs = self.minimal_upper_bounds(a, b); + let mubs = self.minimal_upper_bounds(a, b); + self.mutual_immediate_postdominator(mubs) + } + + /// Viewing the relation as a graph, computes the "mutual + /// immediate postdominator" of a set of points (if one + /// exists). See `postdom_upper_bound` for details. + pub fn mutual_immediate_postdominator<'a>(&'a self, mut mubs: Vec<&'a T>) -> Option<&'a T> { loop { match mubs.len() { 0 => return None, @@ -276,6 +284,8 @@ impl TransitiveRelation { // After step 3, we know that no element can reach any of // its predecesssors (because of step 2) nor successors // (because we just called `pare_down`) + // + // This same algorithm is used in `parents` below. let mut candidates = closure.intersection(a.0, b.0); // (1) pare_down(&mut candidates, closure); // (2) @@ -290,6 +300,59 @@ impl TransitiveRelation { .collect() } + /// Given an element A, returns the maximal set {B} of elements B + /// such that + /// + /// - A != B + /// - A R B is true + /// - for each i, j: B[i] R B[j] does not hold + /// + /// The intuition is that this moves "one step up" through a lattice + /// (where the relation is encoding the `<=` relation for the lattice). + /// So e.g. if the relation is `->` and we have + /// + /// ``` + /// a -> b -> d -> f + /// | ^ + /// +--> c -> e ---+ + /// ``` + /// + /// then `parents(a)` returns `[b, c]`. The `postdom_parent` function + /// would further reduce this to just `f`. + pub fn parents(&self, a: &T) -> Vec<&T> { + let a = match self.index(a) { + Some(a) => a, + None => return vec![] + }; + + // Steal the algorithm for `minimal_upper_bounds` above, but + // with a slight tweak. In the case where `a R a`, we remove + // that from the set of candidates. + let ancestors = self.with_closure(|closure| { + let mut ancestors = closure.intersection(a.0, a.0); + + // Remove anything that can reach `a`. If this is a + // reflexive relation, this will include `a` itself. + ancestors.retain(|&e| !closure.contains(e, a.0)); + + pare_down(&mut ancestors, closure); // (2) + ancestors.reverse(); // (3a) + pare_down(&mut ancestors, closure); // (3b) + ancestors + }); + + ancestors.into_iter() + .rev() // (4) + .map(|i| &self.elements[i]) + .collect() + } + + /// A "best" parent in some sense. See `parents` and + /// `postdom_upper_bound` for more details. + pub fn postdom_parent(&self, a: &T) -> Option<&T> { + self.mutual_immediate_postdominator(self.parents(a)) + } + fn with_closure(&self, op: OP) -> R where OP: FnOnce(&BitMatrix) -> R { @@ -468,11 +531,17 @@ fn test_many_steps() { } #[test] -fn mubs_triange() { +fn mubs_triangle() { + // a -> tcx + // ^ + // | + // b let mut relation = TransitiveRelation::new(); relation.add("a", "tcx"); relation.add("b", "tcx"); assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"tcx"]); + assert_eq!(relation.parents(&"a"), vec![&"tcx"]); + assert_eq!(relation.parents(&"b"), vec![&"tcx"]); } #[test] @@ -498,6 +567,9 @@ fn mubs_best_choice1() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"2"]); + assert_eq!(relation.parents(&"0"), vec![&"2"]); + assert_eq!(relation.parents(&"2"), vec![&"1"]); + assert!(relation.parents(&"1").is_empty()); } #[test] @@ -522,6 +594,9 @@ fn mubs_best_choice2() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]); + assert_eq!(relation.parents(&"0"), vec![&"1"]); + assert_eq!(relation.parents(&"1"), vec![&"2"]); + assert!(relation.parents(&"2").is_empty()); } #[test] @@ -536,10 +611,15 @@ fn mubs_no_best_choice() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1", &"2"]); + assert_eq!(relation.parents(&"0"), vec![&"1", &"2"]); + assert_eq!(relation.parents(&"3"), vec![&"1", &"2"]); } #[test] fn mubs_best_choice_scc() { + // in this case, 1 and 2 form a cycle; we pick arbitrarily (but + // consistently). + let mut relation = TransitiveRelation::new(); relation.add("0", "1"); relation.add("0", "2"); @@ -551,6 +631,7 @@ fn mubs_best_choice_scc() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]); + assert_eq!(relation.parents(&"0"), vec![&"1"]); } #[test] @@ -572,6 +653,8 @@ fn pdub_crisscross() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"a1", &"b1"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"a"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"x")); } #[test] @@ -603,6 +686,9 @@ fn pdub_crisscross_more() { assert_eq!(relation.minimal_upper_bounds(&"a1", &"b1"), vec![&"a2", &"b2"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + + assert_eq!(relation.postdom_parent(&"a"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"x")); } #[test] @@ -620,6 +706,11 @@ fn pdub_lub() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"x"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + + assert_eq!(relation.postdom_parent(&"a"), Some(&"a1")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"b1")); + assert_eq!(relation.postdom_parent(&"a1"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b1"), Some(&"x")); } #[test] @@ -721,3 +812,39 @@ fn mubs_scc_4() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"c"]); } + +#[test] +fn parent() { + // An example that was misbehaving in the compiler. + // + // 4 -> 1 -> 3 + // \ | / + // \ v / + // 2 -> 0 + // + // plus a bunch of self-loops + // + // Here `->` represents `<=` and `0` is `'static`. + + let pairs = vec![ + (2, /*->*/ 0), + (2, /*->*/ 2), + (0, /*->*/ 0), + (0, /*->*/ 0), + (1, /*->*/ 0), + (1, /*->*/ 1), + (3, /*->*/ 0), + (3, /*->*/ 3), + (4, /*->*/ 0), + (4, /*->*/ 1), + (1, /*->*/ 3), + ]; + + let mut relation = TransitiveRelation::new(); + for (a, b) in pairs { + relation.add(a, b); + } + + let p = relation.postdom_parent(&3); + assert_eq!(p, Some(&0)); +} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 57989f75cbaec..b1e9bc7e47c76 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -14,7 +14,7 @@ use rustc::hir::lowering::lower_crate; use rustc::ich::Fingerprint; use rustc_data_structures::stable_hasher::StableHasher; use rustc_mir as mir; -use rustc::session::{Session, CompileResult}; +use rustc::session::{Session, CompileResult, CrateDisambiguator}; use rustc::session::CompileIncomplete; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; @@ -22,7 +22,6 @@ use rustc::lint; use rustc::middle::{self, stability, reachable}; use rustc::middle::cstore::CrateStore; use rustc::middle::privacy::AccessLevels; -use rustc::mir::transform::{MIR_CONST, MIR_VALIDATED, MIR_OPTIMIZED, Passes}; use rustc::ty::{self, TyCtxt, Resolutions, GlobalArenas}; use rustc::traits; use rustc::util::common::{ErrorReported, time}; @@ -38,7 +37,7 @@ use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; -use rustc_passes::{ast_validation, no_asm, loops, consts, static_recursion, hir_stats}; +use rustc_passes::{self, ast_validation, no_asm, loops, consts, static_recursion, hir_stats}; use rustc_const_eval::{self, check_match}; use super::Compilation; use ::DefaultTransCrate; @@ -57,14 +56,15 @@ use std::sync::mpsc; use syntax::{ast, diagnostics, visit}; use syntax::attr; use syntax::ext::base::ExtCtxt; +use syntax::fold::Folder; use syntax::parse::{self, PResult}; -use syntax::symbol::Symbol; use syntax::util::node_count::NodeCounter; use syntax; use syntax_ext; use arena::DroplessArena; use derive_registrar; +use pretty::ReplaceBodyWithLoop; use profile; @@ -208,7 +208,8 @@ pub fn compile_input(sess: &Session, None }; - phase_3_run_analysis_passes(sess, + phase_3_run_analysis_passes(control, + sess, cstore, hir_map, analysis, @@ -349,6 +350,13 @@ pub struct CompileController<'a> { pub keep_ast: bool, // -Zcontinue-parse-after-error pub continue_parse_after_error: bool, + + /// Allows overriding default rustc query providers, + /// after `default_provide` has installed them. + pub provide: Box, + /// Same as `provide`, but only for non-local crates, + /// applied after `default_provide_extern`. + pub provide_extern: Box, } impl<'a> CompileController<'a> { @@ -363,6 +371,8 @@ impl<'a> CompileController<'a> { make_glob_map: MakeGlobMap::No, keep_ast: false, continue_parse_after_error: false, + provide: box |_| {}, + provide_extern: box |_| {}, } } } @@ -633,17 +643,17 @@ pub fn phase_2_configure_and_expand(sess: &Session, *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs); - let disambiguator = Symbol::intern(&compute_crate_disambiguator(sess)); + let disambiguator = compute_crate_disambiguator(sess); *sess.crate_disambiguator.borrow_mut() = Some(disambiguator); rustc_incremental::prepare_session_directory( sess, &crate_name, - &disambiguator.as_str(), + disambiguator, ); let dep_graph = if sess.opts.build_dep_graph() { - let prev_dep_graph = time(time_passes, "load prev dep-graph (new)", || { - rustc_incremental::load_dep_graph_new(sess) + let prev_dep_graph = time(time_passes, "load prev dep-graph", || { + rustc_incremental::load_dep_graph(sess) }); DepGraph::new(prev_dep_graph) @@ -801,6 +811,12 @@ pub fn phase_2_configure_and_expand(sess: &Session, sess.diagnostic()) }); + // If we're actually rustdoc then there's no need to actually compile + // anything, so switch everything to just looping + if sess.opts.actually_rustdoc { + krate = ReplaceBodyWithLoop::new(sess).fold_crate(krate); + } + // If we're in rustdoc we're always compiling as an rlib, but that'll trip a // bunch of checks in the `modify` function below. For now just skip this // step entirely if we're rustdoc as it's not too useful anyway. @@ -908,10 +924,33 @@ pub fn phase_2_configure_and_expand(sess: &Session, }) } +pub fn default_provide(providers: &mut ty::maps::Providers) { + borrowck::provide(providers); + mir::provide(providers); + reachable::provide(providers); + rustc_privacy::provide(providers); + DefaultTransCrate::provide(providers); + typeck::provide(providers); + ty::provide(providers); + traits::provide(providers); + reachable::provide(providers); + rustc_const_eval::provide(providers); + rustc_passes::provide(providers); + middle::region::provide(providers); + cstore::provide(providers); + lint::provide(providers); +} + +pub fn default_provide_extern(providers: &mut ty::maps::Providers) { + cstore::provide_extern(providers); + DefaultTransCrate::provide_extern(providers); +} + /// Run the resolution, typechecking, region checking and other /// miscellaneous analysis passes on the crate. Return various /// structures carrying the results of the analysis. -pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, +pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController, + sess: &'tcx Session, cstore: &'tcx CrateStore, hir_map: hir_map::Map<'tcx>, mut analysis: ty::CrateAnalysis, @@ -941,6 +980,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let time_passes = sess.time_passes(); + let query_result_on_disk_cache = time(time_passes, + "load query result cache", + || rustc_incremental::load_query_result_cache(sess)); + let named_region_map = time(time_passes, "lifetime resolution", || middle::resolve_lifetime::krate(sess, cstore, &hir_map))?; @@ -963,78 +1006,12 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || static_recursion::check_crate(sess, &hir_map))?; let mut local_providers = ty::maps::Providers::default(); - borrowck::provide(&mut local_providers); - mir::provide(&mut local_providers); - reachable::provide(&mut local_providers); - rustc_privacy::provide(&mut local_providers); - DefaultTransCrate::provide_local(&mut local_providers); - typeck::provide(&mut local_providers); - ty::provide(&mut local_providers); - traits::provide(&mut local_providers); - reachable::provide(&mut local_providers); - rustc_const_eval::provide(&mut local_providers); - middle::region::provide(&mut local_providers); - cstore::provide_local(&mut local_providers); - lint::provide(&mut local_providers); - - let mut extern_providers = ty::maps::Providers::default(); - cstore::provide(&mut extern_providers); - DefaultTransCrate::provide_extern(&mut extern_providers); - ty::provide_extern(&mut extern_providers); - traits::provide_extern(&mut extern_providers); - // FIXME(eddyb) get rid of this once we replace const_eval with miri. - rustc_const_eval::provide(&mut extern_providers); - - // Setup the MIR passes that we want to run. - let mut passes = Passes::new(); - passes.push_hook(mir::transform::dump_mir::DumpMir); - - // Remove all `EndRegion` statements that are not involved in borrows. - passes.push_pass(MIR_CONST, mir::transform::clean_end_regions::CleanEndRegions); - - // What we need to do constant evaluation. - passes.push_pass(MIR_CONST, mir::transform::simplify::SimplifyCfg::new("initial")); - passes.push_pass(MIR_CONST, mir::transform::type_check::TypeckMir); - passes.push_pass(MIR_CONST, mir::transform::rustc_peek::SanityCheck); - - // We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED. - - // What we need to run borrowck etc. - - passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants); - passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts")); - passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL); - - // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED. - - passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads); - passes.push_pass(MIR_OPTIMIZED, - mir::transform::simplify_branches::SimplifyBranches::new("initial")); - - // These next passes must be executed together - passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges); - passes.push_pass(MIR_OPTIMIZED, mir::transform::elaborate_drops::ElaborateDrops); - passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads); - // AddValidation needs to run after ElaborateDrops and before EraseRegions, and it needs - // an AllCallEdges pass right before it. - passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::AllCallEdges); - passes.push_pass(MIR_OPTIMIZED, mir::transform::add_validation::AddValidation); - passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("elaborate-drops")); - // No lifetime analysis based on borrowing can be done from here on out. - - // From here on out, regions are gone. - passes.push_pass(MIR_OPTIMIZED, mir::transform::erase_regions::EraseRegions); - - // Optimizations begin. - passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline); - passes.push_pass(MIR_OPTIMIZED, mir::transform::instcombine::InstCombine); - passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator); - passes.push_pass(MIR_OPTIMIZED, mir::transform::copy_prop::CopyPropagation); - passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyLocals); - - passes.push_pass(MIR_OPTIMIZED, mir::transform::generator::StateTransform); - passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges); - passes.push_pass(MIR_OPTIMIZED, mir::transform::dump_mir::Marker("PreTrans")); + default_provide(&mut local_providers); + (control.provide)(&mut local_providers); + + let mut extern_providers = local_providers; + default_provide_extern(&mut extern_providers); + (control.provide_extern)(&mut extern_providers); let (tx, rx) = mpsc::channel(); @@ -1042,19 +1019,19 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, cstore, local_providers, extern_providers, - Rc::new(passes), arenas, arena, resolutions, named_region_map, hir_map, + query_result_on_disk_cache, name, tx, output_filenames, |tcx| { - time(time_passes, - "load_dep_graph", - || rustc_incremental::load_dep_graph(tcx)); + // Do some initialization of the DepGraph that can only be done with the + // tcx available. + rustc_incremental::dep_graph_tcx_init(tcx); time(time_passes, "stability checking", @@ -1311,16 +1288,13 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec String { +pub fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { use std::hash::Hasher; // The crate_disambiguator is a 128 bit hash. The disambiguator is fed // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits // should still be safe enough to avoid collisions in practice. - // FIXME(mw): It seems that the crate_disambiguator is used everywhere as - // a hex-string instead of raw bytes. We should really use the - // smaller representation. let mut hasher = StableHasher::::new(); let mut metadata = session.opts.cg.metadata.clone(); @@ -1339,11 +1313,13 @@ pub fn compute_crate_disambiguator(session: &Session) -> String { hasher.write(s.as_bytes()); } - // If this is an executable, add a special suffix, so that we don't get - // symbol conflicts when linking against a library of the same name. + // Also incorporate crate type, so that we don't get symbol conflicts when + // linking against a library of the same name, if this is an executable. let is_exe = session.crate_types.borrow().contains(&config::CrateTypeExecutable); + hasher.write(if is_exe { b"exe" } else { b"lib" }); + + CrateDisambiguator::from(hasher.finish()) - format!("{}{}", hasher.finish().to_hex(), if is_exe { "-exe" } else {""}) } pub fn build_output_filenames(input: &Input, diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6bdad0b212cf5..bda721d078310 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -138,7 +138,9 @@ pub fn run(run_compiler: F) -> isize } None => { let emitter = - errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None); + errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, + None, + true); let handler = errors::Handler::with_emitter(true, false, Box::new(emitter)); handler.emit(&MultiSpan::new(), "aborting due to previous error(s)", @@ -175,6 +177,7 @@ mod rustc_trans { pub mod write { pub const RELOC_MODEL_ARGS: [(&'static str, ()); 0] = []; pub const CODE_GEN_MODEL_ARGS: [(&'static str, ()); 0] = []; + pub const TLS_MODEL_ARGS: [(&'static str, ()); 0] = []; } } } @@ -562,7 +565,9 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { control.after_hir_lowering.stop = Compilation::Stop; control.after_parse.callback = box move |state| { - state.krate = Some(pretty::fold_crate(state.krate.take().unwrap(), ppm)); + state.krate = Some(pretty::fold_crate(state.session, + state.krate.take().unwrap(), + ppm)); }; control.after_hir_lowering.callback = box move |state| { pretty::print_after_hir_lowering(state.session, @@ -584,7 +589,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { control.after_parse.stop = Compilation::Stop; control.after_parse.callback = box move |state| { - let krate = pretty::fold_crate(state.krate.take().unwrap(), ppm); + let krate = pretty::fold_crate(state.session, state.krate.take().unwrap(), ppm); pretty::print_after_parsing(state.session, state.input, &krate, @@ -795,6 +800,13 @@ impl RustcDefaultCalls { } println!(""); } + PrintRequest::TlsModels => { + println!("Available TLS models:"); + for &(name, _) in rustc_trans::back::write::TLS_MODEL_ARGS.iter(){ + println!(" {}", name); + } + println!(""); + } PrintRequest::TargetCPUs | PrintRequest::TargetFeatures => { rustc_trans::print(*req, sess); } @@ -1208,7 +1220,9 @@ pub fn monitor(f: F) { // Thread panicked without emitting a fatal diagnostic if !value.is::() { let emitter = - Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None)); + Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, + None, + false)); let handler = errors::Handler::with_emitter(true, false, emitter); // a .span_bug or .bug call has already printed what @@ -1238,7 +1252,7 @@ pub fn monitor(f: F) { errors::Level::Note); } - writeln!(io::stderr(), "{}", str::from_utf8(&data.lock().unwrap()).unwrap()).unwrap(); + eprintln!("{}", str::from_utf8(&data.lock().unwrap()).unwrap()); } exit_on_err(); @@ -1259,7 +1273,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { let mut all_errors = Vec::new(); all_errors.extend_from_slice(&rustc::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS); - all_errors.extend_from_slice(&rustc_borrowck::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); #[cfg(feature="llvm")] diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 2f665af8433b9..d930739c9f014 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -77,6 +77,7 @@ pub enum PpFlowGraphMode { pub enum PpMode { PpmSource(PpSourceMode), PpmHir(PpSourceMode), + PpmHirTree(PpSourceMode), PpmFlowGraph(PpFlowGraphMode), PpmMir, PpmMirCFG, @@ -93,6 +94,7 @@ impl PpMode { PpmSource(PpmExpandedIdentified) | PpmSource(PpmExpandedHygiene) | PpmHir(_) | + PpmHirTree(_) | PpmMir | PpmMirCFG | PpmFlowGraph(_) => true, @@ -125,6 +127,7 @@ pub fn parse_pretty(sess: &Session, ("hir", true) => PpmHir(PpmNormal), ("hir,identified", true) => PpmHir(PpmIdentified), ("hir,typed", true) => PpmHir(PpmTyped), + ("hir-tree", true) => PpmHirTree(PpmNormal), ("mir", true) => PpmMir, ("mir-cfg", true) => PpmMirCFG, ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default), @@ -227,7 +230,9 @@ impl PpSourceMode { f(&annotation, hir_map.forest.krate()) } PpmTyped => { - abort_on_err(driver::phase_3_run_analysis_passes(sess, + let control = &driver::CompileController::basic(); + abort_on_err(driver::phase_3_run_analysis_passes(control, + sess, cstore, hir_map.clone(), analysis.clone(), @@ -633,13 +638,14 @@ impl UserIdentifiedItem { // ambitious form of the closed RFC #1637. See also [#34511]. // // [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 -pub struct ReplaceBodyWithLoop { +pub struct ReplaceBodyWithLoop<'a> { within_static_or_const: bool, + sess: &'a Session, } -impl ReplaceBodyWithLoop { - pub fn new() -> ReplaceBodyWithLoop { - ReplaceBodyWithLoop { within_static_or_const: false } +impl<'a> ReplaceBodyWithLoop<'a> { + pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { + ReplaceBodyWithLoop { within_static_or_const: false, sess } } fn run R>(&mut self, is_const: bool, action: F) -> R { @@ -659,10 +665,26 @@ impl ReplaceBodyWithLoop { ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), - ast::TyKind::Tup(ref tys) => tys.iter().any(|subty| involves_impl_trait(subty)), + ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), + ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { + match seg.parameters.as_ref().map(|p| &**p) { + None => false, + Some(&ast::PathParameters::AngleBracketed(ref data)) => + any_involves_impl_trait(data.types.iter()) || + any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty)), + Some(&ast::PathParameters::Parenthesized(ref data)) => + any_involves_impl_trait(data.inputs.iter()) || + any_involves_impl_trait(data.output.iter()), + } + }), _ => false, } } + + fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { + it.any(|subty| involves_impl_trait(subty)) + } + involves_impl_trait(ty) } else { false @@ -670,7 +692,7 @@ impl ReplaceBodyWithLoop { } } -impl fold::Folder for ReplaceBodyWithLoop { +impl<'a> fold::Folder for ReplaceBodyWithLoop<'a> { fn fold_item_kind(&mut self, i: ast::ItemKind) -> ast::ItemKind { let is_const = match i { ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, @@ -702,11 +724,13 @@ impl fold::Folder for ReplaceBodyWithLoop { } fn fold_block(&mut self, b: P) -> P { - fn expr_to_block(rules: ast::BlockCheckMode, e: Option>) -> P { + fn expr_to_block(rules: ast::BlockCheckMode, + e: Option>, + sess: &Session) -> P { P(ast::Block { stmts: e.map(|e| { ast::Stmt { - id: ast::DUMMY_NODE_ID, + id: sess.next_node_id(), span: e.span, node: ast::StmtKind::Expr(e), } @@ -714,22 +738,22 @@ impl fold::Folder for ReplaceBodyWithLoop { .into_iter() .collect(), rules, - id: ast::DUMMY_NODE_ID, + id: sess.next_node_id(), span: syntax_pos::DUMMY_SP, }) } if !self.within_static_or_const { - let empty_block = expr_to_block(BlockCheckMode::Default, None); + let empty_block = expr_to_block(BlockCheckMode::Default, None, self.sess); let loop_expr = P(ast::Expr { node: ast::ExprKind::Loop(empty_block, None), - id: ast::DUMMY_NODE_ID, + id: self.sess.next_node_id(), span: syntax_pos::DUMMY_SP, attrs: ast::ThinVec::new(), }); - expr_to_block(b.rules, Some(loop_expr)) + expr_to_block(b.rules, Some(loop_expr), self.sess) } else { fold::noop_fold_block(b, self) @@ -808,9 +832,9 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, } } -pub fn fold_crate(krate: ast::Crate, ppm: PpMode) -> ast::Crate { +pub fn fold_crate(sess: &Session, krate: ast::Crate, ppm: PpMode) -> ast::Crate { if let PpmSource(PpmEveryBodyLoops) = ppm { - let mut fold = ReplaceBodyWithLoop::new(); + let mut fold = ReplaceBodyWithLoop::new(sess); fold.fold_crate(krate) } else { krate @@ -953,6 +977,23 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, }) } + (PpmHirTree(s), None) => { + let out: &mut Write = &mut out; + s.call_with_pp_support_hir(sess, + cstore, + hir_map, + analysis, + resolutions, + arena, + arenas, + output_filenames, + crate_name, + move |_annotation, krate| { + debug!("pretty printing source code {:?}", s); + write!(out, "{:#?}", krate) + }) + } + (PpmHir(s), Some(uii)) => { let out: &mut Write = &mut out; s.call_with_pp_support_hir(sess, @@ -987,6 +1028,28 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, pp_state.s.eof() }) } + + (PpmHirTree(s), Some(uii)) => { + let out: &mut Write = &mut out; + s.call_with_pp_support_hir(sess, + cstore, + hir_map, + analysis, + resolutions, + arena, + arenas, + output_filenames, + crate_name, + move |_annotation, _krate| { + debug!("pretty printing source code {:?}", s); + for node_id in uii.all_matching_node_ids(hir_map) { + let node = hir_map.get(node_id); + write!(out, "{:#?}", node)?; + } + Ok(()) + }) + } + _ => unreachable!(), } .unwrap(); @@ -1020,7 +1083,9 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let mut out = Vec::new(); - abort_on_err(driver::phase_3_run_analysis_passes(sess, + let control = &driver::CompileController::basic(); + abort_on_err(driver::phase_3_run_analysis_passes(control, + sess, cstore, hir_map.clone(), analysis.clone(), diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 6de36820f0c19..0818b929ee7ad 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -17,17 +17,17 @@ use driver; use rustc_lint; use rustc_resolve::MakeGlobMap; use rustc_trans; -use rustc::middle::free_region::FreeRegionMap; use rustc::middle::region; use rustc::middle::resolve_lifetime; use rustc::ty::subst::{Kind, Subst}; use rustc::traits::{ObligationCause, Reveal}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::maps::OnDiskCache; use rustc::infer::{self, InferOk, InferResult}; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::infer::type_variable::TypeVariableOrigin; use rustc_metadata::cstore::CStore; use rustc::hir::map as hir_map; -use rustc::mir::transform::Passes; use rustc::session::{self, config}; use rustc::session::config::{OutputFilenames, OutputTypes}; use rustc_trans_utils::trans_crate::TransCrate; @@ -150,26 +150,27 @@ fn test_env(source_string: &str, &*cstore, ty::maps::Providers::default(), ty::maps::Providers::default(), - Rc::new(Passes::new()), &arenas, &arena, resolutions, named_region_map.unwrap(), hir_map, + OnDiskCache::new_empty(sess.codemap()), "test_crate", tx, &outputs, |tcx| { tcx.infer_ctxt().enter(|infcx| { let mut region_scope_tree = region::ScopeTree::default(); + let param_env = ty::ParamEnv::empty(Reveal::UserFacing); body(Env { infcx: &infcx, region_scope_tree: &mut region_scope_tree, - param_env: ty::ParamEnv::empty(Reveal::UserFacing), + param_env: param_env, }); - let free_regions = FreeRegionMap::new(); + let outlives_env = OutlivesEnvironment::new(param_env); let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID); - infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &free_regions); + infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env); assert_eq!(tcx.sess.err_count(), expected_err_count); }); }); @@ -251,7 +252,7 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { hir::ItemUnion(..) | hir::ItemTrait(..) | hir::ItemImpl(..) | - hir::ItemDefaultImpl(..) => None, + hir::ItemAutoImpl(..) => None, hir::ItemMod(ref m) => search_mod(this, m, idx, names), }; @@ -353,28 +354,10 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize) } - pub fn t_rptr_static(&self) -> Ty<'tcx> { - self.infcx.tcx.mk_imm_ref(self.infcx.tcx.types.re_static, - self.tcx().types.isize) - } - - pub fn t_rptr_empty(&self) -> Ty<'tcx> { - self.infcx.tcx.mk_imm_ref(self.infcx.tcx.types.re_empty, - self.tcx().types.isize) - } - pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> { self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(t1, t2) } - pub fn lub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { - self.infcx.at(&ObligationCause::dummy(), self.param_env).lub(t1, t2) - } - - pub fn glb(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { - self.infcx.at(&ObligationCause::dummy(), self.param_env).glb(t1, t2) - } - /// Checks that `t1 <: t2` is true (this may register additional /// region checks). pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) { @@ -399,37 +382,6 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } } } - - /// Checks that `LUB(t1,t2) == t_lub` - pub fn check_lub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>, t_lub: Ty<'tcx>) { - match self.lub(t1, t2) { - Ok(InferOk { obligations, value: t }) => { - // None of these tests should require nested obligations: - assert!(obligations.is_empty()); - - self.assert_eq(t, t_lub); - } - Err(ref e) => panic!("unexpected error in LUB: {}", e), - } - } - - /// Checks that `GLB(t1,t2) == t_glb` - pub fn check_glb(&self, t1: Ty<'tcx>, t2: Ty<'tcx>, t_glb: Ty<'tcx>) { - debug!("check_glb(t1={}, t2={}, t_glb={})", t1, t2, t_glb); - match self.glb(t1, t2) { - Err(e) => panic!("unexpected error computing LUB: {:?}", e), - Ok(InferOk { obligations, value: t }) => { - // None of these tests should require nested obligations: - assert!(obligations.is_empty()); - - self.assert_eq(t, t_glb); - - // sanity check for good measure: - self.assert_subtype(t, t1); - self.assert_subtype(t, t2); - } - } - } } #[test] @@ -508,169 +460,6 @@ fn sub_free_bound_false_infer() { }) } -#[test] -fn lub_free_bound_infer() { - //! Test result of: - //! - //! LUB(fn(_#1), for<'b> fn(&'b isize)) - //! - //! This should yield `fn(&'_ isize)`. We check - //! that it yields `fn(&'x isize)` for some free `'x`, - //! anyhow. - - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_free1 = env.t_rptr_free(1); - env.check_lub(env.t_fn(&[t_infer1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free1], env.tcx().types.isize)); - }); -} - -#[test] -fn lub_bound_bound() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_bound2 = env.t_rptr_late_bound(2); - env.check_lub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound2], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); - }) -} - -#[test] -fn lub_bound_free() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_free1 = env.t_rptr_free(1); - env.check_lub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free1], env.tcx().types.isize)); - }) -} - -#[test] -fn lub_bound_static() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_static = env.t_rptr_static(); - env.check_lub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_static], env.tcx().types.isize), - env.t_fn(&[t_rptr_static], env.tcx().types.isize)); - }) -} - -#[test] -fn lub_bound_bound_inverse_order() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_bound2 = env.t_rptr_late_bound(2); - env.check_lub(env.t_fn(&[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1), - env.t_fn(&[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1), - env.t_fn(&[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1)); - }) -} - -#[test] -fn lub_free_free() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_rptr_free1 = env.t_rptr_free(1); - let t_rptr_free2 = env.t_rptr_free(2); - let t_rptr_static = env.t_rptr_static(); - env.check_lub(env.t_fn(&[t_rptr_free1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free2], env.tcx().types.isize), - env.t_fn(&[t_rptr_static], env.tcx().types.isize)); - }) -} - -#[test] -fn lub_returning_scope() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_rptr_scope10 = env.t_rptr_scope(10); - let t_rptr_scope11 = env.t_rptr_scope(11); - let t_rptr_empty = env.t_rptr_empty(); - env.check_lub(env.t_fn(&[t_rptr_scope10], env.tcx().types.isize), - env.t_fn(&[t_rptr_scope11], env.tcx().types.isize), - env.t_fn(&[t_rptr_empty], env.tcx().types.isize)); - }); -} - -#[test] -fn glb_free_free_with_common_scope() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_rptr_free1 = env.t_rptr_free(1); - let t_rptr_free2 = env.t_rptr_free(2); - let t_rptr_scope = env.t_rptr_scope(1); - env.check_glb(env.t_fn(&[t_rptr_free1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free2], env.tcx().types.isize), - env.t_fn(&[t_rptr_scope], env.tcx().types.isize)); - }) -} - -#[test] -fn glb_bound_bound() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_bound2 = env.t_rptr_late_bound(2); - env.check_glb(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound2], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); - }) -} - -#[test] -fn glb_bound_free() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { - env.create_simple_region_hierarchy(); - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_free1 = env.t_rptr_free(1); - env.check_glb(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); - }) -} - -#[test] -fn glb_bound_free_infer() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); - - // compute GLB(fn(_) -> isize, for<'b> fn(&'b isize) -> isize), - // which should yield for<'b> fn(&'b isize) -> isize - env.check_glb(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_infer1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); - - // as a side-effect, computing GLB should unify `_` with - // `&'_ isize` - let t_resolve1 = env.infcx.shallow_resolve(t_infer1); - match t_resolve1.sty { - ty::TyRef(..) => {} - _ => { - panic!("t_resolve1={:?}", t_resolve1); - } - } - }) -} - -#[test] -fn glb_bound_static() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_rptr_bound1 = env.t_rptr_late_bound(1); - let t_rptr_static = env.t_rptr_static(); - env.check_glb(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_static], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); - }) -} - /// Test substituting a bound region into a function, which introduces another level of binding. /// This requires adjusting the Debruijn index. #[test] diff --git a/src/librustc_errors/Cargo.toml b/src/librustc_errors/Cargo.toml index 78ff52b4b2371..c72e9dd0ea322 100644 --- a/src/librustc_errors/Cargo.toml +++ b/src/librustc_errors/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["dylib"] [dependencies] serialize = { path = "../libserialize" } syntax_pos = { path = "../libsyntax_pos" } +rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 9aae188f9ecdf..221c75186e9c8 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -9,31 +9,37 @@ // except according to those terms. use CodeSuggestion; +use SubstitutionPart; use Substitution; use Level; -use RenderSpan; use std::fmt; use syntax_pos::{MultiSpan, Span}; use snippet::Style; #[must_use] -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct Diagnostic { pub level: Level, pub message: Vec<(String, Style)>, - pub code: Option, + pub code: Option, pub span: MultiSpan, pub children: Vec, pub suggestions: Vec, } +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +pub enum DiagnosticId { + Error(String), + Lint(String), +} + /// For example a note attached to an error. -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct SubDiagnostic { pub level: Level, pub message: Vec<(String, Style)>, pub span: MultiSpan, - pub render_span: Option, + pub render_span: Option, } #[derive(PartialEq, Eq)] @@ -81,7 +87,7 @@ impl Diagnostic { Diagnostic::new_with_code(level, None, message) } - pub fn new_with_code(level: Level, code: Option, message: &str) -> Self { + pub fn new_with_code(level: Level, code: Option, message: &str) -> Self { Diagnostic { level, message: vec![(message.to_owned(), Style::NoStyle)], @@ -208,12 +214,14 @@ impl Diagnostic { /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline it will only show the text message and not the text. /// - /// See `diagnostic::CodeSuggestion` for more information. + /// See `CodeSuggestion` for more information. pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { self.suggestions.push(CodeSuggestion { - substitution_parts: vec![Substitution { - span: sp, - substitutions: vec![suggestion], + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], }], msg: msg.to_owned(), show_code_when_inline: false, @@ -229,18 +237,21 @@ impl Diagnostic { /// "try adding parentheses: `(tup.0).1`" /// /// The message + /// /// * should not end in any punctuation (a `:` is added automatically) /// * should not be a question /// * should not contain any parts like "the following", "as shown" /// * may look like "to do xyz, use" or "to do xyz, use abc" /// * may contain a name of a function, variable or type, but not whole expressions /// - /// See `diagnostic::CodeSuggestion` for more information. + /// See `CodeSuggestion` for more information. pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { self.suggestions.push(CodeSuggestion { - substitution_parts: vec![Substitution { - span: sp, - substitutions: vec![suggestion], + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], }], msg: msg.to_owned(), show_code_when_inline: true, @@ -248,12 +259,15 @@ impl Diagnostic { self } + /// Prints out a message with multiple suggested edits of the code. pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec) -> &mut Self { self.suggestions.push(CodeSuggestion { - substitution_parts: vec![Substitution { - span: sp, - substitutions: suggestions, - }], + substitutions: suggestions.into_iter().map(|snippet| Substitution { + parts: vec![SubstitutionPart { + snippet, + span: sp, + }], + }).collect(), msg: msg.to_owned(), show_code_when_inline: true, }); @@ -265,7 +279,7 @@ impl Diagnostic { self } - pub fn code(&mut self, s: String) -> &mut Self { + pub fn code(&mut self, s: DiagnosticId) -> &mut Self { self.code = Some(s); self } @@ -292,7 +306,7 @@ impl Diagnostic { level: Level, message: &str, span: MultiSpan, - render_span: Option) { + render_span: Option) { let sub = SubDiagnostic { level, message: vec![(message.to_owned(), Style::NoStyle)], @@ -308,7 +322,7 @@ impl Diagnostic { level: Level, message: Vec<(String, Style)>, span: MultiSpan, - render_span: Option) { + render_span: Option) { let sub = SubDiagnostic { level, message, diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 2cd433bfe3aee..27e895164e764 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -9,6 +9,7 @@ // except according to those terms. use Diagnostic; +use DiagnosticId; use DiagnosticStyledString; use Level; @@ -22,7 +23,7 @@ use syntax_pos::{MultiSpan, Span}; #[must_use] #[derive(Clone)] pub struct DiagnosticBuilder<'a> { - handler: &'a Handler, + pub handler: &'a Handler, diagnostic: Diagnostic, } @@ -192,7 +193,7 @@ impl<'a> DiagnosticBuilder<'a> { suggestions: Vec) -> &mut Self); forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); - forward!(pub fn code(&mut self, s: String) -> &mut Self); + forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); /// Convenience function for internal use, clients should use one of the /// struct_* methods on Handler. @@ -204,7 +205,7 @@ impl<'a> DiagnosticBuilder<'a> { /// struct_* methods on Handler. pub fn new_with_code(handler: &'a Handler, level: Level, - code: Option, + code: Option, message: &str) -> DiagnosticBuilder<'a> { let diagnostic = Diagnostic::new_with_code(level, code, message); diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index daa132dbf6213..16bbd755b8869 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,10 +10,9 @@ use self::Destination::*; -use syntax_pos::{DUMMY_SP, FileMap, Span, MultiSpan, CharPos}; +use syntax_pos::{DUMMY_SP, FileMap, Span, MultiSpan}; -use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper}; -use RenderSpan::*; +use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper, DiagnosticId}; use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; use styled_buffer::StyledBuffer; @@ -35,48 +34,46 @@ impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { let mut primary_span = db.span.clone(); let mut children = db.children.clone(); + let mut suggestions: &[_] = &[]; if let Some((sugg, rest)) = db.suggestions.split_first() { if rest.is_empty() && - // don't display multipart suggestions as labels - sugg.substitution_parts.len() == 1 && // don't display multi-suggestions as labels - sugg.substitutions() == 1 && + sugg.substitutions.len() == 1 && + // don't display multipart suggestions as labels + sugg.substitutions[0].parts.len() == 1 && // don't display long messages as labels sugg.msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels - sugg.substitution_parts[0].substitutions[0].find('\n').is_none() { - let substitution = &sugg.substitution_parts[0].substitutions[0]; + !sugg.substitutions[0].parts[0].snippet.contains('\n') { + let substitution = &sugg.substitutions[0].parts[0].snippet; let msg = if substitution.len() == 0 || !sugg.show_code_when_inline { - // This substitution is only removal or we explicitely don't want to show the + // This substitution is only removal or we explicitly don't want to show the // code inline, don't show it format!("help: {}", sugg.msg) } else { format!("help: {}: `{}`", sugg.msg, substitution) }; - primary_span.push_span_label(sugg.substitution_spans().next().unwrap(), msg); + primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg); } else { // if there are multiple suggestions, print them all in full // to be consistent. We could try to figure out if we can // make one (or the first one) inline, but that would give // undue importance to a semi-random suggestion - for sugg in &db.suggestions { - children.push(SubDiagnostic { - level: Level::Help, - message: Vec::new(), - span: MultiSpan::new(), - render_span: Some(Suggestion(sugg.clone())), - }); - } + suggestions = &db.suggestions; } } - self.fix_multispans_in_std_macros(&mut primary_span, &mut children); + if !db.handler.flags.external_macro_backtrace { + self.fix_multispans_in_std_macros(&mut primary_span, &mut children); + } self.emit_messages_default(&db.level, + db.handler.flags.external_macro_backtrace, &db.styled_message(), &db.code, &primary_span, - &children); + &children, + &suggestions); } } @@ -107,6 +104,7 @@ impl ColorConfig { pub struct EmitterWriter { dst: Destination, cm: Option>, + short_message: bool, } struct FileWithAnnotatedLines { @@ -116,25 +114,34 @@ struct FileWithAnnotatedLines { } impl EmitterWriter { - pub fn stderr(color_config: ColorConfig, code_map: Option>) -> EmitterWriter { + pub fn stderr(color_config: ColorConfig, + code_map: Option>, + short_message: bool) + -> EmitterWriter { if color_config.use_color() { let dst = Destination::from_stderr(); EmitterWriter { dst, cm: code_map, + short_message: short_message, } } else { EmitterWriter { dst: Raw(Box::new(io::stderr())), cm: code_map, + short_message: short_message, } } } - pub fn new(dst: Box, code_map: Option>) -> EmitterWriter { + pub fn new(dst: Box, + code_map: Option>, + short_message: bool) + -> EmitterWriter { EmitterWriter { dst: Raw(dst), cm: code_map, + short_message: short_message, } } @@ -191,8 +198,8 @@ impl EmitterWriter { // 6..7. This is degenerate input, but it's best to degrade // gracefully -- and the parser likes to supply a span like // that for EOF, in particular. - if lo.col == hi.col && lo.line == hi.line { - hi.col = CharPos(lo.col.0 + 1); + if lo.col_display == hi.col_display && lo.line == hi.line { + hi.col_display += 1; } let ann_type = if lo.line != hi.line { @@ -200,8 +207,8 @@ impl EmitterWriter { depth: 1, line_start: lo.line, line_end: hi.line, - start_col: lo.col.0, - end_col: hi.col.0, + start_col: lo.col_display, + end_col: hi.col_display, is_primary: span_label.is_primary, label: span_label.label.clone(), }; @@ -211,8 +218,8 @@ impl EmitterWriter { AnnotationType::Singleline }; let ann = Annotation { - start_col: lo.col.0, - end_col: hi.col.0, + start_col: lo.col_display, + end_col: hi.col_display, is_primary: span_label.is_primary, label: span_label.label.clone(), annotation_type: ann_type, @@ -671,7 +678,7 @@ impl EmitterWriter { Style::LabelSecondary }; Some((p, style)) - }, + } _ => None } @@ -689,11 +696,13 @@ impl EmitterWriter { } } } - for span_label in msp.span_labels() { - if span_label.span != DUMMY_SP { - let hi = cm.lookup_char_pos(span_label.span.hi()); - if hi.line > max { - max = hi.line; + if !self.short_message { + for span_label in msp.span_labels() { + if span_label.span != DUMMY_SP { + let hi = cm.lookup_char_pos(span_label.span.hi()); + if hi.line > max { + max = hi.line; + } } } } @@ -787,8 +796,12 @@ impl EmitterWriter { if spans_updated { children.push(SubDiagnostic { level: Level::Note, - message: vec![("this error originates in a macro outside of the current crate" - .to_string(), Style::NoStyle)], + message: vec![ + (["this error originates in a macro outside of the current crate", + "(in Nightly builds, run with -Z external-macro-backtrace for more info)"] + .join(" "), + Style::NoStyle), + ], span: MultiSpan::new(), render_span: None, }); @@ -874,14 +887,16 @@ impl EmitterWriter { fn emit_message_default(&mut self, msp: &MultiSpan, msg: &Vec<(String, Style)>, - code: &Option, + code: &Option, level: &Level, + external_macro_backtrace: bool, max_line_num_len: usize, is_secondary: bool) -> io::Result<()> { let mut buffer = StyledBuffer::new(); - if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary { + if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary + && !self.short_message { // This is a secondary message with no span info for _ in 0..max_line_num_len { buffer.prepend(0, " ", Style::NoStyle); @@ -892,13 +907,11 @@ impl EmitterWriter { self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None); } else { buffer.append(0, &level.to_string(), Style::Level(level.clone())); - match code { - &Some(ref code) => { - buffer.append(0, "[", Style::Level(level.clone())); - buffer.append(0, &code, Style::Level(level.clone())); - buffer.append(0, "]", Style::Level(level.clone())); - } - _ => {} + // only render error codes, not lint codes + if let Some(DiagnosticId::Error(ref code)) = *code { + buffer.append(0, "[", Style::Level(level.clone())); + buffer.append(0, &code, Style::Level(level.clone())); + buffer.append(0, "]", Style::Level(level.clone())); } buffer.append(0, ": ", Style::HeaderMsg); for &(ref text, _) in msg.iter() { @@ -916,12 +929,12 @@ impl EmitterWriter { if primary_span != &&DUMMY_SP { (cm.lookup_char_pos(primary_span.lo()), cm) } else { - emit_to_destination(&buffer.render(), level, &mut self.dst)?; + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; return Ok(()); } } else { // If we don't have span information, emit and exit - emit_to_destination(&buffer.render(), level, &mut self.dst)?; + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; return Ok(()); }; if let Ok(pos) = @@ -940,18 +953,24 @@ impl EmitterWriter { // to do this, we need to know if this span will be primary let is_primary = primary_lo.file.name == annotated_file.file.name; if is_primary { - // remember where we are in the output buffer for easy reference - let buffer_msg_line_offset = buffer.num_lines(); - - buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); let loc = primary_lo.clone(); - buffer.append(buffer_msg_line_offset, - &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1), - Style::LineAndColumn); - for _ in 0..max_line_num_len { - buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); + if !self.short_message { + // remember where we are in the output buffer for easy reference + let buffer_msg_line_offset = buffer.num_lines(); + + buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); + buffer.append(buffer_msg_line_offset, + &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1), + Style::LineAndColumn); + for _ in 0..max_line_num_len { + buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); + } + } else { + buffer.prepend(0, + &format!("{}:{}:{} - ", loc.file.name, loc.line, loc.col.0 + 1), + Style::LineAndColumn); } - } else { + } else if !self.short_message { // remember where we are in the output buffer for easy reference let buffer_msg_line_offset = buffer.num_lines(); @@ -968,104 +987,117 @@ impl EmitterWriter { } } - // Put in the spacer between the location and annotated source - let buffer_msg_line_offset = buffer.num_lines(); - draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + if !self.short_message { + // Put in the spacer between the location and annotated source + let buffer_msg_line_offset = buffer.num_lines(); + draw_col_separator_no_space(&mut buffer, + buffer_msg_line_offset, + max_line_num_len + 1); - // Contains the vertical lines' positions for active multiline annotations - let mut multilines = HashMap::new(); + // Contains the vertical lines' positions for active multiline annotations + let mut multilines = HashMap::new(); - // Next, output the annotate source for this file - for line_idx in 0..annotated_file.lines.len() { - let previous_buffer_line = buffer.num_lines(); + // Next, output the annotate source for this file + for line_idx in 0..annotated_file.lines.len() { + let previous_buffer_line = buffer.num_lines(); - let width_offset = 3 + max_line_num_len; - let code_offset = if annotated_file.multiline_depth == 0 { - width_offset - } else { - width_offset + annotated_file.multiline_depth + 1 - }; + let width_offset = 3 + max_line_num_len; + let code_offset = if annotated_file.multiline_depth == 0 { + width_offset + } else { + width_offset + annotated_file.multiline_depth + 1 + }; - let depths = self.render_source_line(&mut buffer, - annotated_file.file.clone(), - &annotated_file.lines[line_idx], - width_offset, - code_offset); + let depths = self.render_source_line(&mut buffer, + annotated_file.file.clone(), + &annotated_file.lines[line_idx], + width_offset, + code_offset); - let mut to_add = HashMap::new(); + let mut to_add = HashMap::new(); - for (depth, style) in depths { - if multilines.get(&depth).is_some() { - multilines.remove(&depth); - } else { - to_add.insert(depth, style); + for (depth, style) in depths { + if multilines.get(&depth).is_some() { + multilines.remove(&depth); + } else { + to_add.insert(depth, style); + } } - } - // Set the multiline annotation vertical lines to the left of - // the code in this line. - for (depth, style) in &multilines { - for line in previous_buffer_line..buffer.num_lines() { - draw_multiline_line(&mut buffer, - line, - width_offset, - *depth, - *style); - } - } - // check to see if we need to print out or elide lines that come between - // this annotated line and the next one. - if line_idx < (annotated_file.lines.len() - 1) { - let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - - annotated_file.lines[line_idx].line_index; - if line_idx_delta > 2 { - let last_buffer_line_num = buffer.num_lines(); - buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); - - // Set the multiline annotation vertical lines on `...` bridging line. - for (depth, style) in &multilines { + // Set the multiline annotation vertical lines to the left of + // the code in this line. + for (depth, style) in &multilines { + for line in previous_buffer_line..buffer.num_lines() { draw_multiline_line(&mut buffer, - last_buffer_line_num, + line, width_offset, *depth, *style); } - } else if line_idx_delta == 2 { - let unannotated_line = annotated_file.file - .get_line(annotated_file.lines[line_idx].line_index) - .unwrap_or_else(|| Cow::from("")); - - let last_buffer_line_num = buffer.num_lines(); - - buffer.puts(last_buffer_line_num, - 0, - &(annotated_file.lines[line_idx + 1].line_index - 1) - .to_string(), - Style::LineNumber); - draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len); - buffer.puts(last_buffer_line_num, - code_offset, - &unannotated_line, - Style::Quotation); - - for (depth, style) in &multilines { - draw_multiline_line(&mut buffer, - last_buffer_line_num, - width_offset, - *depth, - *style); + } + // check to see if we need to print out or elide lines that come between + // this annotated line and the next one. + if line_idx < (annotated_file.lines.len() - 1) { + let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - + annotated_file.lines[line_idx].line_index; + if line_idx_delta > 2 { + let last_buffer_line_num = buffer.num_lines(); + buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); + + // Set the multiline annotation vertical lines on `...` bridging line. + for (depth, style) in &multilines { + draw_multiline_line(&mut buffer, + last_buffer_line_num, + width_offset, + *depth, + *style); + } + } else if line_idx_delta == 2 { + let unannotated_line = annotated_file.file + .get_line(annotated_file.lines[line_idx].line_index) + .unwrap_or_else(|| Cow::from("")); + + let last_buffer_line_num = buffer.num_lines(); + + buffer.puts(last_buffer_line_num, + 0, + &(annotated_file.lines[line_idx + 1].line_index - 1) + .to_string(), + Style::LineNumber); + draw_col_separator(&mut buffer, + last_buffer_line_num, + 1 + max_line_num_len); + buffer.puts(last_buffer_line_num, + code_offset, + &unannotated_line, + Style::Quotation); + + for (depth, style) in &multilines { + draw_multiline_line(&mut buffer, + last_buffer_line_num, + width_offset, + *depth, + *style); + } } } + + multilines.extend(&to_add); } + } + } - multilines.extend(&to_add); + if external_macro_backtrace { + if let Some(ref primary_span) = msp.primary_span().as_ref() { + self.render_macro_backtrace_old_school(primary_span, &mut buffer)?; } } // final step: take our styled buffer, render it, then output it - emit_to_destination(&buffer.render(), level, &mut self.dst)?; + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; Ok(()) + } fn emit_suggestion_default(&mut self, suggestion: &CodeSuggestion, @@ -1074,14 +1106,10 @@ impl EmitterWriter { -> io::Result<()> { use std::borrow::Borrow; - let primary_sub = &suggestion.substitution_parts[0]; if let Some(ref cm) = self.cm { let mut buffer = StyledBuffer::new(); - let lines = cm.span_to_lines(primary_sub.span).unwrap(); - - assert!(!lines.lines.is_empty()); - + // Render the suggestion message buffer.append(0, &level.to_string(), Style::Level(level.clone())); buffer.append(0, ": ", Style::HeaderMsg); self.msg_to_buffer(&mut buffer, @@ -1090,14 +1118,22 @@ impl EmitterWriter { "suggestion", Some(Style::HeaderMsg)); + // Render the replacements for each suggestion let suggestions = suggestion.splice_lines(cm.borrow()); - let span_start_pos = cm.lookup_char_pos(primary_sub.span.lo()); - let line_start = span_start_pos.line; - draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); + let mut row_num = 2; - for (&(ref complete, show_underline), ref sub) in suggestions - .iter().zip(primary_sub.substitutions.iter()).take(MAX_SUGGESTIONS) - { + for &(ref complete, ref parts) in suggestions.iter().take(MAX_SUGGESTIONS) { + let show_underline = parts.len() == 1 + && complete.lines().count() == 1 + && parts[0].snippet.trim() != complete.trim(); + + let lines = cm.span_to_lines(parts[0].span).unwrap(); + + assert!(!lines.lines.is_empty()); + + let span_start_pos = cm.lookup_char_pos(parts[0].span.lo()); + let line_start = span_start_pos.line; + draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); let mut line_pos = 0; // Only show underline if there's a single suggestion and it is a single line let mut lines = complete.lines(); @@ -1112,21 +1148,22 @@ impl EmitterWriter { buffer.append(row_num, line, Style::NoStyle); line_pos += 1; row_num += 1; - // Only show an underline in the suggestions if the suggestion is not the - // entirety of the code being shown and the displayed code is not multiline. - if show_underline { - draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - let sub_len = sub.trim_right().len(); - let underline_start = span_start_pos.col.0; - let underline_end = span_start_pos.col.0 + sub_len; - for p in underline_start..underline_end { - buffer.putc(row_num, - max_line_num_len + 3 + p, - '^', - Style::UnderlinePrimary); - } - row_num += 1; + } + // Only show an underline in the suggestions if the suggestion is not the + // entirety of the code being shown and the displayed code is not multiline. + if show_underline { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + let start = parts[0].snippet.len() - parts[0].snippet.trim_left().len(); + let sub_len = parts[0].snippet.trim().len(); + let underline_start = span_start_pos.col.0 + start; + let underline_end = span_start_pos.col.0 + start + sub_len; + for p in underline_start..underline_end { + buffer.putc(row_num, + max_line_num_len + 3 + p, + '^', + Style::UnderlinePrimary); } + row_num += 1; } // if we elided some lines, add an ellipsis @@ -1141,60 +1178,60 @@ impl EmitterWriter { let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS); buffer.puts(row_num, 0, &msg, Style::NoStyle); } - emit_to_destination(&buffer.render(), level, &mut self.dst)?; + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; } Ok(()) } fn emit_messages_default(&mut self, level: &Level, + external_macro_backtrace: bool, message: &Vec<(String, Style)>, - code: &Option, + code: &Option, span: &MultiSpan, - children: &Vec) { + children: &Vec, + suggestions: &[CodeSuggestion]) { let max_line_num = self.get_max_line_num(span, children); let max_line_num_len = max_line_num.to_string().len(); - match self.emit_message_default(span, message, code, level, max_line_num_len, false) { + match self.emit_message_default(span, + message, + code, + level, + external_macro_backtrace, + max_line_num_len, + false) { Ok(()) => { if !children.is_empty() { let mut buffer = StyledBuffer::new(); - draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); - match emit_to_destination(&buffer.render(), level, &mut self.dst) { + if !self.short_message { + draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); + } + match emit_to_destination(&buffer.render(), level, &mut self.dst, + self.short_message) { Ok(()) => (), Err(e) => panic!("failed to emit error: {}", e) } } - for child in children { - match child.render_span { - Some(FullSpan(ref msp)) => { - match self.emit_message_default(msp, - &child.styled_message(), - &None, - &child.level, - max_line_num_len, - true) { - Err(e) => panic!("failed to emit error: {}", e), - _ => () - } - }, - Some(Suggestion(ref cs)) => { - match self.emit_suggestion_default(cs, - &child.level, - max_line_num_len) { - Err(e) => panic!("failed to emit error: {}", e), - _ => () - } - }, - None => { - match self.emit_message_default(&child.span, - &child.styled_message(), - &None, - &child.level, - max_line_num_len, - true) { - Err(e) => panic!("failed to emit error: {}", e), - _ => (), - } + if !self.short_message { + for child in children { + let span = child.render_span.as_ref().unwrap_or(&child.span); + match self.emit_message_default(&span, + &child.styled_message(), + &None, + &child.level, + external_macro_backtrace, + max_line_num_len, + true) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } + for sugg in suggestions { + match self.emit_suggestion_default(sugg, + &Level::Help, + max_line_num_len) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () } } } @@ -1211,6 +1248,30 @@ impl EmitterWriter { } } } + + fn render_macro_backtrace_old_school(&self, + sp: &Span, + buffer: &mut StyledBuffer) -> io::Result<()> { + if let Some(ref cm) = self.cm { + for trace in sp.macro_backtrace().iter().rev() { + let line_offset = buffer.num_lines(); + + let mut diag_string = + format!("in this expansion of {}", trace.macro_decl_name); + if let Some(def_site_span) = trace.def_site_span { + diag_string.push_str( + &format!(" (defined in {})", + cm.span_to_filename(def_site_span))); + } + let snippet = cm.span_to_string(trace.call_site); + buffer.append(line_offset, &format!("{} ", snippet), Style::NoStyle); + buffer.append(line_offset, "note", Style::Level(Level::Note)); + buffer.append(line_offset, ": ", Style::NoStyle); + buffer.append(line_offset, &diag_string, Style::OldSchoolNoteText); + } + } + Ok(()) + } } fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { @@ -1263,7 +1324,8 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { fn emit_to_destination(rendered_buffer: &Vec>, lvl: &Level, - dst: &mut Destination) + dst: &mut Destination, + short_message: bool) -> io::Result<()> { use lock; @@ -1286,7 +1348,9 @@ fn emit_to_destination(rendered_buffer: &Vec>, write!(dst, "{}", part.text)?; dst.reset_attrs()?; } - write!(dst, "\n")?; + if !short_message { + write!(dst, "\n")?; + } } dst.flush()?; Ok(()) @@ -1397,7 +1461,7 @@ impl Destination { } } Style::Quotation => {} - Style::HeaderMsg => { + Style::OldSchoolNoteText | Style::HeaderMsg => { self.start_attr(term::Attr::Bold)?; if cfg!(windows) { self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_WHITE))?; diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index d9b0f4ac8a6c0..605cfc5ed127e 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -18,10 +18,12 @@ #![feature(range_contains)] #![cfg_attr(unix, feature(libc))] #![feature(conservative_impl_trait)] +#![feature(i128_type)] extern crate term; #[cfg(unix)] extern crate libc; +extern crate rustc_data_structures; extern crate serialize as rustc_serialize; extern crate syntax_pos; @@ -31,6 +33,9 @@ use self::Level::*; use emitter::{Emitter, EmitterWriter}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::stable_hasher::StableHasher; + use std::borrow::Cow; use std::cell::{RefCell, Cell}; use std::mem; @@ -47,21 +52,7 @@ mod lock; use syntax_pos::{BytePos, Loc, FileLinesResult, FileMap, FileName, MultiSpan, Span, NO_EXPANSION}; -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] -pub enum RenderSpan { - /// A FullSpan renders with both with an initial line for the - /// message, prefixed by file:linenum, followed by a summary of - /// the source code covered by the span. - FullSpan(MultiSpan), - - /// A suggestion renders with both with an initial line for the - /// message, prefixed by file:linenum, followed by a summary - /// of hypothetical source code, where each `String` is spliced - /// into the lines in place of the code covered by each span. - Suggestion(CodeSuggestion), -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct CodeSuggestion { /// Each substitute can have multiple variants due to multiple /// applicable suggestions @@ -71,26 +62,34 @@ pub struct CodeSuggestion { /// /// ``` /// vec![ - /// (0..3, vec!["a", "x"]), - /// (4..7, vec!["b", "y"]), + /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, + /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, /// ] /// ``` /// /// or by replacing the entire span: /// /// ``` - /// vec![(0..7, vec!["a.b", "x.y"])] + /// vec![ + /// Substitution { parts: vec![(0..7, "a.b")] }, + /// Substitution { parts: vec![(0..7, "x.y")] }, + /// ] /// ``` - pub substitution_parts: Vec, + pub substitutions: Vec, pub msg: String, pub show_code_when_inline: bool, } -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] /// See the docs on `CodeSuggestion::substitutions` pub struct Substitution { + pub parts: Vec, +} + +#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +pub struct SubstitutionPart { pub span: Span, - pub substitutions: Vec, + pub snippet: String, } pub trait CodeMapper { @@ -104,18 +103,8 @@ pub trait CodeMapper { } impl CodeSuggestion { - /// Returns the number of substitutions - fn substitutions(&self) -> usize { - self.substitution_parts[0].substitutions.len() - } - - /// Returns the number of substitutions - fn substitution_spans<'a>(&'a self) -> impl Iterator + 'a { - self.substitution_parts.iter().map(|sub| sub.span) - } - - /// Returns the assembled code suggestions and wether they should be shown with an underline. - pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, bool)> { + /// Returns the assembled code suggestions and whether they should be shown with an underline. + pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, Vec)> { use syntax_pos::{CharPos, Loc, Pos}; fn push_trailing(buf: &mut String, @@ -137,60 +126,42 @@ impl CodeSuggestion { } } - if self.substitution_parts.is_empty() { - return vec![(String::new(), false)]; - } - - let mut primary_spans: Vec<_> = self.substitution_parts - .iter() - .map(|sub| (sub.span, &sub.substitutions)) - .collect(); - - // Assumption: all spans are in the same file, and all spans - // are disjoint. Sort in ascending order. - primary_spans.sort_by_key(|sp| sp.0.lo()); - - // Find the bounding span. - let lo = primary_spans.iter().map(|sp| sp.0.lo()).min().unwrap(); - let hi = primary_spans.iter().map(|sp| sp.0.hi()).min().unwrap(); - let bounding_span = Span::new(lo, hi, NO_EXPANSION); - let lines = cm.span_to_lines(bounding_span).unwrap(); - assert!(!lines.lines.is_empty()); - - // To build up the result, we do this for each span: - // - push the line segment trailing the previous span - // (at the beginning a "phantom" span pointing at the start of the line) - // - push lines between the previous and current span (if any) - // - if the previous and current span are not on the same line - // push the line segment leading up to the current span - // - splice in the span substitution - // - // Finally push the trailing line segment of the last span - let fm = &lines.file; - let mut prev_hi = cm.lookup_char_pos(bounding_span.lo()); - prev_hi.col = CharPos::from_usize(0); - - let mut prev_line = fm.get_line(lines.lines[0].line_index); - let mut bufs = vec![(String::new(), false); self.substitutions()]; - - for (sp, substitutes) in primary_spans { - let cur_lo = cm.lookup_char_pos(sp.lo()); - for (&mut (ref mut buf, ref mut underline), substitute) in bufs.iter_mut() - .zip(substitutes) { + assert!(!self.substitutions.is_empty()); + + self.substitutions.iter().cloned().map(|mut substitution| { + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + substitution.parts.sort_by_key(|part| part.span.lo()); + + // Find the bounding span. + let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap(); + let hi = substitution.parts.iter().map(|part| part.span.hi()).min().unwrap(); + let bounding_span = Span::new(lo, hi, NO_EXPANSION); + let lines = cm.span_to_lines(bounding_span).unwrap(); + assert!(!lines.lines.is_empty()); + + // To build up the result, we do this for each span: + // - push the line segment trailing the previous span + // (at the beginning a "phantom" span pointing at the start of the line) + // - push lines between the previous and current span (if any) + // - if the previous and current span are not on the same line + // push the line segment leading up to the current span + // - splice in the span substitution + // + // Finally push the trailing line segment of the last span + let fm = &lines.file; + let mut prev_hi = cm.lookup_char_pos(bounding_span.lo()); + prev_hi.col = CharPos::from_usize(0); + + let mut prev_line = fm.get_line(lines.lines[0].line_index); + let mut buf = String::new(); + + for part in &substitution.parts { + let cur_lo = cm.lookup_char_pos(part.span.lo()); if prev_hi.line == cur_lo.line { - push_trailing(buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); - - // Only show an underline in the suggestions if the suggestion is not the - // entirety of the code being shown and the displayed code is not multiline. - if prev_line.as_ref().unwrap().trim().len() > 0 - && !substitute.ends_with('\n') - && substitute.lines().count() == 1 - { - *underline = true; - } + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); } else { - *underline = false; - push_trailing(buf, prev_line.as_ref(), &prev_hi, None); + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); // push lines between the previous and current span (if any) for idx in prev_hi.line..(cur_lo.line - 1) { if let Some(line) = fm.get_line(idx) { @@ -202,22 +173,20 @@ impl CodeSuggestion { buf.push_str(&cur_line[..cur_lo.col.to_usize()]); } } - buf.push_str(substitute); + buf.push_str(&part.snippet); + prev_hi = cm.lookup_char_pos(part.span.hi()); + prev_line = fm.get_line(prev_hi.line - 1); } - prev_hi = cm.lookup_char_pos(sp.hi()); - prev_line = fm.get_line(prev_hi.line - 1); - } - for &mut (ref mut buf, _) in &mut bufs { // if the replacement already ends with a newline, don't print the next line if !buf.ends_with('\n') { - push_trailing(buf, prev_line.as_ref(), &prev_hi, None); + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); } // remove trailing newlines while buf.ends_with('\n') { buf.pop(); } - } - bufs + (buf, substitution.parts) + }).collect() } } @@ -257,20 +226,32 @@ impl error::Error for ExplicitBug { } } -pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString}; +pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId}; pub use diagnostic_builder::DiagnosticBuilder; /// A handler deals with errors; certain errors /// (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. pub struct Handler { + pub flags: HandlerFlags, + err_count: Cell, emitter: RefCell>, - pub can_emit_warnings: bool, - treat_err_as_bug: bool, continue_after_error: Cell, delayed_span_bug: RefCell>, tracked_diagnostics: RefCell>>, + + // This set contains a hash of every diagnostic that has been emitted by + // this handler. These hashes is used to avoid emitting the same error + // twice. + emitted_diagnostics: RefCell>, +} + +#[derive(Default)] +pub struct HandlerFlags { + pub can_emit_warnings: bool, + pub treat_err_as_bug: bool, + pub external_macro_backtrace: bool, } impl Handler { @@ -279,22 +260,46 @@ impl Handler { treat_err_as_bug: bool, cm: Option>) -> Handler { - let emitter = Box::new(EmitterWriter::stderr(color_config, cm)); - Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) + Handler::with_tty_emitter_and_flags( + color_config, + cm, + HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + .. Default::default() + }) + } + + pub fn with_tty_emitter_and_flags(color_config: ColorConfig, + cm: Option>, + flags: HandlerFlags) + -> Handler { + let emitter = Box::new(EmitterWriter::stderr(color_config, cm, false)); + Handler::with_emitter_and_flags(emitter, flags) } pub fn with_emitter(can_emit_warnings: bool, treat_err_as_bug: bool, e: Box) -> Handler { + Handler::with_emitter_and_flags( + e, + HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + .. Default::default() + }) + } + + pub fn with_emitter_and_flags(e: Box, flags: HandlerFlags) -> Handler { Handler { + flags, err_count: Cell::new(0), emitter: RefCell::new(e), - can_emit_warnings, - treat_err_as_bug, continue_after_error: Cell::new(true), delayed_span_bug: RefCell::new(None), tracked_diagnostics: RefCell::new(None), + emitted_diagnostics: RefCell::new(FxHashSet()), } } @@ -318,7 +323,7 @@ impl Handler { -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); - if !self.can_emit_warnings { + if !self.flags.can_emit_warnings { result.cancel(); } result @@ -326,19 +331,19 @@ impl Handler { pub fn struct_span_warn_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); - result.code(code.to_owned()); - if !self.can_emit_warnings { + result.code(code); + if !self.flags.can_emit_warnings { result.cancel(); } result } pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - if !self.can_emit_warnings { + if !self.flags.can_emit_warnings { result.cancel(); } result @@ -354,20 +359,24 @@ impl Handler { pub fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); - result.code(code.to_owned()); + result.code(code); result } // FIXME: This method should be removed (every error should have an associated error code). pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder::new(self, Level::Error, msg) } - pub fn struct_err_with_code<'a>(&'a self, msg: &str, code: &str) -> DiagnosticBuilder<'a> { + pub fn struct_err_with_code<'a>( + &'a self, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.code(code.to_owned()); + result.code(code); result } pub fn struct_span_fatal<'a, S: Into>(&'a self, @@ -381,11 +390,11 @@ impl Handler { pub fn struct_span_fatal_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); result.set_span(sp); - result.code(code.to_owned()); + result.code(code); result } pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { @@ -397,7 +406,7 @@ impl Handler { } fn panic_if_treat_err_as_bug(&self) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { panic!("encountered error with `-Z treat_err_as_bug"); } } @@ -409,7 +418,7 @@ impl Handler { pub fn span_fatal_with_code>(&self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> FatalError { self.emit_with_code(&sp.into(), msg, code, Fatal); FatalError @@ -425,13 +434,13 @@ impl Handler { result.set_span(sp); result } - pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { self.emit_with_code(&sp.into(), msg, code, Error); } pub fn span_warn>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Warning); } - pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: &str) { + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { self.emit_with_code(&sp.into(), msg, code, Warning); } pub fn span_bug>(&self, sp: S, msg: &str) -> ! { @@ -439,7 +448,7 @@ impl Handler { panic!(ExplicitBug); } pub fn delay_span_bug>(&self, sp: S, msg: &str) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.span_bug(sp, msg); } let mut diagnostic = Diagnostic::new(Level::Bug, msg); @@ -464,7 +473,7 @@ impl Handler { self.span_bug(sp, &format!("unimplemented {}", msg)); } pub fn fatal(&self, msg: &str) -> FatalError { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.bug(msg); } let mut db = DiagnosticBuilder::new(self, Fatal, msg); @@ -472,7 +481,7 @@ impl Handler { FatalError } pub fn err(&self, msg: &str) { - if self.treat_err_as_bug { + if self.flags.treat_err_as_bug { self.bug(msg); } let mut db = DiagnosticBuilder::new(self, Error, msg); @@ -525,7 +534,7 @@ impl Handler { panic!(self.fatal(&s)); } pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { + if lvl == Warning && !self.flags.can_emit_warnings { return; } let mut db = DiagnosticBuilder::new(self, lvl, msg); @@ -535,11 +544,11 @@ impl Handler { self.abort_if_errors(); } } - pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: &str, lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { + pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: DiagnosticId, lvl: Level) { + if lvl == Warning && !self.flags.can_emit_warnings { return; } - let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code.to_owned()), msg); + let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code), msg); db.set_span(msp.clone()); db.emit(); if !self.continue_after_error.get() { @@ -559,15 +568,29 @@ impl Handler { } fn emit_db(&self, db: &DiagnosticBuilder) { + let diagnostic = &**db; + if let Some(ref mut list) = *self.tracked_diagnostics.borrow_mut() { - list.push((**db).clone()); + list.push(diagnostic.clone()); + } + + let diagnostic_hash = { + use std::hash::Hash; + let mut hasher = StableHasher::new(); + diagnostic.hash(&mut hasher); + hasher.finish() + }; + + // Only emit the diagnostic if we haven't already emitted an equivalent + // one: + if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) { + self.emitter.borrow_mut().emit(db); } - self.emitter.borrow_mut().emit(db); } } -#[derive(Copy, PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum Level { Bug, Fatal, diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 52e3fcc1b474c..c2f4701999ea9 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -70,7 +70,7 @@ impl MultilineAnnotation { pub fn as_end(&self) -> Annotation { Annotation { - start_col: self.end_col - 1, + start_col: self.end_col.saturating_sub(1), end_col: self.end_col, is_primary: self.is_primary, label: self.label.clone(), @@ -203,7 +203,7 @@ pub struct StyledString { pub style: Style, } -#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub enum Style { HeaderMsg, LineAndColumn, @@ -213,6 +213,7 @@ pub enum Style { UnderlineSecondary, LabelPrimary, LabelSecondary, + OldSchoolNoteText, NoStyle, Level(Level), Highlight, diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index 7bf2efa4b885f..b40a1b7c0cb7d 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -10,9 +10,10 @@ crate-type = ["dylib"] [dependencies] graphviz = { path = "../libgraphviz" } +log = "0.3" +rand = "0.3" rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } serialize = { path = "../libserialize" } -log = "0.3" syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index 1d58d17996090..acbd3e0d63dde 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -209,7 +209,7 @@ fn check_paths<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } let query = tcx.dep_graph.query(); for &(_, source_def_id, ref source_dep_node) in if_this_changed { - let dependents = query.transitive_successors(source_dep_node); + let dependents = query.transitive_predecessors(source_dep_node); for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { if !dependents.contains(&target_dep_node) { tcx.sess.span_err( diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 0fba6d8e9c67d..5eaf8553ee3d3 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -15,13 +15,16 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] -#![feature(rand)] #![feature(conservative_impl_trait)] +#![feature(i128_type)] +#![feature(inclusive_range_syntax)] +#![feature(specialization)] extern crate graphviz; #[macro_use] extern crate rustc; extern crate rustc_data_structures; extern crate serialize as rustc_serialize; +extern crate rand; #[macro_use] extern crate log; extern crate syntax; @@ -31,11 +34,13 @@ mod assert_dep_graph; mod persist; pub use assert_dep_graph::assert_dep_graph; +pub use persist::dep_graph_tcx_init; pub use persist::load_dep_graph; -pub use persist::load_dep_graph_new; +pub use persist::load_query_result_cache; pub use persist::save_dep_graph; pub use persist::save_trans_partition; pub use persist::save_work_products; pub use persist::in_incr_comp_dir; pub use persist::prepare_session_directory; pub use persist::finalize_session_directory; +pub use persist::delete_workproduct_files; diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs index 06acfb5d77807..d7d142aac75cc 100644 --- a/src/librustc_incremental/persist/data.rs +++ b/src/librustc_incremental/persist/data.rs @@ -10,86 +10,7 @@ //! The data that we will serialize and deserialize. -use rustc::dep_graph::{DepNode, WorkProduct, WorkProductId}; -use rustc::hir::def_id::DefIndex; -use rustc::hir::map::DefPathHash; -use rustc::ich::Fingerprint; -use rustc::middle::cstore::EncodedMetadataHash; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; - -/// Data for use when recompiling the **current crate**. -#[derive(Debug, RustcEncodable, RustcDecodable)] -pub struct SerializedDepGraph { - /// The set of all DepNodes in the graph - pub nodes: IndexVec, - /// For each DepNode, stores the list of edges originating from that - /// DepNode. Encoded as a [start, end) pair indexing into edge_list_data, - /// which holds the actual DepNodeIndices of the target nodes. - pub edge_list_indices: IndexVec, - /// A flattened list of all edge targets in the graph. Edge sources are - /// implicit in edge_list_indices. - pub edge_list_data: Vec, - - /// These are output nodes that have no incoming edges. We track - /// these separately so that when we reload all edges, we don't - /// lose track of these nodes. - pub bootstrap_outputs: Vec, - - /// These are hashes of two things: - /// - the HIR nodes in this crate - /// - the metadata nodes from dependent crates we use - /// - /// In each case, we store a hash summarizing the contents of - /// those items as they were at the time we did this compilation. - /// In the case of HIR nodes, this hash is derived by walking the - /// HIR itself. In the case of metadata nodes, the hash is loaded - /// from saved state. - /// - /// When we do the next compile, we will load these back up and - /// compare them against the hashes we see at that time, which - /// will tell us what has changed, either in this crate or in some - /// crate that we depend on. - /// - /// Because they will be reloaded, we don't store the DefId (which - /// will be different when we next compile) related to each node, - /// but rather the `DefPathIndex`. This can then be retraced - /// to find the current def-id. - pub hashes: Vec<(DepNodeIndex, Fingerprint)>, -} - -impl SerializedDepGraph { - pub fn edge_targets_from(&self, source: DepNodeIndex) -> &[DepNodeIndex] { - let targets = self.edge_list_indices[source]; - &self.edge_list_data[targets.0 as usize .. targets.1 as usize] - } -} - -/// The index of a DepNode in the SerializedDepGraph::nodes array. -#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug, - RustcEncodable, RustcDecodable)] -pub struct DepNodeIndex(pub u32); - -impl DepNodeIndex { - #[inline] - pub fn new(idx: usize) -> DepNodeIndex { - assert!(idx <= ::std::u32::MAX as usize); - DepNodeIndex(idx as u32) - } -} - -impl Idx for DepNodeIndex { - #[inline] - fn new(idx: usize) -> Self { - assert!(idx <= ::std::u32::MAX as usize); - DepNodeIndex(idx as u32) - } - - #[inline] - fn index(self) -> usize { - self.0 as usize - } -} +use rustc::dep_graph::{WorkProduct, WorkProductId}; #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct SerializedWorkProduct { @@ -99,39 +20,3 @@ pub struct SerializedWorkProduct { /// work-product data itself pub work_product: WorkProduct, } - -/// Data for use when downstream crates get recompiled. -#[derive(Debug, RustcEncodable, RustcDecodable)] -pub struct SerializedMetadataHashes { - /// For each def-id defined in this crate that appears in the - /// metadata, we hash all the inputs that were used when producing - /// the metadata. We save this after compilation is done. Then, - /// when some downstream crate is being recompiled, it can compare - /// the hashes we saved against the hashes that it saw from - /// before; this will tell it which of the items in this crate - /// changed, which in turn implies what items in the downstream - /// crate need to be recompiled. - /// - /// Note that we store the def-ids here. This is because we don't - /// reload this file when we recompile this crate, we will just - /// regenerate it completely with the current hashes and new def-ids. - /// - /// Then downstream creates will load up their - /// `SerializedDepGraph`, which may contain `MetaData(X)` nodes - /// where `X` refers to some item in this crate. That `X` will be - /// a `DefPathIndex` that gets retracted to the current `DefId` - /// (matching the one found in this structure). - pub entry_hashes: Vec, - - /// For each DefIndex (as it occurs in SerializedMetadataHash), this - /// map stores the DefPathIndex (as it occurs in DefIdDirectory), so - /// that we can find the new DefId for a SerializedMetadataHash in a - /// subsequent compilation session. - /// - /// This map is only needed for running auto-tests using the - /// #[rustc_metadata_dirty] and #[rustc_metadata_clean] attributes, and - /// is only populated if -Z query-dep-graph is specified. It will be - /// empty otherwise. Importing crates are perfectly happy with just having - /// the DefIndex. - pub index_map: FxHashMap -} diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index a6d39a918631c..7c3f903f22846 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -13,46 +13,209 @@ //! we will compare the fingerprint from the current and from the previous //! compilation session as appropriate: //! -//! - `#[rustc_dirty(label="TypeckTables", cfg="rev2")]` if we are +//! - `#[rustc_clean(cfg="rev2", except="TypeckTables")]` if we are //! in `#[cfg(rev2)]`, then the fingerprints associated with //! `DepNode::TypeckTables(X)` must be DIFFERENT (`X` is the def-id of the //! current node). -//! - `#[rustc_clean(label="TypeckTables", cfg="rev2")]` same as above, -//! except that the fingerprints must be the SAME. +//! - `#[rustc_clean(cfg="rev2")]` same as above, except that the +//! fingerprints must be the SAME (along with all other fingerprints). //! //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. //! -//! The `#[rustc_metadata_dirty]` and `#[rustc_metadata_clean]` attributes -//! can be used to check the incremental compilation hash (ICH) values of -//! metadata exported in rlibs. -//! -//! - If a node is marked with `#[rustc_metadata_clean(cfg="rev2")]` we -//! check that the metadata hash for that node is the same for "rev2" -//! it was for "rev1". -//! - If a node is marked with `#[rustc_metadata_dirty(cfg="rev2")]` we -//! check that the metadata hash for that node is *different* for "rev2" -//! than it was for "rev1". -//! -//! Note that the metadata-testing attributes must never specify the -//! first revision. This would lead to a crash since there is no -//! previous revision to compare things to. -//! -use rustc::dep_graph::DepNode; +use std::collections::HashSet; +use std::iter::FromIterator; +use std::vec::Vec; +use rustc::dep_graph::{DepNode, label_strs}; use rustc::hir; +use rustc::hir::{Item_ as HirItem, ImplItemKind, TraitItemKind}; +use rustc::hir::map::Node as HirNode; use rustc::hir::def_id::DefId; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::intravisit; -use rustc::ich::{Fingerprint, ATTR_DIRTY, ATTR_CLEAN, ATTR_DIRTY_METADATA, - ATTR_CLEAN_METADATA}; +use rustc::ich::{ATTR_DIRTY, ATTR_CLEAN}; use syntax::ast::{self, Attribute, NestedMetaItem}; -use rustc_data_structures::fx::{FxHashSet, FxHashMap}; +use rustc_data_structures::fx::FxHashSet; use syntax_pos::Span; use rustc::ty::TyCtxt; -const LABEL: &'static str = "label"; -const CFG: &'static str = "cfg"; +const EXCEPT: &str = "except"; +const LABEL: &str = "label"; +const CFG: &str = "cfg"; + +// Base and Extra labels to build up the labels + +/// For typedef, constants, and statics +const BASE_CONST: &[&str] = &[ + label_strs::TypeOfItem, +]; + +/// DepNodes for functions + methods +const BASE_FN: &[&str] = &[ + // Callers will depend on the signature of these items, so we better test + label_strs::FnSignature, + label_strs::GenericsOfItem, + label_strs::PredicatesOfItem, + label_strs::TypeOfItem, + + // And a big part of compilation (that we eventually want to cache) is type inference + // information: + label_strs::TypeckTables, +]; + +/// DepNodes for Hir, which is pretty much everything +const BASE_HIR: &[&str] = &[ + // Hir and HirBody should be computed for all nodes + label_strs::Hir, + label_strs::HirBody, +]; + +/// `impl` implementation of struct/trait +const BASE_IMPL: &[&str] = &[ + label_strs::AssociatedItemDefIds, + label_strs::GenericsOfItem, + label_strs::ImplTraitRef, +]; + +/// DepNodes for MirValidated/Optimized, which is relevant in "executable" +/// code, i.e. functions+methods +const BASE_MIR: &[&str] = &[ + label_strs::MirOptimized, + label_strs::MirValidated, +]; + +/// Struct, Enum and Union DepNodes +/// +/// Note that changing the type of a field does not change the type of the struct or enum, but +/// adding/removing fields or changing a fields name or visibility does. +const BASE_STRUCT: &[&str] = &[ + label_strs::GenericsOfItem, + label_strs::PredicatesOfItem, + label_strs::TypeOfItem, +]; + +/// Trait Definition DepNodes +const BASE_TRAIT_DEF: &[&str] = &[ + label_strs::AssociatedItemDefIds, + label_strs::GenericsOfItem, + label_strs::ObjectSafety, + label_strs::PredicatesOfItem, + label_strs::SpecializationGraph, + label_strs::TraitDefOfItem, + label_strs::TraitImpls, +]; + +/// extra DepNodes for methods (+fn) +const EXTRA_ASSOCIATED: &[&str] = &[ + label_strs::AssociatedItems, +]; + +const EXTRA_TRAIT: &[&str] = &[ + label_strs::TraitOfItem, +]; + +// Fully Built Labels + +const LABELS_CONST: &[&[&str]] = &[ + BASE_HIR, + BASE_CONST, +]; + +/// Constant/Typedef in an impl +const LABELS_CONST_IN_IMPL: &[&[&str]] = &[ + BASE_HIR, + BASE_CONST, + EXTRA_ASSOCIATED, +]; + +/// Trait-Const/Typedef DepNodes +const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[ + BASE_HIR, + BASE_CONST, + EXTRA_ASSOCIATED, + EXTRA_TRAIT, +]; + +/// Function DepNode +const LABELS_FN: &[&[&str]] = &[ + BASE_HIR, + BASE_MIR, + BASE_FN, +]; + +/// Method DepNodes +const LABELS_FN_IN_IMPL: &[&[&str]] = &[ + BASE_HIR, + BASE_MIR, + BASE_FN, + EXTRA_ASSOCIATED, +]; + +/// Trait-Method DepNodes +const LABELS_FN_IN_TRAIT: &[&[&str]] = &[ + BASE_HIR, + BASE_MIR, + BASE_FN, + EXTRA_ASSOCIATED, + EXTRA_TRAIT, +]; + +/// For generic cases like inline-assemply/mod/etc +const LABELS_HIR_ONLY: &[&[&str]] = &[ + BASE_HIR, +]; + +/// Impl DepNodes +const LABELS_IMPL: &[&[&str]] = &[ + BASE_HIR, + BASE_IMPL, +]; + +/// Abstract Data Type (Struct, Enum, Unions) DepNodes +const LABELS_ADT: &[&[&str]] = &[ + BASE_HIR, + BASE_STRUCT, +]; + +/// Trait Definition DepNodes +#[allow(dead_code)] +const LABELS_TRAIT: &[&[&str]] = &[ + BASE_HIR, + BASE_TRAIT_DEF, +]; + + +// FIXME: Struct/Enum/Unions Fields (there is currently no way to attach these) +// +// Fields are kind of separate from their containers, as they can change independently from +// them. We should at least check +// +// TypeOfItem for these. + +type Labels = HashSet; + +/// Represents the requested configuration by rustc_clean/dirty +struct Assertion { + clean: Labels, + dirty: Labels, +} + +impl Assertion { + fn from_clean_labels(labels: Labels) -> Assertion { + Assertion { + clean: labels, + dirty: Labels::new(), + } + } + + fn from_dirty_labels(labels: Labels) -> Assertion { + Assertion { + clean: Labels::new(), + dirty: labels, + } + } +} pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // can't add `#[rustc_dirty]` etc without opting in to this feature @@ -87,23 +250,221 @@ pub struct DirtyCleanVisitor<'a, 'tcx:'a> { } impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { - fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { - let def_path_hash = self.tcx.def_path_hash(def_id); + + /// Possibly "deserialize" the attribute into a clean/dirty assertion + fn assertion_maybe(&mut self, item_id: ast::NodeId, attr: &Attribute) + -> Option + { + let is_clean = if attr.check_name(ATTR_DIRTY) { + false + } else if attr.check_name(ATTR_CLEAN) { + true + } else { + // skip: not rustc_clean/dirty + return None + }; + if !check_config(self.tcx, attr) { + // skip: not the correct `cfg=` + return None; + } + let assertion = if let Some(labels) = self.labels(attr) { + if is_clean { + Assertion::from_clean_labels(labels) + } else { + Assertion::from_dirty_labels(labels) + } + } else { + self.assertion_auto(item_id, attr, is_clean) + }; + Some(assertion) + } + + /// Get the "auto" assertion on pre-validated attr, along with the `except` labels + fn assertion_auto(&mut self, item_id: ast::NodeId, attr: &Attribute, is_clean: bool) + -> Assertion + { + let (name, mut auto) = self.auto_labels(item_id, attr); + let except = self.except(attr); + for e in except.iter() { + if !auto.remove(e) { + let msg = format!( + "`except` specified DepNodes that can not be affected for \"{}\": \"{}\"", + name, + e + ); + self.tcx.sess.span_fatal(attr.span, &msg); + } + } + if is_clean { + Assertion { + clean: auto, + dirty: except, + } + } else { + Assertion { + clean: except, + dirty: auto, + } + } + } + + fn labels(&self, attr: &Attribute) -> Option { for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(LABEL) { let value = expect_associated_value(self.tcx, &item); - match DepNode::from_label_string(&value.as_str(), def_path_hash) { - Ok(dep_node) => return dep_node, - Err(()) => { - self.tcx.sess.span_fatal( - item.span, - &format!("dep-node label `{}` not recognized", value)); - } + return Some(self.resolve_labels(&item, value.as_str().as_ref())); + } + } + None + } + + /// `except=` attribute value + fn except(&self, attr: &Attribute) -> Labels { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { + if item.check_name(EXCEPT) { + let value = expect_associated_value(self.tcx, &item); + return self.resolve_labels(&item, value.as_str().as_ref()); + } + } + // if no `label` or `except` is given, only the node's group are asserted + Labels::new() + } + + /// Return all DepNode labels that should be asserted for this item. + /// index=0 is the "name" used for error messages + fn auto_labels(&mut self, item_id: ast::NodeId, attr: &Attribute) -> (&'static str, Labels) { + let node = self.tcx.hir.get(item_id); + let (name, labels) = match node { + HirNode::NodeItem(item) => { + match item.node { + // note: these are in the same order as hir::Item_; + // FIXME(michaelwoerister): do commented out ones + + // // An `extern crate` item, with optional original crate name, + // HirItem::ItemExternCrate(..), // intentionally no assertions + + // // `use foo::bar::*;` or `use foo::bar::baz as quux;` + // HirItem::ItemUse(..), // intentionally no assertions + + // A `static` item + HirItem::ItemStatic(..) => ("ItemStatic", LABELS_CONST), + + // A `const` item + HirItem::ItemConst(..) => ("ItemConst", LABELS_CONST), + + // A function declaration + HirItem::ItemFn(..) => ("ItemFn", LABELS_FN), + + // // A module + HirItem::ItemMod(..) =>("ItemMod", LABELS_HIR_ONLY), + + // // An external module + HirItem::ItemForeignMod(..) => ("ItemForeignMod", LABELS_HIR_ONLY), + + // Module-level inline assembly (from global_asm!) + HirItem::ItemGlobalAsm(..) => ("ItemGlobalAsm", LABELS_HIR_ONLY), + + // A type alias, e.g. `type Foo = Bar` + HirItem::ItemTy(..) => ("ItemTy", LABELS_HIR_ONLY), + + // An enum definition, e.g. `enum Foo {C, D}` + HirItem::ItemEnum(..) => ("ItemEnum", LABELS_ADT), + + // A struct definition, e.g. `struct Foo {x: A}` + HirItem::ItemStruct(..) => ("ItemStruct", LABELS_ADT), + + // A union definition, e.g. `union Foo {x: A, y: B}` + HirItem::ItemUnion(..) => ("ItemUnion", LABELS_ADT), + + // Represents a Trait Declaration + // FIXME(michaelwoerister): trait declaration is buggy because sometimes some of + // the depnodes don't exist (because they legitametely didn't need to be + // calculated) + // + // michaelwoerister and vitiral came up with a possible solution, + // to just do this before every query + // ``` + // ::rustc::ty::maps::plumbing::force_from_dep_node(tcx, dep_node) + // ``` + // + // However, this did not seem to work effectively and more bugs were hit. + // Nebie @vitiral gave up :) + // + //HirItem::ItemTrait(..) => ("ItemTrait", LABELS_TRAIT), + + // `impl Trait for .. {}` + HirItem::ItemAutoImpl(..) => ("ItemAutoImpl", LABELS_IMPL), + + // An implementation, eg `impl Trait for Foo { .. }` + HirItem::ItemImpl(..) => ("ItemImpl", LABELS_IMPL), + + _ => self.tcx.sess.span_fatal( + attr.span, + &format!( + "clean/dirty auto-assertions not yet defined for NodeItem.node={:?}", + item.node + ) + ), + } + }, + HirNode::NodeTraitItem(item) => { + match item.node { + TraitItemKind::Method(..) => ("NodeTraitItem", LABELS_FN_IN_TRAIT), + TraitItemKind::Const(..) => ("NodeTraitConst", LABELS_CONST_IN_TRAIT), + TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT), } + }, + HirNode::NodeImplItem(item) => { + match item.node { + ImplItemKind::Method(..) => ("NodeImplItem", LABELS_FN_IN_IMPL), + ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), + ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), + } + }, + _ => self.tcx.sess.span_fatal( + attr.span, + &format!( + "clean/dirty auto-assertions not yet defined for {:?}", + node + ) + ), + }; + let labels = Labels::from_iter( + labels.iter().flat_map(|s| s.iter().map(|l| l.to_string())) + ); + (name, labels) + } + + fn resolve_labels(&self, item: &NestedMetaItem, value: &str) -> Labels { + let mut out: Labels = HashSet::new(); + for label in value.split(',') { + let label = label.trim(); + if DepNode::has_label_string(label) { + if out.contains(label) { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` is repeated", label)); + } + out.insert(label.to_string()); + } else { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` not recognized", label)); } } + out + } - self.tcx.sess.span_fatal(attr.span, "no `label` found"); + fn dep_nodes(&self, labels: &Labels, def_id: DefId) -> Vec { + let mut out = Vec::with_capacity(labels.len()); + let def_path_hash = self.tcx.def_path_hash(def_id); + for label in labels.iter() { + match DepNode::from_label_string(label, def_path_hash) { + Ok(dep_node) => out.push(dep_node), + Err(()) => unreachable!(), + } + } + out } fn dep_node_str(&self, dep_node: &DepNode) -> String { @@ -122,7 +483,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { let current_fingerprint = self.tcx.dep_graph.fingerprint_of(&dep_node); let prev_fingerprint = self.tcx.dep_graph.prev_fingerprint_of(&dep_node); - if current_fingerprint == prev_fingerprint { + if Some(current_fingerprint) == prev_fingerprint { let dep_node_str = self.dep_node_str(&dep_node); self.tcx.sess.span_err( item_span, @@ -136,7 +497,7 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { let current_fingerprint = self.tcx.dep_graph.fingerprint_of(&dep_node); let prev_fingerprint = self.tcx.dep_graph.prev_fingerprint_of(&dep_node); - if current_fingerprint != prev_fingerprint { + if Some(current_fingerprint) != prev_fingerprint { let dep_node_str = self.dep_node_str(&dep_node); self.tcx.sess.span_err( item_span, @@ -147,16 +508,16 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { fn check_item(&mut self, item_id: ast::NodeId, item_span: Span) { let def_id = self.tcx.hir.local_def_id(item_id); for attr in self.tcx.get_attrs(def_id).iter() { - if attr.check_name(ATTR_DIRTY) { - if check_config(self.tcx, attr) { - self.checked_attrs.insert(attr.id); - self.assert_dirty(item_span, self.dep_node(attr, def_id)); - } - } else if attr.check_name(ATTR_CLEAN) { - if check_config(self.tcx, attr) { - self.checked_attrs.insert(attr.id); - self.assert_clean(item_span, self.dep_node(attr, def_id)); - } + let assertion = match self.assertion_maybe(item_id, attr) { + Some(a) => a, + None => continue, + }; + self.checked_attrs.insert(attr.id); + for dep_node in self.dep_nodes(&assertion.clean, def_id) { + self.assert_clean(item_span, dep_node); + } + for dep_node in self.dep_nodes(&assertion.dirty, def_id) { + self.assert_dirty(item_span, dep_node); } } } @@ -176,175 +537,45 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> { } } -pub fn check_dirty_clean_metadata<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - prev_metadata_hashes: &FxHashMap, - current_metadata_hashes: &FxHashMap) -{ - if !tcx.sess.opts.debugging_opts.query_dep_graph { - return; - } - - tcx.dep_graph.with_ignore(||{ - let krate = tcx.hir.krate(); - let mut dirty_clean_visitor = DirtyCleanMetadataVisitor { - tcx, - prev_metadata_hashes, - current_metadata_hashes, - checked_attrs: FxHashSet(), - }; - intravisit::walk_crate(&mut dirty_clean_visitor, krate); - - let mut all_attrs = FindAllAttrs { - tcx, - attr_names: vec![ATTR_DIRTY_METADATA, ATTR_CLEAN_METADATA], - found_attrs: vec![], - }; - intravisit::walk_crate(&mut all_attrs, krate); - - // Note that we cannot use the existing "unused attribute"-infrastructure - // here, since that is running before trans. This is also the reason why - // all trans-specific attributes are `Whitelisted` in syntax::feature_gate. - all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs); - }); -} - -pub struct DirtyCleanMetadataVisitor<'a, 'tcx: 'a, 'm> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - prev_metadata_hashes: &'m FxHashMap, - current_metadata_hashes: &'m FxHashMap, - checked_attrs: FxHashSet, -} - -impl<'a, 'tcx, 'm> intravisit::Visitor<'tcx> for DirtyCleanMetadataVisitor<'a, 'tcx, 'm> { - - fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> { - intravisit::NestedVisitorMap::All(&self.tcx.hir) - } - - fn visit_item(&mut self, item: &'tcx hir::Item) { - self.check_item(item.id, item.span); - intravisit::walk_item(self, item); - } - - fn visit_variant(&mut self, - variant: &'tcx hir::Variant, - generics: &'tcx hir::Generics, - parent_id: ast::NodeId) { - if let Some(e) = variant.node.disr_expr { - self.check_item(e.node_id, variant.span); - } - - intravisit::walk_variant(self, variant, generics, parent_id); - } - - fn visit_variant_data(&mut self, - variant_data: &'tcx hir::VariantData, - _: ast::Name, - _: &'tcx hir::Generics, - _parent_id: ast::NodeId, - span: Span) { - if self.tcx.hir.find(variant_data.id()).is_some() { - // VariantData that represent structs or tuples don't have a - // separate entry in the HIR map and checking them would error, - // so only check if this is an enum or union variant. - self.check_item(variant_data.id(), span); - } - - intravisit::walk_struct_def(self, variant_data); - } - - fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) { - self.check_item(item.id, item.span); - intravisit::walk_trait_item(self, item); - } - - fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) { - self.check_item(item.id, item.span); - intravisit::walk_impl_item(self, item); - } - - fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem) { - self.check_item(i.id, i.span); - intravisit::walk_foreign_item(self, i); - } - - fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { - self.check_item(s.id, s.span); - intravisit::walk_struct_field(self, s); - } -} - -impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> { - - fn check_item(&mut self, item_id: ast::NodeId, item_span: Span) { - let def_id = self.tcx.hir.local_def_id(item_id); - - for attr in self.tcx.get_attrs(def_id).iter() { - if attr.check_name(ATTR_DIRTY_METADATA) { - if check_config(self.tcx, attr) { - if self.checked_attrs.insert(attr.id) { - self.assert_state(false, def_id, item_span); - } - } - } else if attr.check_name(ATTR_CLEAN_METADATA) { - if check_config(self.tcx, attr) { - if self.checked_attrs.insert(attr.id) { - self.assert_state(true, def_id, item_span); - } - } - } - } - } - - fn assert_state(&self, should_be_clean: bool, def_id: DefId, span: Span) { - let item_path = self.tcx.item_path_str(def_id); - debug!("assert_state({})", item_path); - - if let Some(&prev_hash) = self.prev_metadata_hashes.get(&def_id) { - let hashes_are_equal = prev_hash == self.current_metadata_hashes[&def_id]; - - if should_be_clean && !hashes_are_equal { - self.tcx.sess.span_err( - span, - &format!("Metadata hash of `{}` is dirty, but should be clean", - item_path)); - } - - let should_be_dirty = !should_be_clean; - if should_be_dirty && hashes_are_equal { - self.tcx.sess.span_err( - span, - &format!("Metadata hash of `{}` is clean, but should be dirty", - item_path)); - } - } else { - self.tcx.sess.span_err( - span, - &format!("Could not find previous metadata hash of `{}`", - item_path)); - } - } -} - /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan /// for a `cfg="foo"` attribute and check whether we have a cfg /// flag called `foo`. +/// +/// Also make sure that the `label` and `except` fields do not +/// both exist. fn check_config(tcx: TyCtxt, attr: &Attribute) -> bool { debug!("check_config(attr={:?})", attr); let config = &tcx.sess.parse_sess.config; debug!("check_config: config={:?}", config); + let (mut cfg, mut except, mut label) = (None, false, false); for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(CFG) { let value = expect_associated_value(tcx, &item); debug!("check_config: searching for cfg {:?}", value); - return config.contains(&(value, None)); + cfg = Some(config.contains(&(value, None))); + } + if item.check_name(LABEL) { + label = true; + } + if item.check_name(EXCEPT) { + except = true; } } - tcx.sess.span_fatal( - attr.span, - "no cfg attribute"); + if label && except { + tcx.sess.span_fatal( + attr.span, + "must specify only one of: `label`, `except`" + ); + } + + match cfg { + None => tcx.sess.span_fatal( + attr.span, + "no cfg attribute" + ), + Some(c) => c, + } } fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> ast::Name { @@ -361,7 +592,6 @@ fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> ast::Name { } } - // A visitor that collects all #[rustc_dirty]/#[rustc_clean] attributes from // the HIR. It is used to verfiy that we really ran checks for all annotated // nodes. diff --git a/src/librustc_incremental/persist/file_format.rs b/src/librustc_incremental/persist/file_format.rs index 13b019af2eaa1..7d27b842a68a7 100644 --- a/src/librustc_incremental/persist/file_format.rs +++ b/src/librustc_incremental/persist/file_format.rs @@ -53,19 +53,25 @@ pub fn write_file_header(stream: &mut W) -> io::Result<()> { /// Reads the contents of a file with a file header as defined in this module. /// -/// - Returns `Ok(Some(data))` if the file existed and was generated by a +/// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a /// compatible compiler version. `data` is the entire contents of the file -/// *after* the header. +/// and `pos` points to the first byte after the header. /// - Returns `Ok(None)` if the file did not exist or was generated by an /// incompatible version of the compiler. /// - Returns `Err(..)` if some kind of IO error occurred while reading the /// file. -pub fn read_file(sess: &Session, path: &Path) -> io::Result>> { +pub fn read_file(sess: &Session, path: &Path) -> io::Result, usize)>> { if !path.exists() { return Ok(None); } let mut file = File::open(path)?; + let file_size = file.metadata()?.len() as usize; + + let mut data = Vec::with_capacity(file_size); + file.read_to_end(&mut data)?; + + let mut file = io::Cursor::new(data); // Check FILE_MAGIC { @@ -107,17 +113,15 @@ pub fn read_file(sess: &Session, path: &Path) -> io::Result>> { } } - let mut data = vec![]; - file.read_to_end(&mut data)?; - - Ok(Some(data)) + let post_header_start_pos = file.position() as usize; + Ok(Some((file.into_inner(), post_header_start_pos))) } fn report_format_mismatch(sess: &Session, file: &Path, message: &str) { debug!("read_file: {}", message); if sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: ignoring cache artifact `{}`: {}", + println!("[incremental] ignoring cache artifact `{}`: {}", file.file_name().unwrap().to_string_lossy(), message); } diff --git a/src/librustc_incremental/persist/fs.rs b/src/librustc_incremental/persist/fs.rs index 592b8f1a9eb20..2a8cfb7e91d71 100644 --- a/src/librustc_incremental/persist/fs.rs +++ b/src/librustc_incremental/persist/fs.rs @@ -115,7 +115,7 @@ //! implemented. use rustc::hir::svh::Svh; -use rustc::session::Session; +use rustc::session::{Session, CrateDisambiguator}; use rustc::util::fs as fs_util; use rustc_data_structures::{flock, base_n}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; @@ -125,13 +125,13 @@ use std::io; use std::mem; use std::path::{Path, PathBuf}; use std::time::{UNIX_EPOCH, SystemTime, Duration}; -use std::__rand::{thread_rng, Rng}; + +use rand::{thread_rng, Rng}; const LOCK_FILE_EXT: &'static str = ".lock"; const DEP_GRAPH_FILENAME: &'static str = "dep-graph.bin"; -const DEP_GRAPH_NEW_FILENAME: &'static str = "dep-graph-new.bin"; const WORK_PRODUCTS_FILENAME: &'static str = "work-products.bin"; -const METADATA_HASHES_FILENAME: &'static str = "metadata.bin"; +const QUERY_CACHE_FILENAME: &'static str = "query-cache.bin"; // We encode integers using the following base, so they are shorter than decimal // or hexadecimal numbers (we want short file and directory names). Since these @@ -143,16 +143,12 @@ pub fn dep_graph_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, DEP_GRAPH_FILENAME) } -pub fn dep_graph_path_new(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, DEP_GRAPH_NEW_FILENAME) -} - pub fn work_products_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME) } -pub fn metadata_hash_export_path(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, METADATA_HASHES_FILENAME) +pub fn query_cache_path(sess: &Session) -> PathBuf { + in_incr_comp_dir_sess(sess, QUERY_CACHE_FILENAME) } pub fn lock_file_path(session_dir: &Path) -> PathBuf { @@ -193,7 +189,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. pub fn prepare_session_directory(sess: &Session, crate_name: &str, - crate_disambiguator: &str) { + crate_disambiguator: CrateDisambiguator) { if sess.opts.incremental.is_none() { return } @@ -261,11 +257,12 @@ pub fn prepare_session_directory(sess: &Session, debug!("attempting to copy data from source: {}", source_directory.display()); - let print_file_copy_stats = sess.opts.debugging_opts.incremental_info; + // Try copying over all files from the source directory - if let Ok(allows_links) = copy_files(&session_dir, &source_directory, - print_file_copy_stats) { + if let Ok(allows_links) = copy_files(sess, + &session_dir, + &source_directory) { debug!("successfully copied data from: {}", source_directory.display()); @@ -395,9 +392,9 @@ pub fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> { Ok(()) } -fn copy_files(target_dir: &Path, - source_dir: &Path, - print_stats_on_success: bool) +fn copy_files(sess: &Session, + target_dir: &Path, + source_dir: &Path) -> Result { // We acquire a shared lock on the lock file of the directory, so that // nobody deletes it out from under us while we are reading from it. @@ -445,9 +442,11 @@ fn copy_files(target_dir: &Path, } } - if print_stats_on_success { - eprintln!("incremental: session directory: {} files hard-linked", files_linked); - eprintln!("incremental: session directory: {} files copied", files_copied); + if sess.opts.debugging_opts.incremental_info { + println!("[incremental] session directory: \ + {} files hard-linked", files_linked); + println!("[incremental] session directory: \ + {} files copied", files_copied); } Ok(files_linked > 0 || files_copied == 0) @@ -616,21 +615,17 @@ fn string_to_timestamp(s: &str) -> Result { fn crate_path(sess: &Session, crate_name: &str, - crate_disambiguator: &str) + crate_disambiguator: CrateDisambiguator) -> PathBuf { - use std::hash::{Hasher, Hash}; - use std::collections::hash_map::DefaultHasher; let incr_dir = sess.opts.incremental.as_ref().unwrap().clone(); - // The full crate disambiguator is really long. A hash of it should be + // The full crate disambiguator is really long. 64 bits of it should be // sufficient. - let mut hasher = DefaultHasher::new(); - crate_disambiguator.hash(&mut hasher); + let crate_disambiguator = crate_disambiguator.to_fingerprint().to_smaller_hash(); + let crate_disambiguator = base_n::encode(crate_disambiguator, INT_ENCODE_BASE); - let crate_name = format!("{}-{}", - crate_name, - base_n::encode(hasher.finish(), INT_ENCODE_BASE)); + let crate_name = format!("{}-{}", crate_name, crate_disambiguator); incr_dir.join(crate_name) } diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 6d019a25ed3ec..5907f00e3dc48 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -10,261 +10,46 @@ //! Code to save/load the dep-graph from files. -use rustc::dep_graph::{DepNode, WorkProductId, DepKind, PreviousDepGraph}; -use rustc::hir::svh::Svh; -use rustc::ich::Fingerprint; +use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph}; use rustc::session::Session; use rustc::ty::TyCtxt; -use rustc::util::nodemap::DefIdMap; -use rustc_data_structures::fx::{FxHashSet, FxHashMap}; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc::ty::maps::OnDiskCache; use rustc_serialize::Decodable as RustcDecodable; use rustc_serialize::opaque::Decoder; -use std::path::{Path}; +use std::path::Path; use super::data::*; use super::fs::*; use super::file_format; use super::work_product; -// The key is a dirty node. The value is **some** base-input that we -// can blame it on. -pub type DirtyNodes = FxHashMap; +pub fn dep_graph_tcx_init<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + if !tcx.dep_graph.is_fully_enabled() { + return + } -/// If we are in incremental mode, and a previous dep-graph exists, -/// then load up those nodes/edges that are still valid into the -/// dep-graph for this session. (This is assumed to be running very -/// early in compilation, before we've really done any work, but -/// actually it doesn't matter all that much.) See `README.md` for -/// more general overview. -pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.allocate_metadata_dep_nodes(); tcx.precompute_in_scope_traits_hashes(); - if tcx.sess.incr_session_load_dep_graph() { - let _ignore = tcx.dep_graph.in_ignore(); - load_dep_graph_if_exists(tcx); - } -} -fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - let dep_graph_path = dep_graph_path(tcx.sess); - let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) { - Some(p) => p, - None => return // no file - }; - - let work_products_path = work_products_path(tcx.sess); - let work_products_data = match load_data(tcx.sess, &work_products_path) { - Some(p) => p, - None => return // no file - }; - - match decode_dep_graph(tcx, &dep_graph_data, &work_products_data) { - Ok(dirty_nodes) => dirty_nodes, - Err(err) => { - tcx.sess.warn( - &format!("decoding error in dep-graph from `{}` and `{}`: {}", - dep_graph_path.display(), - work_products_path.display(), - err)); - } - } -} - -fn load_data(sess: &Session, path: &Path) -> Option> { - match file_format::read_file(sess, path) { - Ok(Some(data)) => return Some(data), - Ok(None) => { - // The file either didn't exist or was produced by an incompatible - // compiler version. Neither is an error. - } - Err(err) => { - sess.err( - &format!("could not load dep-graph from `{}`: {}", - path.display(), err)); - } - } - - if let Err(err) = delete_all_session_dir_contents(sess) { - sess.err(&format!("could not clear incompatible incremental \ - compilation session directory `{}`: {}", - path.display(), err)); - } - - None -} - -/// Check if a DepNode from the previous dep-graph refers to something that -/// still exists in the current compilation session. Only works for DepNode -/// variants that represent inputs (HIR and imported Metadata). -fn does_still_exist(tcx: TyCtxt, dep_node: &DepNode) -> bool { - match dep_node.kind { - DepKind::Hir | - DepKind::HirBody | - DepKind::InScopeTraits | - DepKind::CrateMetadata => { - dep_node.extract_def_id(tcx).is_some() - } - _ => { - bug!("unexpected Input DepNode: {:?}", dep_node) - } + if tcx.sess.incr_comp_session_dir_opt().is_none() { + // If we are only building with -Zquery-dep-graph but without an actual + // incr. comp. session directory, we exit here. Otherwise we'd fail + // when trying to load work products. + return } -} - -/// Decode the dep graph and load the edges/nodes that are still clean -/// into `tcx.dep_graph`. -pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - dep_graph_data: &[u8], - work_products_data: &[u8]) - -> Result<(), String> -{ - // Decode the list of work_products - let mut work_product_decoder = Decoder::new(work_products_data, 0); - let work_products = >::decode(&mut work_product_decoder)?; - // Deserialize the directory and dep-graph. - let mut dep_graph_decoder = Decoder::new(dep_graph_data, 0); - let prev_commandline_args_hash = u64::decode(&mut dep_graph_decoder)?; + let work_products_path = work_products_path(tcx.sess); + if let Some((work_products_data, start_pos)) = load_data(tcx.sess, &work_products_path) { + // Decode the list of work_products + let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); + let work_products: Vec = + RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { + let msg = format!("Error decoding `work-products` from incremental \ + compilation session directory: {}", e); + tcx.sess.fatal(&msg[..]) + }); - if prev_commandline_args_hash != tcx.sess.opts.dep_tracking_hash() { - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: completely ignoring cache because of \ - differing commandline arguments"); - } - // We can't reuse the cache, purge it. - debug!("decode_dep_graph: differing commandline arg hashes"); for swp in work_products { - delete_dirty_work_product(tcx, swp); - } - - // No need to do any further work - return Ok(()); - } - - let serialized_dep_graph = SerializedDepGraph::decode(&mut dep_graph_decoder)?; - - // Compute the set of nodes from the old graph where some input - // has changed or been removed. - let dirty_raw_nodes = initial_dirty_nodes(tcx, - &serialized_dep_graph.nodes, - &serialized_dep_graph.hashes); - let dirty_raw_nodes = transitive_dirty_nodes(&serialized_dep_graph, - dirty_raw_nodes); - - // Recreate the edges in the graph that are still clean. - let mut clean_work_products = FxHashSet(); - let mut dirty_work_products = FxHashSet(); // incomplete; just used to suppress debug output - for (source, targets) in serialized_dep_graph.edge_list_indices.iter_enumerated() { - let target_begin = targets.0 as usize; - let target_end = targets.1 as usize; - - for &target in &serialized_dep_graph.edge_list_data[target_begin .. target_end] { - process_edge(tcx, - source, - target, - &serialized_dep_graph.nodes, - &dirty_raw_nodes, - &mut clean_work_products, - &mut dirty_work_products, - &work_products); - } - } - - // Recreate bootstrap outputs, which are outputs that have no incoming edges - // (and hence cannot be dirty). - for bootstrap_output in &serialized_dep_graph.bootstrap_outputs { - if let DepKind::WorkProduct = bootstrap_output.kind { - let wp_id = WorkProductId::from_fingerprint(bootstrap_output.hash); - clean_work_products.insert(wp_id); - } - - tcx.dep_graph.add_node_directly(*bootstrap_output); - } - - // Add in work-products that are still clean, and delete those that are - // dirty. - reconcile_work_products(tcx, work_products, &clean_work_products); - - Ok(()) -} - -/// Computes which of the original set of def-ids are dirty. Stored in -/// a bit vector where the index is the DefPathIndex. -fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - nodes: &IndexVec, - serialized_hashes: &[(DepNodeIndex, Fingerprint)]) - -> DirtyNodes { - let mut dirty_nodes = FxHashMap(); - - for &(dep_node_index, prev_hash) in serialized_hashes { - let dep_node = nodes[dep_node_index]; - if does_still_exist(tcx, &dep_node) { - let current_hash = tcx.dep_graph.fingerprint_of(&dep_node); - - if current_hash == prev_hash { - debug!("initial_dirty_nodes: {:?} is clean (hash={:?})", - dep_node, - current_hash); - continue; - } - - if tcx.sess.opts.debugging_opts.incremental_dump_hash { - println!("node {:?} is dirty as hash is {:?}, was {:?}", - dep_node, - current_hash, - prev_hash); - } - - debug!("initial_dirty_nodes: {:?} is dirty as hash is {:?}, was {:?}", - dep_node, - current_hash, - prev_hash); - } else { - if tcx.sess.opts.debugging_opts.incremental_dump_hash { - println!("node {:?} is dirty as it was removed", dep_node); - } - - debug!("initial_dirty_nodes: {:?} is dirty as it was removed", dep_node); - } - dirty_nodes.insert(dep_node_index, dep_node_index); - } - - dirty_nodes -} - -fn transitive_dirty_nodes(serialized_dep_graph: &SerializedDepGraph, - mut dirty_nodes: DirtyNodes) - -> DirtyNodes -{ - let mut stack: Vec<(DepNodeIndex, DepNodeIndex)> = vec![]; - stack.extend(dirty_nodes.iter().map(|(&s, &b)| (s, b))); - while let Some((source, blame)) = stack.pop() { - // we know the source is dirty (because of the node `blame`)... - debug_assert!(dirty_nodes.contains_key(&source)); - - // ...so we dirty all the targets (with the same blame) - for &target in serialized_dep_graph.edge_targets_from(source) { - if !dirty_nodes.contains_key(&target) { - dirty_nodes.insert(target, blame); - stack.push((target, blame)); - } - } - } - dirty_nodes -} - -/// Go through the list of work-products produced in the previous run. -/// Delete any whose nodes have been found to be dirty or which are -/// otherwise no longer applicable. -fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - work_products: Vec, - clean_work_products: &FxHashSet) { - debug!("reconcile_work_products({:?})", work_products); - for swp in work_products { - if !clean_work_products.contains(&swp.id) { - debug!("reconcile_work_products: dep-node for {:?} is dirty", swp); - delete_dirty_work_product(tcx, swp); - } else { let mut all_files_exist = true; for &(_, ref file_name) in swp.work_product.saved_files.iter() { let path = in_incr_comp_dir_sess(tcx.sess, file_name); @@ -272,8 +57,8 @@ fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, all_files_exist = false; if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: could not find file for \ - up-to-date work product: {}", path.display()); + eprintln!("incremental: could not find file for work \ + product: {}", path.display()); } } } @@ -289,171 +74,64 @@ fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn delete_dirty_work_product(tcx: TyCtxt, - swp: SerializedWorkProduct) { - debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(tcx.sess, &swp.work_product); -} - -pub fn load_prev_metadata_hashes(tcx: TyCtxt) -> DefIdMap { - let mut output = DefIdMap(); - - if !tcx.sess.opts.debugging_opts.query_dep_graph { - // Previous metadata hashes are only needed for testing. - return output - } - - debug!("load_prev_metadata_hashes() - Loading previous metadata hashes"); - - let file_path = metadata_hash_export_path(tcx.sess); - - if !file_path.exists() { - debug!("load_prev_metadata_hashes() - Couldn't find file containing \ - hashes at `{}`", file_path.display()); - return output - } - - debug!("load_prev_metadata_hashes() - File: {}", file_path.display()); - - let data = match file_format::read_file(tcx.sess, &file_path) { - Ok(Some(data)) => data, +fn load_data(sess: &Session, path: &Path) -> Option<(Vec, usize)> { + match file_format::read_file(sess, path) { + Ok(Some(data_and_pos)) => return Some(data_and_pos), Ok(None) => { - debug!("load_prev_metadata_hashes() - File produced by incompatible \ - compiler version: {}", file_path.display()); - return output + // The file either didn't exist or was produced by an incompatible + // compiler version. Neither is an error. } Err(err) => { - debug!("load_prev_metadata_hashes() - Error reading file `{}`: {}", - file_path.display(), err); - return output - } - }; - - debug!("load_prev_metadata_hashes() - Decoding hashes"); - let mut decoder = Decoder::new(&data, 0); - let _ = Svh::decode(&mut decoder).unwrap(); - let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder).unwrap(); - - debug!("load_prev_metadata_hashes() - Mapping DefIds"); - - assert_eq!(serialized_hashes.index_map.len(), serialized_hashes.entry_hashes.len()); - let def_path_hash_to_def_id = tcx.def_path_hash_to_def_id.as_ref().unwrap(); - - for serialized_hash in serialized_hashes.entry_hashes { - let def_path_hash = serialized_hashes.index_map[&serialized_hash.def_index]; - if let Some(&def_id) = def_path_hash_to_def_id.get(&def_path_hash) { - let old = output.insert(def_id, serialized_hash.hash); - assert!(old.is_none(), "already have hash for {:?}", def_id); + sess.err( + &format!("could not load dep-graph from `{}`: {}", + path.display(), err)); } } - debug!("load_prev_metadata_hashes() - successfully loaded {} hashes", - serialized_hashes.index_map.len()); - - output -} - -fn process_edge<'a, 'tcx, 'edges>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: DepNodeIndex, - target: DepNodeIndex, - nodes: &IndexVec, - dirty_raw_nodes: &DirtyNodes, - clean_work_products: &mut FxHashSet, - dirty_work_products: &mut FxHashSet, - work_products: &[SerializedWorkProduct]) -{ - // If the target is dirty, skip the edge. If this is an edge - // that targets a work-product, we can print the blame - // information now. - if let Some(&blame) = dirty_raw_nodes.get(&target) { - let target = nodes[target]; - if let DepKind::WorkProduct = target.kind { - if tcx.sess.opts.debugging_opts.incremental_info { - let wp_id = WorkProductId::from_fingerprint(target.hash); - - if dirty_work_products.insert(wp_id) { - // Try to reconstruct the human-readable version of the - // DepNode. This cannot be done for things that where - // removed. - let blame = nodes[blame]; - let blame_str = if let Some(def_id) = blame.extract_def_id(tcx) { - format!("{:?}({})", - blame.kind, - tcx.def_path(def_id).to_string(tcx)) - } else { - format!("{:?}", blame) - }; - - let wp = work_products.iter().find(|swp| swp.id == wp_id).unwrap(); - - eprintln!("incremental: module {:?} is dirty because \ - {:?} changed or was removed", - wp.work_product.cgu_name, - blame_str); - } - } - } - return; + if let Err(err) = delete_all_session_dir_contents(sess) { + sess.err(&format!("could not clear incompatible incremental \ + compilation session directory `{}`: {}", + path.display(), err)); } - // At this point we have asserted that the target is clean -- otherwise, we - // would have hit the return above. We can do some further consistency - // checks based on this fact: - - // We should never have an edge where the target is clean but the source - // was dirty. Otherwise something was wrong with the dirtying pass above: - debug_assert!(!dirty_raw_nodes.contains_key(&source)); - - // We also never should encounter an edge going from a removed input to a - // clean target because removing the input would have dirtied the input - // node and transitively dirtied the target. - debug_assert!(match nodes[source].kind { - DepKind::Hir | DepKind::HirBody | DepKind::CrateMetadata => { - does_still_exist(tcx, &nodes[source]) - } - _ => true, - }); - - if !dirty_raw_nodes.contains_key(&target) { - let target = nodes[target]; - let source = nodes[source]; - tcx.dep_graph.add_edge_directly(source, target); - - if let DepKind::WorkProduct = target.kind { - let wp_id = WorkProductId::from_fingerprint(target.hash); - clean_work_products.insert(wp_id); - } - } + None } -pub fn load_dep_graph_new(sess: &Session) -> PreviousDepGraph { - use rustc::dep_graph::SerializedDepGraph as SerializedDepGraphNew; +fn delete_dirty_work_product(tcx: TyCtxt, + swp: SerializedWorkProduct) { + debug!("delete_dirty_work_product({:?})", swp); + work_product::delete_workproduct_files(tcx.sess, &swp.work_product); +} - let empty = PreviousDepGraph::new(SerializedDepGraphNew::new()); +pub fn load_dep_graph(sess: &Session) -> PreviousDepGraph { + let empty = PreviousDepGraph::new(SerializedDepGraph::new()); if sess.opts.incremental.is_none() { return empty } - if let Some(bytes) = load_data(sess, &dep_graph_path_new(sess)) { - let mut decoder = Decoder::new(&bytes, 0); + if let Some((bytes, start_pos)) = load_data(sess, &dep_graph_path(sess)) { + let mut decoder = Decoder::new(&bytes, start_pos); let prev_commandline_args_hash = u64::decode(&mut decoder) .expect("Error reading commandline arg hash from cached dep-graph"); if prev_commandline_args_hash != sess.opts.dep_tracking_hash() { if sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: completely ignoring cache because of \ - differing commandline arguments"); + println!("[incremental] completely ignoring cache because of \ + differing commandline arguments"); } // We can't reuse the cache, purge it. debug!("load_dep_graph_new: differing commandline arg hashes"); + delete_all_session_dir_contents(sess) + .expect("Failed to delete invalidated incr. comp. session \ + directory contents."); + // No need to do any further work return empty } - let dep_graph = SerializedDepGraphNew::decode(&mut decoder) + let dep_graph = SerializedDepGraph::decode(&mut decoder) .expect("Error reading cached dep-graph"); PreviousDepGraph::new(dep_graph) @@ -461,3 +139,16 @@ pub fn load_dep_graph_new(sess: &Session) -> PreviousDepGraph { empty } } + +pub fn load_query_result_cache<'sess>(sess: &'sess Session) -> OnDiskCache<'sess> { + if sess.opts.incremental.is_none() || + !sess.opts.debugging_opts.incremental_queries { + return OnDiskCache::new_empty(sess.codemap()); + } + + if let Some((bytes, start_pos)) = load_data(sess, &query_cache_path(sess)) { + OnDiskCache::new(sess, bytes, start_pos) + } else { + OnDiskCache::new_empty(sess.codemap()) + } +} diff --git a/src/librustc_incremental/persist/mod.rs b/src/librustc_incremental/persist/mod.rs index 688d8add57e3f..82a43d85bc608 100644 --- a/src/librustc_incremental/persist/mod.rs +++ b/src/librustc_incremental/persist/mod.rs @@ -16,7 +16,6 @@ mod data; mod dirty_clean; mod fs; mod load; -mod preds; mod save; mod work_product; mod file_format; @@ -24,8 +23,10 @@ mod file_format; pub use self::fs::prepare_session_directory; pub use self::fs::finalize_session_directory; pub use self::fs::in_incr_comp_dir; +pub use self::load::dep_graph_tcx_init; pub use self::load::load_dep_graph; -pub use self::load::load_dep_graph_new; +pub use self::load::load_query_result_cache; pub use self::save::save_dep_graph; pub use self::save::save_work_products; pub use self::work_product::save_trans_partition; +pub use self::work_product::delete_workproduct_files; diff --git a/src/librustc_incremental/persist/preds/compress/README.md b/src/librustc_incremental/persist/preds/compress/README.md deleted file mode 100644 index d2aa245c7c942..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/README.md +++ /dev/null @@ -1,48 +0,0 @@ -Graph compression - -The graph compression algorithm is intended to remove and minimize the -size of the dependency graph so it can be saved, while preserving -everything we care about. In particular, given a set of input/output -nodes in the graph (which must be disjoint), we ensure that the set of -input nodes that can reach a given output node does not change, -although the intermediate nodes may change in various ways. In short, -the output nodes are intended to be the ones whose existence we care -about when we start up, because they have some associated data that we -will try to re-use (and hence if they are dirty, we have to throw that -data away). The other intermediate nodes don't really matter so much. - -### Overview - -The algorithm works as follows: - -1. Do a single walk of the graph to construct a DAG - - in this walk, we identify and unify all cycles, electing a representative "head" node - - this is done using the union-find implementation - - this code is found in the `classify` module -2. The result from this walk is a `Dag`: - - the set of SCCs, represented by the union-find table - - a set of edges in the new DAG, represented by: - - a vector of parent nodes for each child node - - a vector of cross-edges - - once these are canonicalized, some of these edges may turn out to be cyclic edges - (i.e., an edge A -> A where A is the head of some SCC) -3. We pass this `Dag` into the construct code, which then creates a - new graph. This graph has a smaller set of indices which includes - *at least* the inputs/outputs from the original graph, but may have - other nodes as well, if keeping them reduces the overall size of - the graph. - - This code is found in the `construct` module. - -### Some notes - -The input graph is assumed to have *read-by* edges. i.e., `A -> B` -means that the task B reads data from A. But the DAG defined by -classify is expressed in terms of *reads-from* edges, which are the -inverse. So `A -> B` is the same as `B -rf-> A`. *reads-from* edges -are more natural since we want to walk from the outputs to the inputs, -effectively. When we construct the final graph, we reverse these edges -back into the *read-by* edges common elsewhere. - - - - diff --git a/src/librustc_incremental/persist/preds/compress/classify/mod.rs b/src/librustc_incremental/persist/preds/compress/classify/mod.rs deleted file mode 100644 index aa29afd543c77..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/classify/mod.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! First phase. Detect cycles and cross-edges. - -use super::*; - -#[cfg(test)] -mod test; - -pub struct Classify<'a, 'g: 'a, N: 'g, I: 'a, O: 'a> - where N: Debug + Clone + 'g, - I: Fn(&N) -> bool, - O: Fn(&N) -> bool, -{ - r: &'a mut GraphReduce<'g, N, I, O>, - stack: Vec, - colors: Vec, - dag: Dag, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum Color { - // not yet visited - White, - - // visiting; usize is index on stack - Grey(usize), - - // finished visiting - Black, -} - -impl<'a, 'g, N, I, O> Classify<'a, 'g, N, I, O> - where N: Debug + Clone + 'g, - I: Fn(&N) -> bool, - O: Fn(&N) -> bool, -{ - pub(super) fn new(r: &'a mut GraphReduce<'g, N, I, O>) -> Self { - Classify { - r, - colors: vec![Color::White; r.in_graph.len_nodes()], - stack: vec![], - dag: Dag { - parents: (0..r.in_graph.len_nodes()).map(|i| NodeIndex(i)).collect(), - cross_edges: vec![], - input_nodes: vec![], - output_nodes: vec![], - }, - } - } - - pub(super) fn walk(mut self) -> Dag { - for (index, node) in self.r.in_graph.all_nodes().iter().enumerate() { - if (self.r.is_output)(&node.data) { - let index = NodeIndex(index); - self.dag.output_nodes.push(index); - match self.colors[index.0] { - Color::White => self.open(index), - Color::Grey(_) => panic!("grey node but have not yet started a walk"), - Color::Black => (), // already visited, skip - } - } - } - - // At this point we've identifed all the cycles, and we've - // constructed a spanning tree over the original graph - // (encoded in `self.parents`) as well as a list of - // cross-edges that reflect additional edges from the DAG. - // - // If we converted each node to its `cycle-head` (a - // representative choice from each SCC, basically) and then - // take the union of `self.parents` and `self.cross_edges` - // (after canonicalization), that is basically our DAG. - // - // Note that both of those may well contain trivial `X -rf-> X` - // cycle edges after canonicalization, though. e.g., if you - // have a graph `{A -rf-> B, B -rf-> A}`, we will have unioned A and - // B, but A will also be B's parent (or vice versa), and hence - // when we canonicalize the parent edge it would become `A -rf-> - // A` (or `B -rf-> B`). - self.dag - } - - fn open(&mut self, node: NodeIndex) { - let index = self.stack.len(); - self.stack.push(node); - self.colors[node.0] = Color::Grey(index); - for child in self.r.inputs(node) { - self.walk_edge(node, child); - } - self.stack.pop().unwrap(); - self.colors[node.0] = Color::Black; - - if (self.r.is_input)(&self.r.in_graph.node_data(node)) { - // base inputs should have no inputs - assert!(self.r.inputs(node).next().is_none()); - debug!("input: `{:?}`", self.r.in_graph.node_data(node)); - self.dag.input_nodes.push(node); - } - } - - fn walk_edge(&mut self, parent: NodeIndex, child: NodeIndex) { - debug!("walk_edge: {:?} -rf-> {:?}, {:?}", - self.r.in_graph.node_data(parent), - self.r.in_graph.node_data(child), - self.colors[child.0]); - - // Ignore self-edges, just in case they exist. - if child == parent { - return; - } - - match self.colors[child.0] { - Color::White => { - // Not yet visited this node; start walking it. - assert_eq!(self.dag.parents[child.0], child); - self.dag.parents[child.0] = parent; - self.open(child); - } - - Color::Grey(stack_index) => { - // Back-edge; unify everything on stack between here and `stack_index` - // since we are all participating in a cycle - assert!(self.stack[stack_index] == child); - - for &n in &self.stack[stack_index..] { - debug!("cycle `{:?}` and `{:?}`", - self.r.in_graph.node_data(n), - self.r.in_graph.node_data(parent)); - self.r.mark_cycle(n, parent); - } - } - - Color::Black => { - // Cross-edge, record and ignore - self.dag.cross_edges.push((parent, child)); - debug!("cross-edge `{:?} -rf-> {:?}`", - self.r.in_graph.node_data(parent), - self.r.in_graph.node_data(child)); - } - } - } -} diff --git a/src/librustc_incremental/persist/preds/compress/classify/test.rs b/src/librustc_incremental/persist/preds/compress/classify/test.rs deleted file mode 100644 index ca26f714a2a74..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/classify/test.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::*; - -#[test] -fn detect_cycles() { - let (graph, nodes) = graph! { - A -> C0, - A -> C1, - B -> C1, - C0 -> C1, - C1 -> C0, - C0 -> D, - C1 -> E, - }; - let inputs = ["A", "B"]; - let outputs = ["D", "E"]; - let mut reduce = GraphReduce::new(&graph, |n| inputs.contains(n), |n| outputs.contains(n)); - Classify::new(&mut reduce).walk(); - - assert!(!reduce.in_cycle(nodes("A"), nodes("C0"))); - assert!(!reduce.in_cycle(nodes("B"), nodes("C0"))); - assert!(reduce.in_cycle(nodes("C0"), nodes("C1"))); - assert!(!reduce.in_cycle(nodes("D"), nodes("C0"))); - assert!(!reduce.in_cycle(nodes("E"), nodes("C0"))); - assert!(!reduce.in_cycle(nodes("E"), nodes("A"))); -} - -/// Regr test for a bug where we forgot to pop nodes off of the stack -/// as we were walking. In this case, because edges are pushed to the front -/// of the list, we would visit OUT, then A, then IN, and then close IN (but forget -/// to POP. Then visit B, C, and then A, which would mark everything from A to C as -/// cycle. But since we failed to pop IN, the stack was `OUT, A, IN, B, C` so that -/// marked C and IN as being in a cycle. -#[test] -fn edge_order1() { - let (graph, nodes) = graph! { - A -> C, - C -> B, - B -> A, - IN -> B, - IN -> A, - A -> OUT, - }; - let inputs = ["IN"]; - let outputs = ["OUT"]; - let mut reduce = GraphReduce::new(&graph, |n| inputs.contains(n), |n| outputs.contains(n)); - Classify::new(&mut reduce).walk(); - - // A, B, and C are mutually in a cycle, but IN/OUT are not participating. - let names = ["A", "B", "C", "IN", "OUT"]; - let cycle_names = ["A", "B", "C"]; - for &i in &names { - for &j in names.iter().filter(|&&j| j != i) { - let in_cycle = cycle_names.contains(&i) && cycle_names.contains(&j); - assert_eq!(reduce.in_cycle(nodes(i), nodes(j)), in_cycle, - "cycle status for nodes {} and {} is incorrect", - i, j); - } - } -} - -/// Same as `edge_order1` but in reverse order so as to detect a failure -/// if we were to enqueue edges onto end of list instead. -#[test] -fn edge_order2() { - let (graph, nodes) = graph! { - A -> OUT, - IN -> A, - IN -> B, - B -> A, - C -> B, - A -> C, - }; - let inputs = ["IN"]; - let outputs = ["OUT"]; - let mut reduce = GraphReduce::new(&graph, |n| inputs.contains(n), |n| outputs.contains(n)); - Classify::new(&mut reduce).walk(); - - assert!(reduce.in_cycle(nodes("B"), nodes("C"))); - - assert!(!reduce.in_cycle(nodes("IN"), nodes("A"))); - assert!(!reduce.in_cycle(nodes("IN"), nodes("B"))); - assert!(!reduce.in_cycle(nodes("IN"), nodes("C"))); - assert!(!reduce.in_cycle(nodes("IN"), nodes("OUT"))); -} diff --git a/src/librustc_incremental/persist/preds/compress/construct.rs b/src/librustc_incremental/persist/preds/compress/construct.rs deleted file mode 100644 index 0ad8d1789167d..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/construct.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Second phase. Construct new graph. The previous phase has -//! converted the input graph into a DAG by detecting and unifying -//! cycles. It provides us with the following (which is a -//! representation of the DAG): -//! -//! - SCCs, in the form of a union-find repr that can convert each node to -//! its *cycle head* (an arbitrarily chosen representative from the cycle) -//! - a vector of *leaf nodes*, just a convenience -//! - a vector of *parents* for each node (in some cases, nodes have no parents, -//! or their parent is another member of same cycle; in that case, the vector -//! will be stored `v[i] == i`, after canonicalization) -//! - a vector of *cross edges*, meaning add'l edges between graphs nodes beyond -//! the parents. - -use rustc_data_structures::fx::FxHashMap; - -use super::*; - -pub(super) fn construct_graph<'g, N, I, O>(r: &mut GraphReduce<'g, N, I, O>, dag: Dag) - -> Reduction<'g, N> - where N: Debug + Clone, I: Fn(&N) -> bool, O: Fn(&N) -> bool, -{ - let Dag { parents: old_parents, input_nodes, output_nodes, cross_edges } = dag; - let in_graph = r.in_graph; - - debug!("construct_graph"); - - // Create a canonical list of edges; this includes both parent and - // cross-edges. We store this in `(target -> Vec)` form. - // We call the first edge to any given target its "parent". - let mut edges = FxHashMap(); - let old_parent_edges = old_parents.iter().cloned().zip((0..).map(NodeIndex)); - for (source, target) in old_parent_edges.chain(cross_edges) { - debug!("original edge `{:?} -rf-> {:?}`", - in_graph.node_data(source), - in_graph.node_data(target)); - let source = r.cycle_head(source); - let target = r.cycle_head(target); - if source != target { - let v = edges.entry(target).or_insert(vec![]); - if !v.contains(&source) { - debug!("edge `{:?} -rf-> {:?}` is edge #{} with that target", - in_graph.node_data(source), - in_graph.node_data(target), - v.len()); - v.push(source); - } - } - } - let parent = |ni: NodeIndex| -> NodeIndex { - edges[&ni][0] - }; - - // `retain_map`: a map of those nodes that we will want to - // *retain* in the ultimate graph; the key is the node index in - // the old graph, the value is the node index in the new - // graph. These are nodes in the following categories: - // - // - inputs - // - work-products - // - targets of a cross-edge - // - // The first two categories hopefully make sense. We want the - // inputs so we can compare hashes later. We want the - // work-products so we can tell precisely when a given - // work-product is invalidated. But the last one isn't strictly - // needed; we keep cross-target edges so as to minimize the total - // graph size. - // - // Consider a graph like: - // - // WP0 -rf-> Y - // WP1 -rf-> Y - // Y -rf-> INPUT0 - // Y -rf-> INPUT1 - // Y -rf-> INPUT2 - // Y -rf-> INPUT3 - // - // Now if we were to remove Y, we would have a total of 8 edges: both WP0 and WP1 - // depend on INPUT0...INPUT3. As it is, we have 6 edges. - // - // NB: The current rules are not optimal. For example, given this - // input graph: - // - // OUT0 -rf-> X - // OUT1 -rf-> X - // X -rf -> INPUT0 - // - // we will preserve X because it has two "consumers" (OUT0 and - // OUT1). We could as easily skip it, but we'd have to tally up - // the number of input nodes that it (transitively) reaches, and I - // was too lazy to do so. This is the unit test `suboptimal`. - - let mut retain_map = FxHashMap(); - let mut new_graph = Graph::new(); - - { - // Start by adding start-nodes and inputs. - let retained_nodes = output_nodes.iter().chain(&input_nodes).map(|&n| r.cycle_head(n)); - - // Next add in targets of cross-edges. Due to the canonicalization, - // some of these may be self-edges or may may duplicate the parent - // edges, so ignore those. - let retained_nodes = retained_nodes.chain( - edges.iter() - .filter(|&(_, ref sources)| sources.len() > 1) - .map(|(&target, _)| target)); - - // Now create the new graph, adding in the entries from the map. - for n in retained_nodes { - retain_map.entry(n) - .or_insert_with(|| { - let data = in_graph.node_data(n); - debug!("retaining node `{:?}`", data); - new_graph.add_node(data) - }); - } - } - - // Given a cycle-head `ni`, converts it to the closest parent that has - // been retained in the output graph. - let retained_parent = |mut ni: NodeIndex| -> NodeIndex { - loop { - debug!("retained_parent({:?})", in_graph.node_data(ni)); - match retain_map.get(&ni) { - Some(&v) => return v, - None => ni = parent(ni), - } - } - }; - - // Now add in the edges into the graph. - for (&target, sources) in &edges { - if let Some(&r_target) = retain_map.get(&target) { - debug!("adding edges that target `{:?}`", in_graph.node_data(target)); - for &source in sources { - debug!("new edge `{:?} -rf-> {:?}`", - in_graph.node_data(source), - in_graph.node_data(target)); - let r_source = retained_parent(source); - - // NB. In the input graph, we have `a -> b` if b - // **reads from** a. But in the terminology of this - // code, we would describe that edge as `b -> a`, - // because we have edges *from* outputs *to* inputs. - // Therefore, when we create our new graph, we have to - // reverse the edge. - new_graph.add_edge(r_target, r_source, ()); - } - } else { - assert_eq!(sources.len(), 1); - } - } - - // One complication. In some cases, output nodes *may* participate in - // cycles. An example: - // - // [HIR0] [HIR1] - // | | - // v v - // TypeckClosureBody(X) -> ItemSignature(X::SomeClosureInX) - // | ^ | | - // | +-------------------------+ | - // | | - // v v - // Foo Bar - // - // In these cases, the output node may not wind up as the head - // of the cycle, in which case it would be absent from the - // final graph. We don't wish this to happen, therefore we go - // over the list of output nodes again and check for any that - // are not their own cycle-head. If we find such a node, we - // add it to the graph now with an edge from the cycle head. - // So the graph above could get transformed into this: - // - // [HIR0, HIR1] - // | - // v - // TypeckClosureBody(X) ItemSignature(X::SomeClosureInX) - // ^ | | - // +-------------------------+ | - // v - // [Foo, Bar] - // - // (Note that all the edges here are "read-by" edges, not - // "reads-from" edges.) - for &output_node in &output_nodes { - let head = r.cycle_head(output_node); - if output_node == head { - assert!(retain_map.contains_key(&output_node)); - } else { - assert!(!retain_map.contains_key(&output_node)); - let output_data = in_graph.node_data(output_node); - let new_node = new_graph.add_node(output_data); - let new_head_node = retain_map[&head]; - new_graph.add_edge(new_head_node, new_node, ()); - } - } - - // Finally, prepare a list of the input node indices as found in - // the new graph. Note that since all input nodes are leaves in - // the graph, they should never participate in a cycle. - let input_nodes = - input_nodes.iter() - .map(|&n| { - assert_eq!(r.cycle_head(n), n, "input node participating in a cycle"); - retain_map[&n] - }) - .collect(); - - Reduction { graph: new_graph, input_nodes: input_nodes } -} - diff --git a/src/librustc_incremental/persist/preds/compress/dag_id.rs b/src/librustc_incremental/persist/preds/compress/dag_id.rs deleted file mode 100644 index a286862e9551f..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/dag_id.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc_data_structures::graph::NodeIndex; -use rustc_data_structures::unify::UnifyKey; - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct DagId { - index: u32, -} - -impl DagId { - pub fn from_input_index(n: NodeIndex) -> Self { - DagId { index: n.0 as u32 } - } - - pub fn as_input_index(&self) -> NodeIndex { - NodeIndex(self.index as usize) - } -} - -impl UnifyKey for DagId { - type Value = (); - - fn index(&self) -> u32 { - self.index - } - - fn from_index(u: u32) -> Self { - DagId { index: u } - } - - fn tag(_: Option) -> &'static str { - "DagId" - } -} diff --git a/src/librustc_incremental/persist/preds/compress/mod.rs b/src/librustc_incremental/persist/preds/compress/mod.rs deleted file mode 100644 index 974a2221a4575..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/mod.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Graph compression. See `README.md`. - -use rustc_data_structures::graph::{Graph, NodeIndex}; -use rustc_data_structures::unify::UnificationTable; -use std::fmt::Debug; - -#[cfg(test)] -#[macro_use] -mod test_macro; - -mod construct; - -mod classify; -use self::classify::Classify; - -mod dag_id; -use self::dag_id::DagId; - -#[cfg(test)] -mod test; - -pub fn reduce_graph(graph: &Graph, - is_input: I, - is_output: O) -> Reduction - where N: Debug + Clone, - I: Fn(&N) -> bool, - O: Fn(&N) -> bool, -{ - GraphReduce::new(graph, is_input, is_output).compute() -} - -pub struct Reduction<'q, N> where N: 'q + Debug + Clone { - pub graph: Graph<&'q N, ()>, - pub input_nodes: Vec, -} - -struct GraphReduce<'q, N, I, O> - where N: 'q + Debug + Clone, - I: Fn(&N) -> bool, - O: Fn(&N) -> bool, -{ - in_graph: &'q Graph, - unify: UnificationTable, - is_input: I, - is_output: O, -} - -struct Dag { - // The "parent" of a node is the node which reached it during the - // initial DFS. To encode the case of "no parent" (i.e., for the - // roots of the walk), we make `parents[i] == i` to start, which - // turns out be convenient. - parents: Vec, - - // Additional edges beyond the parents. - cross_edges: Vec<(NodeIndex, NodeIndex)>, - - // Nodes which we found that are considered "outputs" - output_nodes: Vec, - - // Nodes which we found that are considered "inputs" - input_nodes: Vec, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct DagNode { - in_index: NodeIndex -} - -impl<'q, N, I, O> GraphReduce<'q, N, I, O> - where N: Debug + Clone, - I: Fn(&N) -> bool, - O: Fn(&N) -> bool, -{ - fn new(in_graph: &'q Graph, is_input: I, is_output: O) -> Self { - let mut unify = UnificationTable::new(); - - // create a set of unification keys whose indices - // correspond to the indices from the input graph - for i in 0..in_graph.len_nodes() { - let k = unify.new_key(()); - assert!(k == DagId::from_input_index(NodeIndex(i))); - } - - GraphReduce { in_graph, unify, is_input, is_output } - } - - fn compute(mut self) -> Reduction<'q, N> { - let dag = Classify::new(&mut self).walk(); - construct::construct_graph(&mut self, dag) - } - - fn inputs(&self, in_node: NodeIndex) -> impl Iterator + 'q { - self.in_graph.predecessor_nodes(in_node) - } - - fn mark_cycle(&mut self, in_node1: NodeIndex, in_node2: NodeIndex) { - let dag_id1 = DagId::from_input_index(in_node1); - let dag_id2 = DagId::from_input_index(in_node2); - self.unify.union(dag_id1, dag_id2); - } - - /// Convert a dag-id into its cycle head representative. This will - /// be a no-op unless `in_node` participates in a cycle, in which - /// case a distinct node *may* be returned. - fn cycle_head(&mut self, in_node: NodeIndex) -> NodeIndex { - let i = DagId::from_input_index(in_node); - self.unify.find(i).as_input_index() - } - - #[cfg(test)] - fn in_cycle(&mut self, ni1: NodeIndex, ni2: NodeIndex) -> bool { - self.cycle_head(ni1) == self.cycle_head(ni2) - } -} diff --git a/src/librustc_incremental/persist/preds/compress/test.rs b/src/librustc_incremental/persist/preds/compress/test.rs deleted file mode 100644 index 1c5130845a855..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/test.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::*; - -fn reduce(graph: &Graph<&'static str, ()>, - inputs: &[&'static str], - outputs: &[&'static str], - expected: &[&'static str]) -{ - let reduce = GraphReduce::new(&graph, - |n| inputs.contains(n), - |n| outputs.contains(n)); - let result = reduce.compute(); - let mut edges: Vec = - result.graph - .all_edges() - .iter() - .map(|edge| format!("{} -> {}", - result.graph.node_data(edge.source()), - result.graph.node_data(edge.target()))) - .collect(); - edges.sort(); - println!("{:#?}", edges); - assert_eq!(edges.len(), expected.len()); - for (expected, actual) in expected.iter().zip(&edges) { - assert_eq!(expected, actual); - } -} - -#[test] -fn test1() { - // +---------------+ - // | | - // | +--------|------+ - // | | v v - // [A] -> [C0] -> [C1] [D] - // [ ] <- [ ] -> [E] - // ^ - // [B] -------------+ - let (graph, _nodes) = graph! { - A -> C0, - A -> C1, - B -> C1, - C0 -> C1, - C1 -> C0, - C0 -> D, - C1 -> E, - }; - - // [A] -> [C1] -> [D] - // [B] -> [ ] -> [E] - reduce(&graph, &["A", "B"], &["D", "E"], &[ - "A -> C1", - "B -> C1", - "C1 -> D", - "C1 -> E", - ]); -} - -#[test] -fn test2() { - // +---------------+ - // | | - // | +--------|------+ - // | | v v - // [A] -> [C0] -> [C1] [D] -> [E] - // [ ] <- [ ] - // ^ - // [B] -------------+ - let (graph, _nodes) = graph! { - A -> C0, - A -> C1, - B -> C1, - C0 -> C1, - C1 -> C0, - C0 -> D, - D -> E, - }; - - // [A] -> [D] -> [E] - // [B] -> [ ] - reduce(&graph, &["A", "B"], &["D", "E"], &[ - "A -> D", - "B -> D", - "D -> E", - ]); -} - -#[test] -fn test2b() { - // Variant on test2 in which [B] is not - // considered an input. - let (graph, _nodes) = graph! { - A -> C0, - A -> C1, - B -> C1, - C0 -> C1, - C1 -> C0, - C0 -> D, - D -> E, - }; - - // [A] -> [D] -> [E] - reduce(&graph, &["A"], &["D", "E"], &[ - "A -> D", - "D -> E", - ]); -} - -#[test] -fn test3() { - - // Edges going *downwards*, so 0, 1 and 2 are inputs, - // while 7, 8, and 9 are outputs. - // - // 0 1 2 - // | \ / - // 3---+ | - // | | | - // | | | - // 4 5 6 - // \ / \ / \ - // | | | - // 7 8 9 - // - // Here the end result removes node 4, instead encoding an edge - // from n3 -> n7, but keeps nodes 5 and 6, as they are common - // inputs to nodes 8/9. - - let (graph, _nodes) = graph! { - n0 -> n3, - n3 -> n4, - n3 -> n5, - n4 -> n7, - n5 -> n7, - n5 -> n8, - n1 -> n6, - n2 -> n6, - n6 -> n8, - n6 -> n9, - }; - - reduce(&graph, &["n0", "n1", "n2"], &["n7", "n8", "n9"], &[ - "n0 -> n3", - "n1 -> n6", - "n2 -> n6", - "n3 -> n5", - "n3 -> n7", - "n5 -> n7", - "n5 -> n8", - "n6 -> n8", - "n6 -> n9" - ]); -} - -#[test] -fn test_cached_dfs_cyclic() { - - // 0 1 <---- 2 3 - // ^ | ^ ^ - // | v | | - // 4 ----> 5 ----> 6 ----> 7 - // ^ ^ ^ ^ - // | | | | - // 8 9 10 11 - - let (graph, _nodes) = graph! { - // edges from above diagram, in columns, top-to-bottom: - n4 -> n0, - n8 -> n4, - n4 -> n5, - n1 -> n5, - n9 -> n5, - n2 -> n1, - n5 -> n6, - n6 -> n2, - n10 -> n6, - n6 -> n7, - n7 -> n3, - n11 -> n7, - }; - - // 0 1 2 3 - // ^ ^ / ^ - // | |/ | - // 4 ----> 5 --------------+ - // ^ ^ \ | - // | | \ | - // 8 9 10 11 - - reduce(&graph, &["n8", "n9", "n10", "n11"], &["n0", "n1", "n2", "n3"], &[ - "n10 -> n5", - "n11 -> n3", - "n4 -> n0", - "n4 -> n5", - "n5 -> n1", - "n5 -> n2", - "n5 -> n3", - "n8 -> n4", - "n9 -> n5" - ]); -} - -/// Demonstrates the case where we don't reduce as much as we could. -#[test] -fn suboptimal() { - let (graph, _nodes) = graph! { - INPUT0 -> X, - X -> OUTPUT0, - X -> OUTPUT1, - }; - - reduce(&graph, &["INPUT0"], &["OUTPUT0", "OUTPUT1"], &[ - "INPUT0 -> X", - "X -> OUTPUT0", - "X -> OUTPUT1" - ]); -} - -#[test] -fn test_cycle_output() { - // +---------------+ - // | | - // | +--------|------+ - // | | v v - // [A] -> [C0] <-> [C1] <- [D] - // +----> [E] - // ^ - // [B] ----------------- ---+ - let (graph, _nodes) = graph! { - A -> C0, - A -> C1, - B -> E, - C0 -> C1, - C1 -> C0, - C0 -> D, - C1 -> E, - D -> C1, - }; - - // [A] -> [C0] --> [D] - // +----> [E] - // ^ - // [B] -------------+ - reduce(&graph, &["A", "B"], &["D", "E"], &[ - "A -> C0", - "B -> E", - "C0 -> D", - "C0 -> E", - ]); -} diff --git a/src/librustc_incremental/persist/preds/compress/test_macro.rs b/src/librustc_incremental/persist/preds/compress/test_macro.rs deleted file mode 100644 index 044b143e30625..0000000000000 --- a/src/librustc_incremental/persist/preds/compress/test_macro.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -macro_rules! graph { - ($( $source:ident -> $target:ident, )*) => { - { - use $crate::rustc_data_structures::graph::{Graph, NodeIndex}; - use $crate::rustc_data_structures::fx::FxHashMap; - - let mut graph = Graph::new(); - let mut nodes: FxHashMap<&'static str, NodeIndex> = FxHashMap(); - - for &name in &[ $(stringify!($source), stringify!($target)),* ] { - let name: &'static str = name; - nodes.entry(name) - .or_insert_with(|| graph.add_node(name)); - } - - $( - { - let source = nodes[&stringify!($source)]; - let target = nodes[&stringify!($target)]; - graph.add_edge(source, target, ()); - } - )* - - let f = move |name: &'static str| -> NodeIndex { nodes[&name] }; - - (graph, f) - } - } -} diff --git a/src/librustc_incremental/persist/preds/mod.rs b/src/librustc_incremental/persist/preds/mod.rs deleted file mode 100644 index a552a27c62af0..0000000000000 --- a/src/librustc_incremental/persist/preds/mod.rs +++ /dev/null @@ -1,108 +0,0 @@ -// 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 rustc::dep_graph::{DepGraphQuery, DepNode, DepKind}; -use rustc::ich::Fingerprint; -use rustc::ty::TyCtxt; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::{Graph, NodeIndex}; - - -mod compress; - -/// A data-structure that makes it easy to enumerate the hashable -/// predecessors of any given dep-node. -pub struct Predecessors<'query> { - // A reduced version of the input graph that contains fewer nodes. - // This is intended to keep all of the base inputs (i.e., HIR - // nodes) and all of the "work-products" we may care about - // later. Other nodes may be retained if it keeps the overall size - // of the graph down. - pub reduced_graph: Graph<&'query DepNode, ()>, - - // These are output nodes that have no incoming edges. We have to - // track these specially because, when we load the data back up - // again, we want to make sure and recreate these nodes (we want - // to recreate the nodes where all incoming edges are clean; but - // since we ordinarily just serialize edges, we wind up just - // forgetting that bootstrap outputs even exist in that case.) - pub bootstrap_outputs: Vec<&'query DepNode>, - - // For the inputs (hir/foreign-metadata), we include hashes. - pub hashes: FxHashMap<&'query DepNode, Fingerprint>, -} - -impl<'q> Predecessors<'q> { - pub fn new(tcx: TyCtxt, query: &'q DepGraphQuery) -> Self { - // Find the set of "start nodes". These are nodes that we will - // possibly query later. - let is_output = |node: &DepNode| -> bool { - match node.kind { - DepKind::WorkProduct => true, - DepKind::CrateMetadata => { - // We do *not* create dep-nodes for the current crate's - // metadata anymore, just for metadata that we import/read - // from other crates. - debug_assert!(!node.extract_def_id(tcx).unwrap().is_local()); - false - } - // if -Z query-dep-graph is passed, save more extended data - // to enable better unit testing - DepKind::TypeckTables => tcx.sess.opts.debugging_opts.query_dep_graph, - - _ => false, - } - }; - - // Reduce the graph to the most important nodes. - let compress::Reduction { graph, input_nodes } = - compress::reduce_graph(&query.graph, - |n| n.kind.is_input(), - |n| is_output(n)); - - let mut hashes = FxHashMap(); - for input_index in input_nodes { - let input = *graph.node_data(input_index); - debug!("computing hash for input node `{:?}`", input); - hashes.entry(input) - .or_insert_with(|| tcx.dep_graph.fingerprint_of(&input)); - } - - if tcx.sess.opts.debugging_opts.query_dep_graph { - // Not all inputs might have been reachable from an output node, - // but we still want their hash for our unit tests. - let hir_nodes = query.graph.all_nodes().iter().filter_map(|node| { - match node.data.kind { - DepKind::Hir => Some(&node.data), - _ => None, - } - }); - - for node in hir_nodes { - hashes.entry(node) - .or_insert_with(|| tcx.dep_graph.fingerprint_of(&node)); - } - } - - let bootstrap_outputs: Vec<&'q DepNode> = - (0 .. graph.len_nodes()) - .map(NodeIndex) - .filter(|&n| graph.incoming_edges(n).next().is_none()) - .map(|n| *graph.node_data(n)) - .filter(|n| is_output(n)) - .collect(); - - Predecessors { - reduced_graph: graph, - bootstrap_outputs, - hashes, - } - } -} diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 83a618211dad3..6eaa14a50f40f 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -8,18 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::dep_graph::{DepGraph, DepNode}; -use rustc::hir::def_id::DefId; -use rustc::hir::svh::Svh; -use rustc::ich::Fingerprint; -use rustc::middle::cstore::EncodedMetadataHashes; +use rustc::dep_graph::{DepGraph, DepKind}; use rustc::session::Session; use rustc::ty::TyCtxt; use rustc::util::common::time; -use rustc::util::nodemap::DefIdMap; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph; -use rustc_data_structures::indexed_vec::IndexVec; use rustc_serialize::Encodable as RustcEncodable; use rustc_serialize::opaque::Encoder; use std::io::{self, Cursor, Write}; @@ -27,17 +20,12 @@ use std::fs::{self, File}; use std::path::PathBuf; use super::data::*; -use super::preds::*; use super::fs::*; use super::dirty_clean; use super::file_format; use super::work_product; -use super::load::load_prev_metadata_hashes; - -pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - metadata_hashes: &EncodedMetadataHashes, - svh: Svh) { +pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { debug!("save_dep_graph()"); let _ignore = tcx.dep_graph.in_ignore(); let sess = tcx.sess; @@ -45,54 +33,21 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, return; } - // We load the previous metadata hashes now before overwriting the file - // (if we need them for testing). - let prev_metadata_hashes = if tcx.sess.opts.debugging_opts.query_dep_graph { - load_prev_metadata_hashes(tcx) - } else { - DefIdMap() - }; - - let mut current_metadata_hashes = FxHashMap(); - - // IMPORTANT: We are saving the metadata hashes *before* the dep-graph, - // since metadata-encoding might add new entries to the - // DefIdDirectory (which is saved in the dep-graph file). - if sess.opts.debugging_opts.incremental_cc || - sess.opts.debugging_opts.query_dep_graph { - save_in(sess, - metadata_hash_export_path(sess), - |e| encode_metadata_hashes(tcx, - svh, - metadata_hashes, - &mut current_metadata_hashes, - e)); - } - - time(sess.time_passes(), "persist dep-graph (old)", || { - let query = tcx.dep_graph.query(); - - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: {} nodes in dep-graph", query.graph.len_nodes()); - eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges()); - } - - let preds = Predecessors::new(tcx, &query); + time(sess.time_passes(), "persist query result cache", || { save_in(sess, - dep_graph_path(sess), - |e| encode_dep_graph(tcx, &preds, e)); + query_cache_path(sess), + |e| encode_query_cache(tcx, e)); }); - time(sess.time_passes(), "persist dep-graph (new)", || { - save_in(sess, - dep_graph_path_new(sess), - |e| encode_dep_graph_new(tcx, e)); - }); + if tcx.sess.opts.debugging_opts.incremental_queries { + time(sess.time_passes(), "persist dep-graph", || { + save_in(sess, + dep_graph_path(sess), + |e| encode_dep_graph(tcx, e)); + }); + } dirty_clean::check_dirty_clean_annotations(tcx); - dirty_clean::check_dirty_clean_metadata(tcx, - &prev_metadata_hashes, - ¤t_metadata_hashes); } pub fn save_work_products(sess: &Session, dep_graph: &DepGraph) { @@ -182,164 +137,96 @@ fn save_in(sess: &Session, path_buf: PathBuf, encode: F) } } -fn encode_dep_graph_new(tcx: TyCtxt, - encoder: &mut Encoder) - -> io::Result<()> { +fn encode_dep_graph(tcx: TyCtxt, + encoder: &mut Encoder) + -> io::Result<()> { // First encode the commandline arguments hash tcx.sess.opts.dep_tracking_hash().encode(encoder)?; // Encode the graph data. let serialized_graph = tcx.dep_graph.serialize(); - serialized_graph.encode(encoder)?; - - Ok(()) -} - -pub fn encode_dep_graph(tcx: TyCtxt, - preds: &Predecessors, - encoder: &mut Encoder) - -> io::Result<()> { - // First encode the commandline arguments hash - tcx.sess.opts.dep_tracking_hash().encode(encoder)?; - - // NB: We rely on this Vec being indexable by reduced_graph's NodeIndex. - let mut nodes: IndexVec = preds - .reduced_graph - .all_nodes() - .iter() - .map(|node| node.data.clone()) - .collect(); - - let mut edge_list_indices = IndexVec::with_capacity(nodes.len()); - let mut edge_list_data = Vec::with_capacity(preds.reduced_graph.len_edges()); - - for node_index in 0 .. nodes.len() { - let start = edge_list_data.len() as u32; - - for target in preds.reduced_graph.successor_nodes(graph::NodeIndex(node_index)) { - edge_list_data.push(DepNodeIndex::new(target.node_id())); - } - let end = edge_list_data.len() as u32; - debug_assert_eq!(node_index, edge_list_indices.len()); - edge_list_indices.push((start, end)); - } - - // Let's make sure we had no overflow there. - assert!(edge_list_data.len() <= ::std::u32::MAX as usize); - // Check that we have a consistent number of edges. - assert_eq!(edge_list_data.len(), preds.reduced_graph.len_edges()); - - let bootstrap_outputs = preds.bootstrap_outputs - .iter() - .map(|dep_node| (**dep_node).clone()) - .collect(); - - // Next, build the map of content hashes. To this end, we need to transform - // the (DepNode -> Fingerprint) map that we have into a - // (DepNodeIndex -> Fingerprint) map. This may necessitate adding nodes back - // to the dep-graph that have been filtered out during reduction. - let content_hashes = { - // We have to build a (DepNode -> DepNodeIndex) map. We over-allocate a - // little because we expect some more nodes to be added. - let capacity = (nodes.len() * 120) / 100; - let mut node_to_index = FxHashMap::with_capacity_and_hasher(capacity, - Default::default()); - // Add the nodes we already have in the graph. - node_to_index.extend(nodes.iter_enumerated() - .map(|(index, &node)| (node, index))); - - let mut content_hashes = Vec::with_capacity(preds.hashes.len()); - - for (&&dep_node, &hash) in preds.hashes.iter() { - let dep_node_index = *node_to_index - .entry(dep_node) - .or_insert_with(|| { - // There is no DepNodeIndex for this DepNode yet. This - // happens when the DepNode got filtered out during graph - // reduction. Since we have a content hash for the DepNode, - // we add it back to the graph. - let next_index = nodes.len(); - nodes.push(dep_node); - - debug_assert_eq!(next_index, edge_list_indices.len()); - // Push an empty list of edges - edge_list_indices.push((0,0)); - - DepNodeIndex::new(next_index) - }); - - content_hashes.push((dep_node_index, hash)); + if tcx.sess.opts.debugging_opts.incremental_info { + #[derive(Clone)] + struct Stat { + kind: DepKind, + node_counter: u64, + edge_counter: u64, } - content_hashes - }; + let total_node_count = serialized_graph.nodes.len(); + let total_edge_count = serialized_graph.edge_list_data.len(); + let (total_edge_reads, total_duplicate_edge_reads) = + tcx.dep_graph.edge_deduplication_data(); - let graph = SerializedDepGraph { - nodes, - edge_list_indices, - edge_list_data, - bootstrap_outputs, - hashes: content_hashes, - }; + let mut counts: FxHashMap<_, Stat> = FxHashMap(); - // Encode the graph data. - graph.encode(encoder)?; + for (i, &(node, _)) in serialized_graph.nodes.iter_enumerated() { + let stat = counts.entry(node.kind).or_insert(Stat { + kind: node.kind, + node_counter: 0, + edge_counter: 0, + }); - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: {} nodes in reduced dep-graph", graph.nodes.len()); - eprintln!("incremental: {} edges in serialized dep-graph", graph.edge_list_data.len()); - eprintln!("incremental: {} hashes in serialized dep-graph", graph.hashes.len()); - } - - if tcx.sess.opts.debugging_opts.incremental_dump_hash { - for (dep_node, hash) in &preds.hashes { - println!("ICH for {:?} is {}", dep_node, hash); + stat.node_counter += 1; + let (edge_start, edge_end) = serialized_graph.edge_list_indices[i]; + stat.edge_counter += (edge_end - edge_start) as u64; } - } - Ok(()) -} - -pub fn encode_metadata_hashes(tcx: TyCtxt, - svh: Svh, - metadata_hashes: &EncodedMetadataHashes, - current_metadata_hashes: &mut FxHashMap, - encoder: &mut Encoder) - -> io::Result<()> { - assert_eq!(metadata_hashes.hashes.len(), - metadata_hashes.hashes.iter().map(|x| (x.def_index, ())).collect::>().len()); - - let mut serialized_hashes = SerializedMetadataHashes { - entry_hashes: metadata_hashes.hashes.to_vec(), - index_map: FxHashMap() - }; - - if tcx.sess.opts.debugging_opts.query_dep_graph { - for serialized_hash in &serialized_hashes.entry_hashes { - let def_id = DefId::local(serialized_hash.def_index); - - // Store entry in the index_map - let def_path_hash = tcx.def_path_hash(def_id); - serialized_hashes.index_map.insert(def_id.index, def_path_hash); - - // Record hash in current_metadata_hashes - current_metadata_hashes.insert(def_id, serialized_hash.hash); + let mut counts: Vec<_> = counts.values().cloned().collect(); + counts.sort_by_key(|s| -(s.node_counter as i64)); + + let percentage_of_all_nodes: Vec = counts.iter().map(|s| { + (100.0 * (s.node_counter as f64)) / (total_node_count as f64) + }).collect(); + + let average_edges_per_kind: Vec = counts.iter().map(|s| { + (s.edge_counter as f64) / (s.node_counter as f64) + }).collect(); + + println!("[incremental]"); + println!("[incremental] DepGraph Statistics"); + + const SEPARATOR: &str = "[incremental] --------------------------------\ + ----------------------------------------------\ + ------------"; + + println!("{}", SEPARATOR); + println!("[incremental]"); + println!("[incremental] Total Node Count: {}", total_node_count); + println!("[incremental] Total Edge Count: {}", total_edge_count); + println!("[incremental] Total Edge Reads: {}", total_edge_reads); + println!("[incremental] Total Duplicate Edge Reads: {}", total_duplicate_edge_reads); + println!("[incremental]"); + println!("[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", + "Node Kind", + "Node Frequency", + "Node Count", + "Avg. Edge Count"); + println!("[incremental] -------------------------------------\ + |------------------\ + |-------------\ + |------------------|"); + + for (i, stat) in counts.iter().enumerate() { + println!("[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", + format!("{:?}", stat.kind), + percentage_of_all_nodes[i], + stat.node_counter, + average_edges_per_kind[i]); } - debug!("save: stored index_map (len={}) for serialized hashes", - serialized_hashes.index_map.len()); + println!("{}", SEPARATOR); + println!("[incremental]"); } - // Encode everything. - svh.encode(encoder)?; - serialized_hashes.encode(encoder)?; + serialized_graph.encode(encoder)?; Ok(()) } -pub fn encode_work_products(dep_graph: &DepGraph, - encoder: &mut Encoder) -> io::Result<()> { +fn encode_work_products(dep_graph: &DepGraph, + encoder: &mut Encoder) -> io::Result<()> { let work_products: Vec<_> = dep_graph .work_products() .iter() @@ -353,3 +240,9 @@ pub fn encode_work_products(dep_graph: &DepGraph, work_products.encode(encoder) } + +fn encode_query_cache(tcx: TyCtxt, + encoder: &mut Encoder) + -> io::Result<()> { + tcx.serialize_query_result_cache(encoder) +} diff --git a/src/librustc_incremental/persist/work_product.rs b/src/librustc_incremental/persist/work_product.rs index 70d96e3a83d37..f23b8dc85b8bb 100644 --- a/src/librustc_incremental/persist/work_product.rs +++ b/src/librustc_incremental/persist/work_product.rs @@ -11,9 +11,8 @@ //! This module contains files for saving intermediate work-products. use persist::fs::*; -use rustc::dep_graph::{WorkProduct, WorkProductId, DepGraph}; +use rustc::dep_graph::{WorkProduct, WorkProductId, DepGraph, WorkProductFileKind}; use rustc::session::Session; -use rustc::session::config::OutputType; use rustc::util::fs::link_or_copy; use std::path::PathBuf; use std::fs as std_fs; @@ -21,21 +20,24 @@ use std::fs as std_fs; pub fn save_trans_partition(sess: &Session, dep_graph: &DepGraph, cgu_name: &str, - partition_hash: u64, - files: &[(OutputType, PathBuf)]) { - debug!("save_trans_partition({:?},{},{:?})", + files: &[(WorkProductFileKind, PathBuf)]) { + debug!("save_trans_partition({:?},{:?})", cgu_name, - partition_hash, files); if sess.opts.incremental.is_none() { - return; + return } let work_product_id = WorkProductId::from_cgu_name(cgu_name); let saved_files: Option> = files.iter() .map(|&(kind, ref path)| { - let file_name = format!("cgu-{}.{}", cgu_name, kind.extension()); + let extension = match kind { + WorkProductFileKind::Object => "o", + WorkProductFileKind::Bytecode => "bc", + WorkProductFileKind::BytecodeCompressed => "bc-compressed", + }; + let file_name = format!("cgu-{}.{}", cgu_name, extension); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); match link_or_copy(path, &path_in_incr_dir) { Ok(_) => Some((kind, file_name)), @@ -57,7 +59,6 @@ pub fn save_trans_partition(sess: &Session, let work_product = WorkProduct { cgu_name: cgu_name.to_string(), - input_hash: partition_hash, saved_files, }; diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index c3c5461ff7c50..cebf52d5af7a9 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -12,7 +12,6 @@ test = false [dependencies] log = "0.3" rustc = { path = "../librustc" } -rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index cbc012a65faad..d14a6943fc112 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -13,6 +13,7 @@ use rustc::ty; use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass}; +use syntax::abi::Abi; use syntax::ast; use syntax::attr; use syntax_pos::Span; @@ -22,7 +23,7 @@ use rustc::hir::intravisit::FnKind; #[derive(PartialEq)] pub enum MethodLateContext { - TraitDefaultImpl, + TraitAutoImpl, TraitImpl, PlainImpl, } @@ -31,7 +32,7 @@ pub fn method_context(cx: &LateContext, id: ast::NodeId) -> MethodLateContext { let def_id = cx.tcx.hir.local_def_id(id); let item = cx.tcx.associated_item(def_id); match item.container { - ty::TraitContainer(..) => MethodLateContext::TraitDefaultImpl, + ty::TraitContainer(..) => MethodLateContext::TraitAutoImpl, ty::ImplContainer(cid) => { match cx.tcx.impl_trait_ref(cid) { Some(_) => MethodLateContext::TraitImpl, @@ -244,13 +245,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { MethodLateContext::PlainImpl => { self.check_snake_case(cx, "method", &name.as_str(), Some(span)) } - MethodLateContext::TraitDefaultImpl => { + MethodLateContext::TraitAutoImpl => { self.check_snake_case(cx, "trait method", &name.as_str(), Some(span)) } _ => (), } } - FnKind::ItemFn(name, ..) => { + FnKind::ItemFn(name, _, _, _, abi, _, attrs) => { + // Skip foreign-ABI #[no_mangle] functions (Issue #31924) + if abi != Abi::Rust && attr::find_by_name(attrs, "no_mangle").is_some() { + return; + } self.check_snake_case(cx, "function", &name.as_str(), Some(span)) } FnKind::Closure(_) => (), diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f3bf37c11a546..07874a8cc69dd 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -44,7 +44,7 @@ use std::collections::HashSet; use syntax::ast; use syntax::attr; use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes}; -use syntax_pos::{Span, SyntaxContext}; +use syntax_pos::{BytePos, Span, SyntaxContext}; use syntax::symbol::keywords; use rustc::hir::{self, PatKind}; @@ -55,6 +55,31 @@ use bad_style::{MethodLateContext, method_context}; // hardwired lints from librustc pub use lint::builtin::*; +declare_lint! { + pub AUTO_IMPL, + Deny, + "The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}`" +} + +#[derive(Copy, Clone)] +pub struct AutoImpl; + +impl LintPass for AutoImpl { + fn get_lints(&self) -> LintArray { + lint_array!(AUTO_IMPL) + } +} + +impl EarlyLintPass for AutoImpl { + fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) { + let msg = "The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}`"; + match item.node { + ast::ItemKind::AutoImpl(..) => cx.span_lint(AUTO_IMPL, item.span, msg), + _ => () + } + } +} + declare_lint! { WHILE_TRUE, Warn, @@ -76,9 +101,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WhileTrue { if let hir::ExprLit(ref lit) = cond.node { if let ast::LitKind::Bool(true) = lit.node { if lit.span.ctxt() == SyntaxContext::empty() { - cx.span_lint(WHILE_TRUE, - e.span, - "denote infinite loops with loop { ... }"); + let msg = "denote infinite loops with `loop { ... }`"; + let mut err = cx.struct_span_lint(WHILE_TRUE, e.span, msg); + let condition_span = cx.tcx.sess.codemap().def_span(e.span); + err.span_suggestion_short(condition_span, + "use `loop`", + "loop".to_owned()); + err.emit(); } } } @@ -149,7 +178,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers { declare_lint! { NON_SHORTHAND_FIELD_PATTERNS, Warn, - "using `Struct { x: x }` instead of `Struct { x }`" + "using `Struct { x: x }` instead of `Struct { x }` in a pattern" } #[derive(Copy, Clone)] @@ -170,11 +199,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns { } if let PatKind::Binding(_, _, ident, None) = fieldpat.node.pat.node { if ident.node == fieldpat.node.name { - cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, + let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, - &format!("the `{}:` in this pattern is redundant and can \ - be removed", - ident.node)) + &format!("the `{}:` in this pattern is redundant", + ident.node)); + let subspan = cx.tcx.sess.codemap().span_through_char(fieldpat.span, ':'); + err.span_suggestion_short(subspan, + "remove this", + format!("{}", ident.node)); + err.emit(); } } } @@ -220,7 +253,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { - hir::ItemTrait(hir::Unsafety::Unsafe, ..) => { + hir::ItemTrait(_, hir::Unsafety::Unsafe, ..) => { self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait") } @@ -613,12 +646,6 @@ impl EarlyLintPass for AnonymousParameters { } } -declare_lint! { - DEPRECATED_ATTR, - Warn, - "detects use of deprecated attributes" -} - /// Checks for use of attributes which have been deprecated. #[derive(Clone)] pub struct DeprecatedAttr { @@ -637,7 +664,7 @@ impl DeprecatedAttr { impl LintPass for DeprecatedAttr { fn get_lints(&self) -> LintArray { - lint_array!(DEPRECATED_ATTR) + lint_array!() } } @@ -650,10 +677,11 @@ impl EarlyLintPass for DeprecatedAttr { ref name, ref reason, _) = g { - cx.span_lint(DEPRECATED, - attr.span, - &format!("use of deprecated attribute `{}`: {}. See {}", - name, reason, link)); + let msg = format!("use of deprecated attribute `{}`: {}. See {}", + name, reason, link); + let mut err = cx.struct_span_lint(DEPRECATED, attr.span, &msg); + err.span_suggestion_short(attr.span, "remove this attribute", "".to_owned()); + err.emit(); } return; } @@ -889,7 +917,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, sp, "function cannot return without recurring"); - // FIXME #19668: these could be span_lint_note's instead of this manual guard. // offer some help to the programmer. for call in &self_call_spans { db.span_note(*call, "recursive call site"); @@ -1125,35 +1152,55 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { hir::ItemFn(.., ref generics, _) => { - if attr::contains_name(&it.attrs, "no_mangle") && - !attr::contains_name(&it.attrs, "linkage") { + if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, "no_mangle") { + if attr::contains_name(&it.attrs, "linkage") { + return; + } if !cx.access_levels.is_reachable(it.id) { - let msg = format!("function {} is marked #[no_mangle], but not exported", - it.name); - cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg); + let msg = "function is marked #[no_mangle], but not exported"; + let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_FNS, it.span, msg); + let insertion_span = it.span.with_hi(it.span.lo()); + err.span_suggestion(insertion_span, + "try making it public", + "pub ".to_owned()); + err.emit(); } if generics.is_type_parameterized() { - cx.span_lint(NO_MANGLE_GENERIC_ITEMS, - it.span, - "functions generic over types must be mangled"); + let mut err = cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, + it.span, + "functions generic over \ + types must be mangled"); + err.span_suggestion_short(no_mangle_attr.span, + "remove this attribute", + "".to_owned()); + err.emit(); } } } hir::ItemStatic(..) => { if attr::contains_name(&it.attrs, "no_mangle") && !cx.access_levels.is_reachable(it.id) { - let msg = format!("static {} is marked #[no_mangle], but not exported", - it.name); - cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg); + let msg = "static is marked #[no_mangle], but not exported"; + let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, msg); + let insertion_span = it.span.with_hi(it.span.lo()); + err.span_suggestion(insertion_span, + "try making it public", + "pub ".to_owned()); + err.emit(); } } hir::ItemConst(..) => { if attr::contains_name(&it.attrs, "no_mangle") { // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to - let msg = "const items should never be #[no_mangle], consider instead using \ - `pub static`"; - cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); + let msg = "const items should never be #[no_mangle]"; + let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); + // `const` is 5 chars + let const_span = it.span.with_hi(BytePos(it.span.lo().0 + 5)); + err.span_suggestion(const_span, + "try a static value", + "pub static".to_owned()); + err.emit(); } } _ => {} @@ -1279,3 +1326,60 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields { } } } + +/// Lint for items marked `pub` that aren't reachable from other crates +pub struct UnreachablePub; + +declare_lint! { + UNREACHABLE_PUB, + Allow, + "`pub` items not reachable from crate root" +} + +impl LintPass for UnreachablePub { + fn get_lints(&self) -> LintArray { + lint_array!(UNREACHABLE_PUB) + } +} + +impl UnreachablePub { + fn perform_lint(&self, cx: &LateContext, what: &str, id: ast::NodeId, + vis: &hir::Visibility, span: Span, exportable: bool) { + if !cx.access_levels.is_reachable(id) && *vis == hir::Visibility::Public { + let def_span = cx.tcx.sess.codemap().def_span(span); + let mut err = cx.struct_span_lint(UNREACHABLE_PUB, def_span, + &format!("unreachable `pub` {}", what)); + // visibility is token at start of declaration (can be macro + // variable rather than literal `pub`) + let pub_span = cx.tcx.sess.codemap().span_until_char(def_span, ' '); + let replacement = if cx.tcx.sess.features.borrow().crate_visibility_modifier { + "crate" + } else { + "pub(crate)" + }.to_owned(); + err.span_suggestion(pub_span, "consider restricting its visibility", replacement); + if exportable { + err.help("or consider exporting it for use by other crates"); + } + err.emit(); + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub { + fn check_item(&mut self, cx: &LateContext, item: &hir::Item) { + self.perform_lint(cx, "item", item.id, &item.vis, item.span, true); + } + + fn check_foreign_item(&mut self, cx: &LateContext, foreign_item: &hir::ForeignItem) { + self.perform_lint(cx, "item", foreign_item.id, &foreign_item.vis, foreign_item.span, true); + } + + fn check_struct_field(&mut self, cx: &LateContext, field: &hir::StructField) { + self.perform_lint(cx, "field", field.id, &field.vis, field.span, false); + } + + fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) { + self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false); + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index fbf993f45576c..fc05f8f0dc245 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -28,6 +28,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(i128_type)] +#![feature(macro_vis_matcher)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] @@ -38,7 +39,6 @@ extern crate syntax; extern crate rustc; #[macro_use] extern crate log; -extern crate rustc_back; extern crate rustc_const_eval; extern crate syntax_pos; @@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { AnonymousParameters, IllegalFloatLiteralPattern, UnusedDocComment, + AutoImpl, ); add_early_builtin_with_new!(sess, @@ -129,7 +130,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { NonUpperCaseGlobals, NonShorthandFieldPatterns, UnsafeCode, - UnusedMut, UnusedAllocation, MissingCopyImplementations, UnstableFeatures, @@ -138,6 +138,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { PluginAsLibrary, MutableTransmutes, UnionsWithDropFields, + UnreachablePub, ); add_builtin_with_new!(sess, @@ -165,7 +166,12 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES, - UNUSED_MACROS); + UNUSED_MACROS, + UNUSED_ALLOCATION, + UNUSED_DOC_COMMENT, + UNUSED_EXTERN_CRATES, + UNUSED_FEATURES, + UNUSED_PARENS); // Guidelines for creating a future incompatibility lint: // @@ -177,6 +183,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { // - Eventually, remove lint store.register_future_incompatible(sess, vec![ + FutureIncompatibleInfo { + id: LintId::of(AUTO_IMPL), + reference: "issue #13231 ", + }, FutureIncompatibleInfo { id: LintId::of(PRIVATE_IN_PUBLIC), reference: "issue #34537 ", @@ -197,10 +207,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(INVALID_TYPE_PARAM_DEFAULT), reference: "issue #36887 ", }, - FutureIncompatibleInfo { - id: LintId::of(EXTRA_REQUIREMENT_IN_IMPL), - reference: "issue #37166 ", - }, FutureIncompatibleInfo { id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), reference: "issue #37872 ", @@ -237,19 +243,28 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS), reference: "issue #42868 ", }, + FutureIncompatibleInfo { + id: LintId::of(SAFE_PACKED_BORROWS), + reference: "issue #46043 ", + }, + FutureIncompatibleInfo { + id: LintId::of(COERCE_NEVER), + reference: "issue #46325 ", + }, + ]); // Register renamed and removed lints store.register_renamed("unknown_features", "unused_features"); - store.register_removed("unsigned_negation", - "replaced by negate_unsigned feature gate"); + store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); // This was renamed to raw_pointer_derive, which was then removed, // so it is also considered removed - store.register_removed("raw_pointer_deriving", - "using derive with raw pointers is ok"); + store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok"); store.register_removed("drop_with_repr_extern", "drop flags have been removed"); + store.register_removed("fat_ptr_transmutes", "was accidentally removed back in 2014"); + store.register_removed("deprecated_attr", "use `deprecated` instead"); store.register_removed("transmute_from_fn_item_types", "always cast functions before transmuting them"); store.register_removed("hr_lifetime_in_assoc_type", @@ -266,4 +281,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { "converted into hard error, see https://github.com/rust-lang/rust/issues/36891"); store.register_removed("lifetime_underscore", "converted into hard error, see https://github.com/rust-lang/rust/issues/36892"); + store.register_removed("extra_requirement_in_impl", + "converted into hard error, see https://github.com/rust-lang/rust/issues/37166"); } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index d3a5d52b295af..1356574f646aa 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -13,7 +13,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, AdtKind, Ty, TyCtxt}; -use rustc::ty::layout::{Layout, Primitive}; +use rustc::ty::layout::{self, LayoutOf}; use middle::const_val::ConstVal; use rustc_const_eval::ConstContext; use util::nodemap::FxHashSet; @@ -431,7 +431,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // fields are actually safe. let mut all_phantom = true; for field in &def.struct_variant().fields { - let field_ty = cx.normalize_associated_type(&field.ty(cx, substs)); + let field_ty = cx.fully_normalize_associated_types_in( + &field.ty(cx, substs) + ); let r = self.check_type_for_ffi(cache, field_ty); match r { FfiSafe => { @@ -463,7 +465,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut all_phantom = true; for field in &def.struct_variant().fields { - let field_ty = cx.normalize_associated_type(&field.ty(cx, substs)); + let field_ty = cx.fully_normalize_associated_types_in( + &field.ty(cx, substs) + ); let r = self.check_type_for_ffi(cache, field_ty); match r { FfiSafe => { @@ -516,7 +520,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Check the contained variants. for variant in &def.variants { for field in &variant.fields { - let arg = cx.normalize_associated_type(&field.ty(cx, substs)); + let arg = cx.fully_normalize_associated_types_in( + &field.ty(cx, substs) + ); let r = self.check_type_for_ffi(cache, arg); match r { FfiSafe => {} @@ -615,6 +621,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } + ty::TyForeign(..) => FfiSafe, + ty::TyParam(..) | ty::TyInfer(..) | ty::TyError | @@ -629,7 +637,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) { // it is only OK to use this function because extern fns cannot have // any generic types right now: - let ty = self.cx.tcx.normalize_associated_type(&ty); + let ty = self.cx.tcx.fully_normalize_associated_types_in(&ty); match self.check_type_for_ffi(&mut FxHashSet(), ty) { FfiResult::FfiSafe => {} @@ -717,6 +725,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes { hir::ForeignItemStatic(ref ty, _) => { vis.check_foreign_static(ni.id, ty.span); } + hir::ForeignItemType => () } } } @@ -739,25 +748,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { // sizes only make sense for non-generic types let item_def_id = cx.tcx.hir.local_def_id(it.id); let t = cx.tcx.type_of(item_def_id); - let param_env = cx.param_env.reveal_all(); let ty = cx.tcx.erase_regions(&t); - let layout = ty.layout(cx.tcx, param_env).unwrap_or_else(|e| { + let layout = cx.layout_of(ty).unwrap_or_else(|e| { bug!("failed to get layout for `{}`: {}", t, e) }); - if let Layout::General { ref variants, ref size, discr, .. } = *layout { - let discr_size = Primitive::Int(discr).size(cx.tcx).bytes(); + if let layout::Variants::Tagged { ref variants, ref discr, .. } = layout.variants { + let discr_size = discr.value.size(cx.tcx).bytes(); debug!("enum `{}` is {} bytes large with layout:\n{:#?}", - t, size.bytes(), layout); + t, layout.size.bytes(), layout); let (largest, slargest, largest_index) = enum_definition.variants .iter() .zip(variants) .map(|(variant, variant_layout)| { // Subtract the size of the enum discriminant - let bytes = variant_layout.min_size - .bytes() + let bytes = variant_layout.size.bytes() .saturating_sub(discr_size); debug!("- variant `{}` is {} bytes large", variant.node.name, bytes); diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index b97920dd18b77..4e066ecf999e3 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -11,100 +11,18 @@ use rustc::hir::def_id::DefId; use rustc::ty; use rustc::ty::adjustment; -use util::nodemap::FxHashMap; use lint::{LateContext, EarlyContext, LintContext, LintArray}; use lint::{LintPass, EarlyLintPass, LateLintPass}; -use std::collections::hash_map::Entry::{Occupied, Vacant}; - use syntax::ast; use syntax::attr; use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType}; +use syntax::print::pprust; use syntax::symbol::keywords; -use syntax::ptr::P; use syntax::util::parser; use syntax_pos::Span; -use rustc_back::slice; use rustc::hir; -use rustc::hir::intravisit::FnKind; - -declare_lint! { - pub UNUSED_MUT, - Warn, - "detect mut variables which don't need to be mutable" -} - -#[derive(Copy, Clone)] -pub struct UnusedMut; - -impl UnusedMut { - fn check_unused_mut_pat(&self, cx: &LateContext, pats: &[P]) { - // collect all mutable pattern and group their NodeIDs by their Identifier to - // avoid false warnings in match arms with multiple patterns - - let mut mutables = FxHashMap(); - for p in pats { - p.each_binding(|_, id, span, path1| { - let hir_id = cx.tcx.hir.node_to_hir_id(id); - let bm = match cx.tables.pat_binding_modes().get(hir_id) { - Some(&bm) => bm, - None => span_bug!(span, "missing binding mode"), - }; - let name = path1.node; - if let ty::BindByValue(hir::MutMutable) = bm { - if !name.as_str().starts_with("_") { - match mutables.entry(name) { - Vacant(entry) => { - entry.insert(vec![id]); - } - Occupied(mut entry) => { - entry.get_mut().push(id); - } - } - } - } - }); - } - - let used_mutables = cx.tcx.used_mut_nodes.borrow(); - for (_, v) in &mutables { - if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(UNUSED_MUT, - cx.tcx.hir.span(v[0]), - "variable does not need to be mutable"); - } - } - } -} - -impl LintPass for UnusedMut { - fn get_lints(&self) -> LintArray { - lint_array!(UNUSED_MUT) - } -} - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedMut { - fn check_arm(&mut self, cx: &LateContext, a: &hir::Arm) { - self.check_unused_mut_pat(cx, &a.pats) - } - - fn check_local(&mut self, cx: &LateContext, l: &hir::Local) { - self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat)); - } - - fn check_fn(&mut self, - cx: &LateContext, - _: FnKind, - _: &hir::FnDecl, - body: &hir::Body, - _: Span, - _: ast::NodeId) { - for a in &body.arguments { - self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat)); - } - } -} declare_lint! { pub UNUSED_MUST_USE, @@ -307,7 +225,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { } declare_lint! { - UNUSED_PARENS, + pub(super) UNUSED_PARENS, Warn, "`if`, `match`, `while` and `return` do not need parentheses" } @@ -325,9 +243,40 @@ impl UnusedParens { let necessary = struct_lit_needs_parens && parser::contains_exterior_struct_lit(&inner); if !necessary { - cx.span_lint(UNUSED_PARENS, - value.span, - &format!("unnecessary parentheses around {}", msg)) + let span_msg = format!("unnecessary parentheses around {}", msg); + let mut err = cx.struct_span_lint(UNUSED_PARENS, + value.span, + &span_msg); + // Remove exactly one pair of parentheses (rather than naïvely + // stripping all paren characters) + let mut ate_left_paren = false; + let mut ate_right_paren = false; + let parens_removed = pprust::expr_to_string(value) + .trim_matches(|c| { + match c { + '(' => { + if ate_left_paren { + false + } else { + ate_left_paren = true; + true + } + }, + ')' => { + if ate_right_paren { + false + } else { + ate_right_paren = true; + true + } + }, + _ => false, + } + }).to_owned(); + err.span_suggestion_short(value.span, + "remove these parentheses", + parens_removed); + err.emit(); } } } @@ -381,6 +330,43 @@ declare_lint! { #[derive(Copy, Clone)] pub struct UnusedImportBraces; +impl UnusedImportBraces { + fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) { + if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { + // Recursively check nested UseTrees + for &(ref tree, _) in items { + self.check_use_tree(cx, tree, item); + } + + // Trigger the lint only if there is one nested item + if items.len() != 1 { + return; + } + + // Trigger the lint if the nested item is a non-self single item + let node_ident; + match items[0].0.kind { + ast::UseTreeKind::Simple(ident) => { + if ident.name == keywords::SelfValue.name() { + return; + } else { + node_ident = ident; + } + } + ast::UseTreeKind::Glob => { + node_ident = ast::Ident::from_str("*"); + } + ast::UseTreeKind::Nested(_) => { + return; + } + } + + let msg = format!("braces around {} is unnecessary", node_ident.name); + cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); + } + } +} + impl LintPass for UnusedImportBraces { fn get_lints(&self) -> LintArray { lint_array!(UNUSED_IMPORT_BRACES) @@ -389,19 +375,14 @@ impl LintPass for UnusedImportBraces { impl EarlyLintPass for UnusedImportBraces { fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) { - if let ast::ItemKind::Use(ref view_path) = item.node { - if let ast::ViewPathList(_, ref items) = view_path.node { - if items.len() == 1 && items[0].node.name.name != keywords::SelfValue.name() { - let msg = format!("braces around {} is unnecessary", items[0].node.name); - cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); - } - } + if let ast::ItemKind::Use(ref use_tree) = item.node { + self.check_use_tree(cx, use_tree, item); } } } declare_lint! { - UNUSED_ALLOCATION, + pub(super) UNUSED_ALLOCATION, Warn, "detects unnecessary allocations that can be eliminated" } diff --git a/src/librustc_llvm/Cargo.toml b/src/librustc_llvm/Cargo.toml index de5add56b761d..a9566c4bcacd7 100644 --- a/src/librustc_llvm/Cargo.toml +++ b/src/librustc_llvm/Cargo.toml @@ -18,4 +18,4 @@ rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } [build-dependencies] build_helper = { path = "../build_helper" } -cc = "1.0" +cc = "1.0.1" diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index dde7a38efc796..47ca30db0c20a 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -17,27 +17,14 @@ use std::path::{PathBuf, Path}; use build_helper::output; -fn detect_llvm_link(major: u32, minor: u32, llvm_config: &Path) - -> (&'static str, Option<&'static str>) { - if major > 3 || (major == 3 && minor >= 9) { - // Force the link mode we want, preferring static by default, but - // possibly overridden by `configure --enable-llvm-link-shared`. - if env::var_os("LLVM_LINK_SHARED").is_some() { - return ("dylib", Some("--link-shared")); - } else { - return ("static", Some("--link-static")); - } - } else if major == 3 && minor == 8 { - // Find out LLVM's default linking mode. - let mut mode_cmd = Command::new(llvm_config); - mode_cmd.arg("--shared-mode"); - if output(&mut mode_cmd).trim() == "shared" { - return ("dylib", None); - } else { - return ("static", None); - } +fn detect_llvm_link() -> (&'static str, &'static str) { + // Force the link mode we want, preferring static by default, but + // possibly overridden by `configure --enable-llvm-link-shared`. + if env::var_os("LLVM_LINK_SHARED").is_some() { + ("dylib", "--link-shared") + } else { + ("static", "--link-static") } - ("static", None) } fn main() { @@ -88,7 +75,7 @@ fn main() { let is_crossed = target != host; let mut optional_components = - vec!["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", + vec!["x86", "arm", "aarch64", "mips", "powerpc", "systemz", "jsbackend", "webassembly", "msp430", "sparc", "nvptx"]; let mut version_cmd = Command::new(&llvm_config); @@ -96,11 +83,11 @@ fn main() { let version_output = output(&mut version_cmd); let mut parts = version_output.split('.').take(2) .filter_map(|s| s.parse::().ok()); - let (major, minor) = + let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { (major, minor) } else { - (3, 7) + (3, 9) }; if major > 3 { @@ -115,6 +102,7 @@ fn main() { "linker", "asmparser", "mcjit", + "lto", "interpreter", "instrumentation"]; @@ -153,13 +141,13 @@ fn main() { } for component in &components { - let mut flag = String::from("-DLLVM_COMPONENT_"); + let mut flag = String::from("LLVM_COMPONENT_"); flag.push_str(&component.to_uppercase()); - cfg.flag(&flag); + cfg.define(&flag, None); } if env::var_os("LLVM_RUSTLLVM").is_some() { - cfg.flag("-DLLVM_RUSTLLVM"); + cfg.define("LLVM_RUSTLLVM", None); } build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); @@ -168,19 +156,15 @@ fn main() { .file("../rustllvm/ArchiveWrapper.cpp") .cpp(true) .cpp_link_stdlib(None) // we handle this below - .compile("librustllvm.a"); + .compile("rustllvm"); - let (llvm_kind, llvm_link_arg) = detect_llvm_link(major, minor, &llvm_config); + let (llvm_kind, llvm_link_arg) = detect_llvm_link(); // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then // we don't pick up system libs because unfortunately they're for the host // of llvm-config, not the target that we're attempting to link. let mut cmd = Command::new(&llvm_config); - cmd.arg("--libs"); - - if let Some(link_arg) = llvm_link_arg { - cmd.arg(link_arg); - } + cmd.arg(llvm_link_arg).arg("--libs"); if !is_crossed { cmd.arg("--system-libs"); @@ -229,10 +213,7 @@ fn main() { // hack around this by replacing the host triple with the target and pray // that those -L directories are the same! let mut cmd = Command::new(&llvm_config); - if let Some(link_arg) = llvm_link_arg { - cmd.arg(link_arg); - } - cmd.arg("--ldflags"); + cmd.arg(llvm_link_arg).arg("--ldflags"); for lib in output(&mut cmd).split_whitespace() { if lib.starts_with("-LIBPATH:") { println!("cargo:rustc-link-search=native={}", &lib[9..]); @@ -251,8 +232,8 @@ fn main() { let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP"); let stdcppname = if target.contains("openbsd") { - // OpenBSD has a particular C++ runtime library name - "estdc++" + // llvm-config on OpenBSD doesn't mention stdlib=libc++ + "c++" } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { // NetBSD uses a separate library when relocation is required "stdc++_pic" diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index d91c706e24e36..1c2fa1bbb4848 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -345,6 +345,31 @@ pub enum PassKind { Module, } +/// LLVMRustThinLTOData +pub enum ThinLTOData {} + +/// LLVMRustThinLTOBuffer +pub enum ThinLTOBuffer {} + +/// LLVMRustThinLTOModule +#[repr(C)] +pub struct ThinLTOModule { + pub identifier: *const c_char, + pub data: *const u8, + pub len: usize, +} + +/// LLVMThreadLocalMode +#[derive(Copy, Clone)] +#[repr(C)] +pub enum ThreadLocalMode { + NotThreadLocal, + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec +} + // Opaque pointer types #[allow(missing_copy_implementations)] pub enum Module_opaque {} @@ -478,18 +503,15 @@ pub mod debuginfo { } } +pub enum ModuleBuffer {} -// Link to our native llvm bindings (things that we need to use the C++ api -// for) and because llvm is written in C++ we need to link against libstdc++ -// -// You'll probably notice that there is an omission of all LLVM libraries -// from this location. This is because the set of LLVM libraries that we -// link to is mostly defined by LLVM, and the `llvm-config` tool is used to -// figure out the exact set of libraries. To do this, the build system -// generates an llvmdeps.rs file next to this one which will be -// automatically updated whenever LLVM is updated to include an up-to-date -// set of the libraries we need to link to LLVM for. -#[link(name = "rustllvm", kind = "static")] // not quite true but good enough +// This annotation is primarily needed for MSVC where attributes like +// dllimport/dllexport are applied and need to be correct for everything to +// link successfully. The #[link] annotation here says "these symbols are +// included statically" which means that they're all exported with dllexport +// and from the rustc_llvm dynamic library. Otherwise the rustc_trans dynamic +// library would not be able to access these symbols. +#[link(name = "rustllvm", kind = "static")] extern "C" { // Create and destroy contexts. pub fn LLVMContextCreate() -> ContextRef; @@ -549,8 +571,6 @@ extern "C" { ElementCount: c_uint, Packed: Bool) -> TypeRef; - pub fn LLVMCountStructElementTypes(StructTy: TypeRef) -> c_uint; - pub fn LLVMGetStructElementTypes(StructTy: TypeRef, Dest: *mut TypeRef); pub fn LLVMIsPackedStruct(StructTy: TypeRef) -> Bool; // Operations on array, pointer, and vector types (sequence types) @@ -559,11 +579,11 @@ extern "C" { pub fn LLVMVectorType(ElementType: TypeRef, ElementCount: c_uint) -> TypeRef; pub fn LLVMGetElementType(Ty: TypeRef) -> TypeRef; - pub fn LLVMGetArrayLength(ArrayTy: TypeRef) -> c_uint; pub fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint; // Operations on other types pub fn LLVMVoidTypeInContext(C: ContextRef) -> TypeRef; + pub fn LLVMX86MMXTypeInContext(C: ContextRef) -> TypeRef; pub fn LLVMRustMetadataTypeInContext(C: ContextRef) -> TypeRef; // Operations on all values @@ -585,10 +605,7 @@ extern "C" { pub fn LLVMConstNull(Ty: TypeRef) -> ValueRef; pub fn LLVMConstICmp(Pred: IntPredicate, V1: ValueRef, V2: ValueRef) -> ValueRef; pub fn LLVMConstFCmp(Pred: RealPredicate, V1: ValueRef, V2: ValueRef) -> ValueRef; - // only for isize/vector pub fn LLVMGetUndef(Ty: TypeRef) -> ValueRef; - pub fn LLVMIsNull(Val: ValueRef) -> Bool; - pub fn LLVMIsUndef(Val: ValueRef) -> Bool; // Operations on metadata pub fn LLVMMDStringInContext(C: ContextRef, Str: *const c_char, SLen: c_uint) -> ValueRef; @@ -694,6 +711,7 @@ extern "C" { pub fn LLVMGetInitializer(GlobalVar: ValueRef) -> ValueRef; pub fn LLVMSetInitializer(GlobalVar: ValueRef, ConstantVal: ValueRef); pub fn LLVMSetThreadLocal(GlobalVar: ValueRef, IsThreadLocal: Bool); + pub fn LLVMSetThreadLocalMode(GlobalVar: ValueRef, Mode: ThreadLocalMode); pub fn LLVMIsGlobalConstant(GlobalVar: ValueRef) -> Bool; pub fn LLVMSetGlobalConstant(GlobalVar: ValueRef, IsConstant: Bool); pub fn LLVMRustGetNamedValue(M: ModuleRef, Name: *const c_char) -> ValueRef; @@ -709,7 +727,9 @@ extern "C" { FunctionTy: TypeRef) -> ValueRef; pub fn LLVMSetFunctionCallConv(Fn: ValueRef, CC: c_uint); + pub fn LLVMRustAddAlignmentAttr(Fn: ValueRef, index: c_uint, bytes: u32); pub fn LLVMRustAddDereferenceableAttr(Fn: ValueRef, index: c_uint, bytes: u64); + pub fn LLVMRustAddDereferenceableOrNullAttr(Fn: ValueRef, index: c_uint, bytes: u64); pub fn LLVMRustAddFunctionAttribute(Fn: ValueRef, index: c_uint, attr: Attribute); pub fn LLVMRustAddFunctionAttrStringValue(Fn: ValueRef, index: c_uint, @@ -739,7 +759,11 @@ extern "C" { // Operations on call sites pub fn LLVMSetInstructionCallConv(Instr: ValueRef, CC: c_uint); pub fn LLVMRustAddCallSiteAttribute(Instr: ValueRef, index: c_uint, attr: Attribute); + pub fn LLVMRustAddAlignmentCallSiteAttr(Instr: ValueRef, index: c_uint, bytes: u32); pub fn LLVMRustAddDereferenceableCallSiteAttr(Instr: ValueRef, index: c_uint, bytes: u64); + pub fn LLVMRustAddDereferenceableOrNullCallSiteAttr(Instr: ValueRef, + index: c_uint, + bytes: u64); // Operations on load/store instructions (only) pub fn LLVMSetVolatile(MemoryAccessInst: ValueRef, volatile: Bool); @@ -1178,15 +1202,13 @@ extern "C" { pub fn LLVMRustBuildAtomicLoad(B: BuilderRef, PointerVal: ValueRef, Name: *const c_char, - Order: AtomicOrdering, - Alignment: c_uint) + Order: AtomicOrdering) -> ValueRef; pub fn LLVMRustBuildAtomicStore(B: BuilderRef, Val: ValueRef, Ptr: ValueRef, - Order: AtomicOrdering, - Alignment: c_uint) + Order: AtomicOrdering) -> ValueRef; pub fn LLVMRustBuildAtomicCmpXchg(B: BuilderRef, @@ -1220,23 +1242,6 @@ extern "C" { /// Creates target data from a target layout string. pub fn LLVMCreateTargetData(StringRep: *const c_char) -> TargetDataRef; - /// Number of bytes clobbered when doing a Store to *T. - pub fn LLVMSizeOfTypeInBits(TD: TargetDataRef, Ty: TypeRef) -> c_ulonglong; - - /// Distance between successive elements in an array of T. Includes ABI padding. - pub fn LLVMABISizeOfType(TD: TargetDataRef, Ty: TypeRef) -> c_ulonglong; - - /// Returns the preferred alignment of a type. - pub fn LLVMPreferredAlignmentOfType(TD: TargetDataRef, Ty: TypeRef) -> c_uint; - /// Returns the minimum alignment of a type. - pub fn LLVMABIAlignmentOfType(TD: TargetDataRef, Ty: TypeRef) -> c_uint; - - /// Computes the byte offset of the indexed struct element for a - /// target. - pub fn LLVMOffsetOfElement(TD: TargetDataRef, - StructTy: TypeRef, - Element: c_uint) - -> c_ulonglong; /// Disposes target data. pub fn LLVMDisposeTargetData(TD: TargetDataRef); @@ -1270,6 +1275,9 @@ extern "C" { PM: PassManagerRef, Internalize: Bool, RunInliner: Bool); + pub fn LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + PMB: PassManagerBuilderRef, + PM: PassManagerRef) -> bool; // Stuff that's in rustllvm/ because it's not upstream yet. @@ -1311,11 +1319,6 @@ extern "C" { ElementCount: c_uint, Packed: Bool); - pub fn LLVMConstNamedStruct(S: TypeRef, - ConstantVals: *const ValueRef, - Count: c_uint) - -> ValueRef; - /// Enables LLVM debug output. pub fn LLVMRustSetDebug(Enabled: c_int); @@ -1575,7 +1578,9 @@ extern "C" { UseSoftFP: bool, PositionIndependentExecutable: bool, FunctionSections: bool, - DataSections: bool) + DataSections: bool, + TrapUnreachable: bool, + Singlethread: bool) -> TargetMachineRef; pub fn LLVMRustDisposeTargetMachine(T: TargetMachineRef); pub fn LLVMRustAddAnalysisPasses(T: TargetMachineRef, PM: PassManagerRef, M: ModuleRef); @@ -1609,6 +1614,7 @@ extern "C" { pub fn LLVMRustSetNormalizedTarget(M: ModuleRef, triple: *const c_char); pub fn LLVMRustAddAlwaysInlinePass(P: PassManagerBuilderRef, AddLifetimes: bool); pub fn LLVMRustLinkInExternalBitcode(M: ModuleRef, bc: *const c_char, len: size_t) -> bool; + pub fn LLVMRustLinkInParsedExternalBitcode(M: ModuleRef, M: ModuleRef) -> bool; pub fn LLVMRustRunRestrictionPass(M: ModuleRef, syms: *const *const c_char, len: size_t); pub fn LLVMRustMarkAllFunctionsNounwind(M: ModuleRef); @@ -1678,4 +1684,48 @@ extern "C" { pub fn LLVMRustSetComdat(M: ModuleRef, V: ValueRef, Name: *const c_char); pub fn LLVMRustUnsetComdat(V: ValueRef); pub fn LLVMRustSetModulePIELevel(M: ModuleRef); + pub fn LLVMRustModuleBufferCreate(M: ModuleRef) -> *mut ModuleBuffer; + pub fn LLVMRustModuleBufferPtr(p: *const ModuleBuffer) -> *const u8; + pub fn LLVMRustModuleBufferLen(p: *const ModuleBuffer) -> usize; + pub fn LLVMRustModuleBufferFree(p: *mut ModuleBuffer); + pub fn LLVMRustModuleCost(M: ModuleRef) -> u64; + + pub fn LLVMRustThinLTOAvailable() -> bool; + pub fn LLVMRustWriteThinBitcodeToFile(PMR: PassManagerRef, + M: ModuleRef, + BC: *const c_char) -> bool; + pub fn LLVMRustThinLTOBufferCreate(M: ModuleRef) -> *mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferFree(M: *mut ThinLTOBuffer); + pub fn LLVMRustThinLTOBufferPtr(M: *const ThinLTOBuffer) -> *const c_char; + pub fn LLVMRustThinLTOBufferLen(M: *const ThinLTOBuffer) -> size_t; + pub fn LLVMRustCreateThinLTOData( + Modules: *const ThinLTOModule, + NumModules: c_uint, + PreservedSymbols: *const *const c_char, + PreservedSymbolsLen: c_uint, + ) -> *mut ThinLTOData; + pub fn LLVMRustPrepareThinLTORename( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOResolveWeak( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOInternalize( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOImport( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustFreeThinLTOData(Data: *mut ThinLTOData); + pub fn LLVMRustParseBitcodeForThinLTO( + Context: ContextRef, + Data: *const u8, + len: usize, + Identifier: *const c_char, + ) -> ModuleRef; + pub fn LLVMGetModuleIdentifier(M: ModuleRef, size: *mut usize) -> *const c_char; } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 9be5f5b54867b..592bd62056455 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -74,22 +74,19 @@ pub fn AddFunctionAttrStringValue(llfn: ValueRef, } } -#[repr(C)] #[derive(Copy, Clone)] pub enum AttributePlace { + ReturnValue, Argument(u32), Function, } impl AttributePlace { - pub fn ReturnValue() -> Self { - AttributePlace::Argument(0) - } - pub fn as_uint(self) -> c_uint { match self { + AttributePlace::ReturnValue => 0, + AttributePlace::Argument(i) => 1 + i, AttributePlace::Function => !0, - AttributePlace::Argument(i) => i, } } } @@ -172,6 +169,11 @@ pub fn set_thread_local(global: ValueRef, is_thread_local: bool) { LLVMSetThreadLocal(global, is_thread_local as Bool); } } +pub fn set_thread_local_mode(global: ValueRef, mode: ThreadLocalMode) { + unsafe { + LLVMSetThreadLocalMode(global, mode); + } +} impl Attribute { pub fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { @@ -346,10 +348,6 @@ pub fn initialize_available_targets() { LLVMInitializePowerPCTargetMC, LLVMInitializePowerPCAsmPrinter, LLVMInitializePowerPCAsmParser); - init_target!(llvm_component = "pnacl", - LLVMInitializePNaClTargetInfo, - LLVMInitializePNaClTarget, - LLVMInitializePNaClTargetMC); init_target!(llvm_component = "systemz", LLVMInitializeSystemZTargetInfo, LLVMInitializeSystemZTarget, diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index d9ab2562efff2..722d0cad238f4 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -56,7 +56,8 @@ impl<'a, 'b, 'tcx> IsolatedEncoder<'a, 'b, 'tcx> { }; let lazy_body = self.lazy(body); - let tables = self.tcx.body_tables(body_id); + let body_owner_def_id = self.tcx.hir.body_owner_def_id(body_id); + let tables = self.tcx.typeck_tables_of(body_owner_def_id); let lazy_tables = self.lazy(tables); let mut visitor = NestedBodyCollector { @@ -67,7 +68,7 @@ impl<'a, 'b, 'tcx> IsolatedEncoder<'a, 'b, 'tcx> { let lazy_nested_bodies = self.lazy_seq_ref_from_slice(&visitor.bodies_found); let rvalue_promotable_to_static = - self.tcx.rvalue_promotable_to_static.borrow()[&body.value.id]; + self.tcx.const_is_rvalue_promotable_to_static(body_owner_def_id); self.lazy(&Ast { body: lazy_body, diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 39bdf88925e44..e1c5cde42eccf 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -19,7 +19,7 @@ use rustc::hir::def_id::{CrateNum, DefIndex, CRATE_DEF_INDEX}; use rustc::hir::svh::Svh; use rustc::middle::allocator::AllocatorKind; use rustc::middle::cstore::DepKind; -use rustc::session::Session; +use rustc::session::{Session, CrateDisambiguator}; use rustc::session::config::{Sanitizer, self}; use rustc_back::PanicStrategy; use rustc::session::search_paths::PathKind; @@ -258,14 +258,15 @@ impl<'a> CrateLoader<'a> { let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span, dep_kind); let def_path_table = record_time(&self.sess.perf_stats.decode_def_path_tables_time, || { - crate_root.def_path_table.decode(&metadata) + crate_root.def_path_table.decode((&metadata, self.sess)) }); - let exported_symbols = crate_root.exported_symbols.decode(&metadata).collect(); - + let exported_symbols = crate_root.exported_symbols + .decode((&metadata, self.sess)) + .collect(); let trait_impls = crate_root .impls - .decode(&metadata) + .decode((&metadata, self.sess)) .map(|trait_impls| (trait_impls.trait_id, trait_impls.impls)) .collect(); @@ -298,7 +299,7 @@ impl<'a> CrateLoader<'a> { let dllimports: FxHashSet<_> = cmeta .root .native_libraries - .decode(&cmeta) + .decode((&cmeta, self.sess)) .filter(|lib| relevant_lib(self.sess, lib) && lib.kind == cstore::NativeLibraryKind::NativeUnknown) .flat_map(|lib| { @@ -555,7 +556,7 @@ impl<'a> CrateLoader<'a> { use std::{env, mem}; use proc_macro::TokenStream; use proc_macro::__internal::Registry; - use rustc_back::dynamic_lib::DynamicLibrary; + use dynamic_lib::DynamicLibrary; use syntax_ext::deriving::custom::ProcMacroDerive; use syntax_ext::proc_macro_impl::{AttrProcMacro, BangProcMacro}; @@ -626,7 +627,7 @@ impl<'a> CrateLoader<'a> { pub fn find_plugin_registrar(&mut self, span: Span, name: &str) - -> Option<(PathBuf, Symbol, DefIndex)> { + -> Option<(PathBuf, CrateDisambiguator, DefIndex)> { let ekrate = self.read_extension_crate(span, &ExternCrateInfo { name: Symbol::intern(name), ident: Symbol::intern(name), @@ -685,14 +686,15 @@ impl<'a> CrateLoader<'a> { let mut needs_panic_runtime = attr::contains_name(&krate.attrs, "needs_panic_runtime"); + let sess = self.sess; self.cstore.iter_crate_data(|cnum, data| { needs_panic_runtime = needs_panic_runtime || - data.needs_panic_runtime(); - if data.is_panic_runtime() { + data.needs_panic_runtime(sess); + if data.is_panic_runtime(sess) { // Inject a dependency from all #![needs_panic_runtime] to this // #![panic_runtime] crate. self.inject_dependency_if(cnum, "a panic runtime", - &|data| data.needs_panic_runtime()); + &|data| data.needs_panic_runtime(sess)); runtime_found = runtime_found || data.dep_kind.get() == DepKind::Explicit; } }); @@ -728,7 +730,7 @@ impl<'a> CrateLoader<'a> { // Sanity check the loaded crate to ensure it is indeed a panic runtime // and the panic strategy is indeed what we thought it was. - if !data.is_panic_runtime() { + if !data.is_panic_runtime(self.sess) { self.sess.err(&format!("the crate `{}` is not a panic runtime", name)); } @@ -740,7 +742,7 @@ impl<'a> CrateLoader<'a> { self.sess.injected_panic_runtime.set(Some(cnum)); self.inject_dependency_if(cnum, "a panic runtime", - &|data| data.needs_panic_runtime()); + &|data| data.needs_panic_runtime(self.sess)); } fn inject_sanitizer_runtime(&mut self) { @@ -835,7 +837,7 @@ impl<'a> CrateLoader<'a> { PathKind::Crate, dep_kind); // Sanity check the loaded crate to ensure it is indeed a sanitizer runtime - if !data.is_sanitizer_runtime() { + if !data.is_sanitizer_runtime(self.sess) { self.sess.err(&format!("the crate `{}` is not a sanitizer runtime", name)); } @@ -856,7 +858,7 @@ impl<'a> CrateLoader<'a> { PathKind::Crate, dep_kind); // Sanity check the loaded crate to ensure it is indeed a profiler runtime - if !data.is_profiler_runtime() { + if !data.is_profiler_runtime(self.sess) { self.sess.err(&format!("the crate `profiler_builtins` is not \ a profiler runtime")); } @@ -875,7 +877,7 @@ impl<'a> CrateLoader<'a> { let mut needs_allocator = attr::contains_name(&krate.attrs, "needs_allocator"); self.cstore.iter_crate_data(|_, data| { - needs_allocator = needs_allocator || data.needs_allocator(); + needs_allocator = needs_allocator || data.needs_allocator(self.sess); }); if !needs_allocator { return @@ -997,7 +999,7 @@ impl<'a> CrateLoader<'a> { Some(data) => { // We have an allocator. We detect separately what kind it is, to allow for some // flexibility in misconfiguration. - let attrs = data.get_item_attrs(CRATE_DEF_INDEX); + let attrs = data.get_item_attrs(CRATE_DEF_INDEX, self.sess); let kind_interned = attr::first_attr_value_str_by_name(&attrs, "rustc_alloc_kind") .map(Symbol::as_str); let kind_str = kind_interned diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 9e47e96aee4ef..3a4ba6768a716 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -17,6 +17,7 @@ use rustc::hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefIndex}; use rustc::hir::map::definitions::DefPathTable; use rustc::hir::svh::Svh; use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader}; +use rustc::session::{Session, CrateDisambiguator}; use rustc_back::PanicStrategy; use rustc_data_structures::indexed_vec::IndexVec; use rustc::util::nodemap::{FxHashMap, FxHashSet, NodeMap}; @@ -33,7 +34,7 @@ pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePrefere pub use rustc::middle::cstore::NativeLibraryKind::*; pub use rustc::middle::cstore::{CrateSource, LibSource}; -pub use cstore_impl::{provide, provide_local}; +pub use cstore_impl::{provide, provide_extern}; // A map from external crate numbers (as decoded from some crate file) to // local crate numbers (as generated during this session). Each external @@ -171,12 +172,12 @@ impl CrateMetadata { pub fn hash(&self) -> Svh { self.root.hash } - pub fn disambiguator(&self) -> Symbol { + pub fn disambiguator(&self) -> CrateDisambiguator { self.root.disambiguator } - pub fn needs_allocator(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn needs_allocator(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "needs_allocator") } @@ -188,43 +189,43 @@ impl CrateMetadata { self.root.has_default_lib_allocator.clone() } - pub fn is_panic_runtime(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn is_panic_runtime(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "panic_runtime") } - pub fn needs_panic_runtime(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn needs_panic_runtime(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "needs_panic_runtime") } - pub fn is_compiler_builtins(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn is_compiler_builtins(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "compiler_builtins") } - pub fn is_sanitizer_runtime(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn is_sanitizer_runtime(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "sanitizer_runtime") } - pub fn is_profiler_runtime(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn is_profiler_runtime(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "profiler_runtime") } - pub fn is_no_builtins(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn is_no_builtins(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_name(&attrs, "no_builtins") } - pub fn has_copy_closures(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn has_copy_closures(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_feature_attr(&attrs, "copy_closures") } - pub fn has_clone_closures(&self) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX); + pub fn has_clone_closures(&self, sess: &Session) -> bool { + let attrs = self.get_item_attrs(CRATE_DEF_INDEX, sess); attr::contains_feature_attr(&attrs, "clone_closures") } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 8eacc21ab003b..911b4dac4e13a 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -17,11 +17,10 @@ use schema; use rustc::ty::maps::QueryConfig; use rustc::middle::cstore::{CrateStore, DepKind, MetadataLoader, LinkMeta, - LoadedMacro, EncodedMetadata, - EncodedMetadataHashes, NativeLibraryKind}; + LoadedMacro, EncodedMetadata, NativeLibraryKind}; use rustc::middle::stability::DeprecationEntry; use rustc::hir::def; -use rustc::session::Session; +use rustc::session::{CrateDisambiguator, Session}; use rustc::ty::{self, TyCtxt}; use rustc::ty::maps::Providers; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; @@ -45,7 +44,7 @@ use rustc::hir; macro_rules! provide { (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $($name:ident => $compute:block)*) => { - pub fn provide<$lt>(providers: &mut Providers<$lt>) { + pub fn provide_extern<$lt>(providers: &mut Providers<$lt>) { $(fn $name<'a, $lt:$lt, T>($tcx: TyCtxt<'a, $lt, $lt>, def_id_arg: T) -> as QueryConfig>::Value @@ -99,11 +98,13 @@ impl IntoArgs for (CrateNum, DefId) { provide! { <'tcx> tcx, def_id, other, cdata, type_of => { cdata.get_type(def_id.index, tcx) } - generics_of => { tcx.alloc_generics(cdata.get_generics(def_id.index)) } + generics_of => { + tcx.alloc_generics(cdata.get_generics(def_id.index, tcx.sess)) + } predicates_of => { cdata.get_predicates(def_id.index, tcx) } super_predicates_of => { cdata.get_super_predicates(def_id.index, tcx) } trait_def => { - tcx.alloc_trait_def(cdata.get_trait_def(def_id.index)) + tcx.alloc_trait_def(cdata.get_trait_def(def_id.index, tcx.sess)) } adt_def => { cdata.get_adt_def(def_id.index, tcx) } adt_destructor => { @@ -134,17 +135,15 @@ provide! { <'tcx> tcx, def_id, other, cdata, mir } - generator_sig => { cdata.generator_sig(def_id.index, tcx) } mir_const_qualif => { (cdata.mir_const_qualif(def_id.index), Rc::new(IdxSetBuf::new_empty(0))) } typeck_tables_of => { cdata.item_body_tables(def_id.index, tcx) } - closure_kind => { cdata.closure_kind(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } inherent_impls => { Rc::new(cdata.get_inherent_implementations_for_type(def_id.index)) } is_const_fn => { cdata.is_const_fn(def_id.index) } is_foreign_item => { cdata.is_foreign_item(def_id.index) } - is_default_impl => { cdata.is_default_impl(def_id.index) } + is_auto_impl => { cdata.is_auto_impl(def_id.index) } describe_def => { cdata.get_def(def_id.index) } def_span => { cdata.get_span(def_id.index, &tcx.sess) } lookup_stability => { @@ -153,7 +152,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, lookup_deprecation_entry => { cdata.get_deprecation(def_id.index).map(DeprecationEntry::external) } - item_attrs => { cdata.get_item_attrs(def_id.index) } + item_attrs => { cdata.get_item_attrs(def_id.index, tcx.sess) } // FIXME(#38501) We've skipped a `read` on the `HirBody` of // a `fn` when encoding, so the dep-tracking wouldn't work. // This is only used by rustdoc anyway, which shouldn't have @@ -171,17 +170,17 @@ provide! { <'tcx> tcx, def_id, other, cdata, is_mir_available => { cdata.is_item_mir_available(def_id.index) } dylib_dependency_formats => { Rc::new(cdata.get_dylib_dependency_formats()) } - is_panic_runtime => { cdata.is_panic_runtime() } - is_compiler_builtins => { cdata.is_compiler_builtins() } + is_panic_runtime => { cdata.is_panic_runtime(tcx.sess) } + is_compiler_builtins => { cdata.is_compiler_builtins(tcx.sess) } has_global_allocator => { cdata.has_global_allocator() } - is_sanitizer_runtime => { cdata.is_sanitizer_runtime() } - is_profiler_runtime => { cdata.is_profiler_runtime() } + is_sanitizer_runtime => { cdata.is_sanitizer_runtime(tcx.sess) } + is_profiler_runtime => { cdata.is_profiler_runtime(tcx.sess) } panic_strategy => { cdata.panic_strategy() } extern_crate => { Rc::new(cdata.extern_crate.get()) } - is_no_builtins => { cdata.is_no_builtins() } + is_no_builtins => { cdata.is_no_builtins(tcx.sess) } impl_defaultness => { cdata.get_impl_defaultness(def_id.index) } exported_symbol_ids => { Rc::new(cdata.get_exported_symbols()) } - native_libraries => { Rc::new(cdata.get_native_libraries()) } + native_libraries => { Rc::new(cdata.get_native_libraries(tcx.sess)) } plugin_registrar_fn => { cdata.root.plugin_registrar_fn.map(|index| { DefId { krate: def_id.krate, index } @@ -237,11 +236,11 @@ provide! { <'tcx> tcx, def_id, other, cdata, used_crate_source => { Rc::new(cdata.source.clone()) } - has_copy_closures => { cdata.has_copy_closures() } - has_clone_closures => { cdata.has_clone_closures() } + has_copy_closures => { cdata.has_copy_closures(tcx.sess) } + has_clone_closures => { cdata.has_clone_closures(tcx.sess) } } -pub fn provide_local<'tcx>(providers: &mut Providers<'tcx>) { +pub fn provide<'tcx>(providers: &mut Providers<'tcx>) { fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { let node_id = tcx.hir.as_local_node_id(def_id) .expect("Non-local call to local provider is_const_fn"); @@ -358,8 +357,8 @@ impl CrateStore for cstore::CStore { self.get_crate_data(def.krate).get_visibility(def.index) } - fn item_generics_cloned_untracked(&self, def: DefId) -> ty::Generics { - self.get_crate_data(def.krate).get_generics(def.index) + fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics { + self.get_crate_data(def.krate).get_generics(def.index, sess) } fn associated_item_cloned_untracked(&self, def: DefId) -> ty::AssociatedItem @@ -384,7 +383,7 @@ impl CrateStore for cstore::CStore { self.get_crate_data(cnum).name } - fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol + fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator { self.get_crate_data(cnum).disambiguator() } @@ -454,7 +453,7 @@ impl CrateStore for cstore::CStore { let body = filemap_to_stream(&sess.parse_sess, filemap, None); // Mark the attrs as used - let attrs = data.get_item_attrs(id.index); + let attrs = data.get_item_attrs(id.index, sess); for attr in attrs.iter() { attr::mark_used(attr); } @@ -498,7 +497,7 @@ impl CrateStore for cstore::CStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> (EncodedMetadata, EncodedMetadataHashes) + -> EncodedMetadata { encoder::encode_metadata(tcx, link_meta, reachable) } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 65cf15e5a0ec7..eb2bcfc93c5fb 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -15,31 +15,28 @@ use schema::*; use rustc::hir::map::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc::hir; - -use rustc::middle::const_val::ByteArray; use rustc::middle::cstore::{LinkagePreference, ExternConstBody, ExternBodyNestedBodies}; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::ich::Fingerprint; use rustc::middle::lang_items; +use rustc::mir; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::subst::Substs; +use rustc::ty::codec::TyDecoder; use rustc::util::nodemap::DefIdSet; - use rustc::mir::Mir; -use std::borrow::Cow; use std::cell::Ref; use std::collections::BTreeMap; use std::io; use std::mem; use std::rc::Rc; -use std::str; use std::u32; use rustc_serialize::{Decodable, Decoder, SpecializedDecoder, opaque}; +use rustc_data_structures::indexed_vec::Idx; use syntax::attr; use syntax::ast::{self, Ident}; use syntax::codemap; @@ -85,6 +82,20 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a MetadataBlob { } } + +impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a MetadataBlob, &'a Session) { + fn raw_bytes(self) -> &'a [u8] { + let (blob, _) = self; + &blob.0 + } + + fn sess(self) -> Option<&'a Session> { + let (_, sess) = self; + Some(sess) + } +} + + impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadata { fn raw_bytes(self) -> &'a [u8] { self.blob.raw_bytes() @@ -143,16 +154,6 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { self.cdata.expect("missing CrateMetadata in DecodeContext") } - fn with_position R, R>(&mut self, pos: usize, f: F) -> R { - let new_opaque = opaque::Decoder::new(self.opaque.data, pos); - let old_opaque = mem::replace(&mut self.opaque, new_opaque); - let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); - let r = f(self); - self.opaque = old_opaque; - self.lazy_state = old_state; - r - } - fn read_lazy_distance(&mut self, min_size: usize) -> Result::Error> { let distance = self.read_usize()?; let position = match self.lazy_state { @@ -168,43 +169,63 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { } } -macro_rules! decoder_methods { - ($($name:ident -> $ty:ty;)*) => { - $(fn $name(&mut self) -> Result<$ty, Self::Error> { - self.opaque.$name() - })* +impl<'a, 'tcx: 'a> TyDecoder<'a, 'tcx> for DecodeContext<'a, 'tcx> { + + #[inline] + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx.expect("missing TyCtxt in DecodeContext") } -} -impl<'doc, 'tcx> Decoder for DecodeContext<'doc, 'tcx> { - type Error = as Decoder>::Error; + #[inline] + fn peek_byte(&self) -> u8 { + self.opaque.data[self.opaque.position()] + } - decoder_methods! { - read_nil -> (); + #[inline] + fn position(&self) -> usize { + self.opaque.position() + } - read_u128 -> u128; - read_u64 -> u64; - read_u32 -> u32; - read_u16 -> u16; - read_u8 -> u8; - read_usize -> usize; + fn cached_ty_for_shorthand(&mut self, + shorthand: usize, + or_insert_with: F) + -> Result, Self::Error> + where F: FnOnce(&mut Self) -> Result, Self::Error> + { + let tcx = self.tcx(); - read_i128 -> i128; - read_i64 -> i64; - read_i32 -> i32; - read_i16 -> i16; - read_i8 -> i8; - read_isize -> isize; + let key = ty::CReaderCacheKey { + cnum: self.cdata().cnum, + pos: shorthand, + }; - read_bool -> bool; - read_f64 -> f64; - read_f32 -> f32; - read_char -> char; - read_str -> Cow; + if let Some(&ty) = tcx.rcache.borrow().get(&key) { + return Ok(ty); + } + + let ty = or_insert_with(self)?; + tcx.rcache.borrow_mut().insert(key, ty); + Ok(ty) } - fn error(&mut self, err: &str) -> Self::Error { - self.opaque.error(err) + fn with_position(&mut self, pos: usize, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + let new_opaque = opaque::Decoder::new(self.opaque.data, pos); + let old_opaque = mem::replace(&mut self.opaque, new_opaque); + let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); + let r = f(self); + self.opaque = old_opaque; + self.lazy_state = old_state; + r + } + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { + if cnum == LOCAL_CRATE { + self.cdata().cnum + } else { + self.cdata().cnum_map.borrow()[cnum] + } } } @@ -226,14 +247,24 @@ impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - let cnum = CrateNum::from_u32(u32::decode(self)?); - if cnum == LOCAL_CRATE { - Ok(self.cdata().cnum) - } else { - Ok(self.cdata().cnum_map.borrow()[cnum]) - } + +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result { + let krate = CrateNum::decode(self)?; + let index = DefIndex::decode(self)?; + + Ok(DefId { + krate, + index, + }) + } +} + +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result { + Ok(DefIndex::from_u32(self.read_u32()?)) } } @@ -245,7 +276,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { let sess = if let Some(sess) = self.sess { sess } else { - return Ok(Span::new(lo, hi, NO_EXPANSION)); + bug!("Cannot decode Span without Session.") }; let (lo, hi) = if lo > hi { @@ -267,7 +298,8 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { // originate from the same filemap. let last_filemap = &imported_filemaps[self.last_filemap_index]; - if lo >= last_filemap.original_start_pos && lo <= last_filemap.original_end_pos && + if lo >= last_filemap.original_start_pos && + lo <= last_filemap.original_end_pos && hi >= last_filemap.original_start_pos && hi <= last_filemap.original_end_pos { last_filemap @@ -289,111 +321,22 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } }; - let lo = (lo - filemap.original_start_pos) + filemap.translated_filemap.start_pos; - let hi = (hi - filemap.original_start_pos) + filemap.translated_filemap.start_pos; + let lo = (lo + filemap.translated_filemap.start_pos) - filemap.original_start_pos; + let hi = (hi + filemap.translated_filemap.start_pos) - filemap.original_start_pos; Ok(Span::new(lo, hi, NO_EXPANSION)) } } -// FIXME(#36588) These impls are horribly unsound as they allow -// the caller to pick any lifetime for 'tcx, including 'static, -// by using the unspecialized proxies to them. - -impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - let tcx = self.tcx(); - - // Handle shorthands first, if we have an usize > 0x80. - if self.opaque.data[self.opaque.position()] & 0x80 != 0 { - let pos = self.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let key = ty::CReaderCacheKey { - cnum: self.cdata().cnum, - pos: pos - SHORTHAND_OFFSET, - }; - if let Some(ty) = tcx.rcache.borrow().get(&key).cloned() { - return Ok(ty); - } - - let ty = self.with_position(key.pos, Ty::decode)?; - tcx.rcache.borrow_mut().insert(key, ty); - Ok(ty) - } else { - Ok(tcx.mk_ty(ty::TypeVariants::decode(self)?)) - } - } -} - - -impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(ty::GenericPredicates { - parent: Decodable::decode(self)?, - predicates: (0..self.read_usize()?).map(|_| { - // Handle shorthands first, if we have an usize > 0x80. - if self.opaque.data[self.opaque.position()] & 0x80 != 0 { - let pos = self.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let pos = pos - SHORTHAND_OFFSET; - - self.with_position(pos, ty::Predicate::decode) - } else { - ty::Predicate::decode(self) - } - }) - .collect::, _>>()?, - }) - } -} - -impl<'a, 'tcx> SpecializedDecoder<&'tcx Substs<'tcx>> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result<&'tcx Substs<'tcx>, Self::Error> { - Ok(self.tcx().mk_substs((0..self.read_usize()?).map(|_| Decodable::decode(self)))?) - } -} - -impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(self.tcx().mk_region(Decodable::decode(self)?)) - } -} - -impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Slice>> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result<&'tcx ty::Slice>, Self::Error> { - Ok(self.tcx().mk_type_list((0..self.read_usize()?).map(|_| Decodable::decode(self)))?) - } -} - -impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::AdtDef> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result<&'tcx ty::AdtDef, Self::Error> { - let def_id = DefId::decode(self)?; - Ok(self.tcx().adt_def(def_id)) - } -} - -impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Slice>> - for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) - -> Result<&'tcx ty::Slice>, Self::Error> { - Ok(self.tcx().mk_existential_predicates((0..self.read_usize()?) - .map(|_| Decodable::decode(self)))?) - } -} - -impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(ByteArray { - data: self.tcx().alloc_byte_array(&Vec::decode(self)?) - }) +impl<'a, 'tcx, T: Decodable> SpecializedDecoder> +for DecodeContext<'a, 'tcx> { + #[inline] + fn specialized_decode(&mut self) -> Result, Self::Error> { + Ok(mir::ClearCrossCrate::Clear) } } -impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Const<'tcx>> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { - Ok(self.tcx().mk_const(Decodable::decode(self)?)) - } -} +implement_ty_decoder!( DecodeContext<'a, 'tcx> ); impl<'a, 'tcx> MetadataBlob { pub fn is_compatible(&self) -> bool { @@ -449,10 +392,11 @@ impl<'tcx> EntryKind<'tcx> { EntryKind::Enum(..) => Def::Enum(did), EntryKind::MacroDef(_) => Def::Macro(did, MacroKind::Bang), EntryKind::GlobalAsm => Def::GlobalAsm(did), + EntryKind::ForeignType => Def::TyForeign(did), EntryKind::ForeignMod | EntryKind::Impl(_) | - EntryKind::DefaultImpl(_) | + EntryKind::AutoImpl(_) | EntryKind::Field | EntryKind::Generator(_) | EntryKind::Closure(_) => return None, @@ -513,16 +457,16 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn get_trait_def(&self, item_id: DefIndex) -> ty::TraitDef { + pub fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { let data = match self.entry(item_id).kind { - EntryKind::Trait(data) => data.decode(self), + EntryKind::Trait(data) => data.decode((self, sess)), _ => bug!(), }; ty::TraitDef::new(self.local_def_id(item_id), data.unsafety, data.paren_sugar, - data.has_default_impl, + data.has_auto_impl, self.def_path_table.def_path_hash(item_id)) } @@ -599,8 +543,11 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn get_generics(&self, item_id: DefIndex) -> ty::Generics { - self.entry(item_id).generics.unwrap().decode(self) + pub fn get_generics(&self, + item_id: DefIndex, + sess: &Session) + -> ty::Generics { + self.entry(item_id).generics.unwrap().decode((self, sess)) } pub fn get_type(&self, id: DefIndex, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { @@ -728,7 +675,7 @@ impl<'a, 'tcx> CrateMetadata { continue; } EntryKind::Impl(_) | - EntryKind::DefaultImpl(_) => continue, + EntryKind::AutoImpl(_) => continue, _ => {} } @@ -900,7 +847,7 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn get_item_attrs(&self, node_id: DefIndex) -> Rc<[ast::Attribute]> { + pub fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Rc<[ast::Attribute]> { let (node_as, node_index) = (node_id.address_space().index(), node_id.as_array_index()); if self.is_proc_macro(node_id) { @@ -920,7 +867,7 @@ impl<'a, 'tcx> CrateMetadata { if def_key.disambiguated_data.data == DefPathData::StructCtor { item = self.entry(def_key.parent.unwrap()); } - let result: Rc<[ast::Attribute]> = Rc::from(self.get_attributes(&item)); + let result: Rc<[ast::Attribute]> = Rc::from(self.get_attributes(&item, sess)); let vec_ = &mut self.attribute_cache.borrow_mut()[node_as]; if vec_.len() < node_index + 1 { vec_.resize(node_index + 1, None); @@ -937,9 +884,9 @@ impl<'a, 'tcx> CrateMetadata { .collect() } - fn get_attributes(&self, item: &Entry<'tcx>) -> Vec { + fn get_attributes(&self, item: &Entry<'tcx>, sess: &Session) -> Vec { item.attributes - .decode(self) + .decode((self, sess)) .map(|mut attr| { // Need new unique IDs: old thread-local IDs won't map to new threads. attr.id = attr::mk_attr_id(); @@ -1005,8 +952,8 @@ impl<'a, 'tcx> CrateMetadata { } - pub fn get_native_libraries(&self) -> Vec { - self.root.native_libraries.decode(self).collect() + pub fn get_native_libraries(&self, sess: &Session) -> Vec { + self.root.native_libraries.decode((self, sess)).collect() } pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> { @@ -1075,20 +1022,13 @@ impl<'a, 'tcx> CrateMetadata { self.dllimport_foreign_items.contains(&id) } - pub fn is_default_impl(&self, impl_id: DefIndex) -> bool { + pub fn is_auto_impl(&self, impl_id: DefIndex) -> bool { match self.entry(impl_id).kind { - EntryKind::DefaultImpl(_) => true, + EntryKind::AutoImpl(_) => true, _ => false, } } - pub fn closure_kind(&self, closure_id: DefIndex) -> ty::ClosureKind { - match self.entry(closure_id).kind { - EntryKind::Closure(data) => data.decode(self).kind, - _ => bug!(), - } - } - pub fn fn_sig(&self, id: DefIndex, tcx: TyCtxt<'a, 'tcx, 'tcx>) @@ -1105,23 +1045,6 @@ impl<'a, 'tcx> CrateMetadata { sig.decode((self, tcx)) } - fn get_generator_data(&self, - id: DefIndex, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> Option> { - match self.entry(id).kind { - EntryKind::Generator(data) => Some(data.decode((self, tcx))), - _ => None, - } - } - - pub fn generator_sig(&self, - id: DefIndex, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> Option> { - self.get_generator_data(id, tcx).map(|d| d.sig) - } - #[inline] pub fn def_key(&self, index: DefIndex) -> DefKey { self.def_path_table.def_key(index) @@ -1182,6 +1105,7 @@ impl<'a, 'tcx> CrateMetadata { end_pos, lines, multibyte_chars, + non_narrow_chars, .. } = filemap_to_import; let source_length = (end_pos - start_pos).to_usize(); @@ -1199,6 +1123,10 @@ impl<'a, 'tcx> CrateMetadata { for mbc in &mut multibyte_chars { mbc.pos = mbc.pos - start_pos; } + let mut non_narrow_chars = non_narrow_chars.into_inner(); + for swc in &mut non_narrow_chars { + *swc = *swc - start_pos; + } let local_version = local_codemap.new_imported_filemap(name, name_was_remapped, @@ -1206,7 +1134,8 @@ impl<'a, 'tcx> CrateMetadata { src_hash, source_length, lines, - multibyte_chars); + multibyte_chars, + non_narrow_chars); debug!("CrateMetaData::imported_filemaps alloc \ filemap {:?} original (start_pos {:?} end_pos {:?}) \ translated (start_pos {:?} end_pos {:?})", diff --git a/src/librustc_back/dynamic_lib.rs b/src/librustc_metadata/dynamic_lib.rs similarity index 100% rename from src/librustc_back/dynamic_lib.rs rename to src/librustc_metadata/dynamic_lib.rs diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 6b49be3e12192..6cfa324797c5d 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -14,25 +14,22 @@ use isolated_encoder::IsolatedEncoder; use schema::*; use rustc::middle::cstore::{LinkMeta, LinkagePreference, NativeLibrary, - EncodedMetadata, EncodedMetadataHashes, - EncodedMetadataHash}; + EncodedMetadata}; use rustc::hir::def::CtorKind; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId, LOCAL_CRATE}; -use rustc::hir::map::definitions::{DefPathTable, GlobalMetaDataKind}; -use rustc::ich::Fingerprint; +use rustc::hir::map::definitions::DefPathTable; use rustc::middle::dependency_format::Linkage; use rustc::middle::lang_items; use rustc::mir; use rustc::traits::specialization_graph; use rustc::ty::{self, Ty, TyCtxt, ReprOptions}; +use rustc::ty::codec::{self as ty_codec, TyEncoder}; use rustc::session::config::{self, CrateTypeProcMacro}; use rustc::util::nodemap::{FxHashMap, NodeSet}; use rustc_serialize::{Encodable, Encoder, SpecializedEncoder, opaque}; -use std::hash::Hash; -use std::intrinsics; use std::io::prelude::*; use std::io::Cursor; use std::path::Path; @@ -58,9 +55,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> { lazy_state: LazyState, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, - - pub metadata_hashes: EncodedMetadataHashes, - pub compute_ich: bool, } macro_rules! encoder_methods { @@ -117,9 +111,36 @@ impl<'a, 'tcx, T> SpecializedEncoder> for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, cnum: &CrateNum) -> Result<(), Self::Error> { + self.emit_u32(cnum.as_u32()) + } +} + +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, def_id: &DefId) -> Result<(), Self::Error> { + let DefId { + krate, + index, + } = *def_id; + + krate.encode(self)?; + index.encode(self) + } +} + +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + #[inline] + fn specialized_encode(&mut self, def_index: &DefIndex) -> Result<(), Self::Error> { + self.emit_u32(def_index.as_u32()) + } +} + impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { - self.encode_with_shorthand(ty, &ty.sty, |ecx| &mut ecx.type_shorthands) + ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) } } @@ -127,20 +148,26 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext fn specialized_encode(&mut self, predicates: &ty::GenericPredicates<'tcx>) -> Result<(), Self::Error> { - predicates.parent.encode(self)?; - predicates.predicates.len().encode(self)?; - for predicate in &predicates.predicates { - self.encode_with_shorthand(predicate, predicate, |ecx| &mut ecx.predicate_shorthands)? - } - Ok(()) + ty_codec::encode_predicates(self, predicates, |ecx| &mut ecx.predicate_shorthands) } } -impl<'a, 'tcx> EncodeContext<'a, 'tcx> { +impl<'a, 'tcx, T: Encodable> SpecializedEncoder> +for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, + _: &mir::ClearCrossCrate) + -> Result<(), Self::Error> { + Ok(()) + } +} - pub fn position(&self) -> usize { +impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> { + fn position(&self) -> usize { self.opaque.position() } +} + +impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn emit_node R, R>(&mut self, f: F) -> R { assert_eq!(self.lazy_state, LazyState::NoNode); @@ -204,63 +231,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }) } - /// Encode the given value or a previously cached shorthand. - fn encode_with_shorthand(&mut self, - value: &T, - variant: &U, - map: M) - -> Result<(), ::Error> - where M: for<'b> Fn(&'b mut Self) -> &'b mut FxHashMap, - T: Clone + Eq + Hash, - U: Encodable - { - let existing_shorthand = map(self).get(value).cloned(); - if let Some(shorthand) = existing_shorthand { - return self.emit_usize(shorthand); - } - - let start = self.position(); - variant.encode(self)?; - let len = self.position() - start; - - // The shorthand encoding uses the same usize as the - // discriminant, with an offset so they can't conflict. - let discriminant = unsafe { intrinsics::discriminant_value(variant) }; - assert!(discriminant < SHORTHAND_OFFSET as u64); - let shorthand = start + SHORTHAND_OFFSET; - - // Get the number of bits that leb128 could fit - // in the same space as the fully encoded type. - let leb128_bits = len * 7; - - // Check that the shorthand is a not longer than the - // full encoding itself, i.e. it's an obvious win. - if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { - map(self).insert(value.clone(), shorthand); - } - - Ok(()) - } - // Encodes something that corresponds to a single DepNode::GlobalMetaData // and registers the Fingerprint in the `metadata_hashes` map. pub fn tracked<'x, DATA, R>(&'x mut self, - def_index: DefIndex, op: fn(&mut IsolatedEncoder<'x, 'a, 'tcx>, DATA) -> R, data: DATA) -> R { - let mut entry_builder = IsolatedEncoder::new(self); - let ret = op(&mut entry_builder, data); - let (fingerprint, this) = entry_builder.finish(); - - if let Some(fingerprint) = fingerprint { - this.metadata_hashes.hashes.push(EncodedMetadataHash { - def_index, - hash: fingerprint, - }) - } - - ret + op(&mut IsolatedEncoder::new(self), data) } fn encode_info_for_items(&mut self) -> Index { @@ -326,30 +303,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_crate_root(&mut self) -> Lazy { let mut i = self.position(); - let tcx = self.tcx; - let global_metadata_def_index = move |kind: GlobalMetaDataKind| { - kind.def_index(tcx.hir.definitions().def_path_table()) - }; - - let crate_deps = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::CrateDeps), - IsolatedEncoder::encode_crate_deps, - ()); + let crate_deps = self.tracked(IsolatedEncoder::encode_crate_deps, ()); let dylib_dependency_formats = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::DylibDependencyFormats), IsolatedEncoder::encode_dylib_dependency_formats, ()); let dep_bytes = self.position() - i; // Encode the language items. i = self.position(); - let lang_items = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::LangItems), - IsolatedEncoder::encode_lang_items, - ()); - + let lang_items = self.tracked(IsolatedEncoder::encode_lang_items, ()); let lang_items_missing = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::LangItemsMissing), IsolatedEncoder::encode_lang_items_missing, ()); let lang_item_bytes = self.position() - i; @@ -357,7 +320,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode the native libraries used i = self.position(); let native_libraries = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::NativeLibraries), IsolatedEncoder::encode_native_libraries, ()); let native_lib_bytes = self.position() - i; @@ -374,16 +336,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode the def IDs of impls, for coherence checking. i = self.position(); - let impls = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::Impls), - IsolatedEncoder::encode_impls, - ()); + let impls = self.tracked(IsolatedEncoder::encode_impls, ()); let impl_bytes = self.position() - i; // Encode exported symbols info. i = self.position(); let exported_symbols = self.tracked( - global_metadata_def_index(GlobalMetaDataKind::ExportedSymbols), IsolatedEncoder::encode_exported_symbols, self.exported_symbols); let exported_symbols_bytes = self.position() - i; @@ -436,11 +394,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let total_bytes = self.position(); - self.metadata_hashes.hashes.push(EncodedMetadataHash { - def_index: global_metadata_def_index(GlobalMetaDataKind::Krate), - hash: Fingerprint::from_smaller_hash(link_meta.crate_hash.as_u64()) - }); - if self.tcx.sess.meta_stats() { let mut zero_bytes = 0; for e in self.opaque.cursor.get_ref() { @@ -626,7 +579,8 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { fn encode_struct_ctor(&mut self, (adt_def_id, def_id): (DefId, DefId)) -> Entry<'tcx> { debug!("IsolatedEncoder::encode_struct_ctor({:?})", def_id); let tcx = self.tcx; - let variant = tcx.adt_def(adt_def_id).struct_variant(); + let adt_def = tcx.adt_def(adt_def_id); + let variant = adt_def.struct_variant(); let data = VariantData { ctor_kind: variant.ctor_kind, @@ -648,6 +602,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { } } + // If the structure is marked as non_exhaustive then lower the visibility + // to within the crate. + if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public { + ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); + } + let repr_options = get_repr_options(&tcx, adt_def_id); Entry { @@ -961,7 +921,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { ctor_sig: None, }), repr_options) } - hir::ItemDefaultImpl(..) => { + hir::ItemAutoImpl(..) => { let data = ImplData { polarity: hir::ImplPolarity::Positive, defaultness: hir::Defaultness::Final, @@ -970,7 +930,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { trait_ref: tcx.impl_trait_ref(def_id).map(|trait_ref| self.lazy(&trait_ref)), }; - EntryKind::DefaultImpl(self.lazy(&data)) + EntryKind::AutoImpl(self.lazy(&data)) } hir::ItemImpl(_, polarity, defaultness, ..) => { let trait_ref = tcx.impl_trait_ref(def_id); @@ -1012,7 +972,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { let data = TraitData { unsafety: trait_def.unsafety, paren_sugar: trait_def.paren_sugar, - has_default_impl: tcx.trait_has_default_impl(def_id), + has_auto_impl: tcx.trait_is_auto(def_id), super_predicates: self.lazy(&tcx.super_predicates_of(def_id)), }; @@ -1213,19 +1173,25 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { debug!("IsolatedEncoder::encode_info_for_closure({:?})", def_id); let tcx = self.tcx; - let kind = if let Some(sig) = self.tcx.generator_sig(def_id) { - let layout = self.tcx.generator_layout(def_id); - let data = GeneratorData { - sig, - layout: layout.clone(), - }; - EntryKind::Generator(self.lazy(&data)) - } else { - let data = ClosureData { - kind: tcx.closure_kind(def_id), - sig: self.lazy(&tcx.fn_sig(def_id)), - }; - EntryKind::Closure(self.lazy(&data)) + let tables = self.tcx.typeck_tables_of(def_id); + let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + let kind = match tables.node_id_to_type(hir_id).sty { + ty::TyGenerator(def_id, ..) => { + let layout = self.tcx.generator_layout(def_id); + let data = GeneratorData { + layout: layout.clone(), + }; + EntryKind::Generator(self.lazy(&data)) + } + + ty::TyClosure(def_id, substs) => { + let sig = substs.closure_sig(def_id, self.tcx); + let data = ClosureData { sig: self.lazy(&sig) }; + EntryKind::Closure(self.lazy(&data)) + } + + _ => bug!("closure that is neither generator nor closure") }; Entry { @@ -1419,6 +1385,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { } hir::ForeignItemStatic(_, true) => EntryKind::ForeignMutStatic, hir::ForeignItemStatic(_, false) => EntryKind::ForeignImmStatic, + hir::ForeignItemType => EntryKind::ForeignType, }; Entry { @@ -1521,7 +1488,7 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> { fn encode_info_for_ty(&mut self, ty: &hir::Ty) { match ty.node { - hir::TyImplTrait(_) => { + hir::TyImplTraitExistential(..) => { let def_id = self.tcx.hir.local_def_id(ty.id); self.record(def_id, IsolatedEncoder::encode_info_for_anon_ty, def_id); } @@ -1558,7 +1525,7 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> { hir::ItemGlobalAsm(..) | hir::ItemExternCrate(..) | hir::ItemUse(..) | - hir::ItemDefaultImpl(..) | + hir::ItemAutoImpl(..) | hir::ItemTy(..) => { // no sub-item recording needed in these cases } @@ -1655,7 +1622,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'a, 'tcx> { pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, exported_symbols: &NodeSet) - -> (EncodedMetadata, EncodedMetadataHashes) + -> EncodedMetadata { let mut cursor = Cursor::new(vec![]); cursor.write_all(METADATA_HEADER).unwrap(); @@ -1663,11 +1630,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Will be filled with the root position after encoding everything. cursor.write_all(&[0, 0, 0, 0]).unwrap(); - let compute_ich = (tcx.sess.opts.debugging_opts.query_dep_graph || - tcx.sess.opts.debugging_opts.incremental_cc) && - tcx.sess.opts.build_dep_graph(); - - let (root, metadata_hashes) = { + let root = { let mut ecx = EncodeContext { opaque: opaque::Encoder::new(&mut cursor), tcx, @@ -1676,8 +1639,6 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, lazy_state: LazyState::NoNode, type_shorthands: Default::default(), predicate_shorthands: Default::default(), - metadata_hashes: EncodedMetadataHashes::new(), - compute_ich, }; // Encode the rustc version string in a predictable location. @@ -1685,8 +1646,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Encode all the entries and extra information in the crate, // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); - (root, ecx.metadata_hashes) + ecx.encode_crate_root() }; let mut result = cursor.into_inner(); @@ -1698,7 +1658,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result[header + 2] = (pos >> 8) as u8; result[header + 3] = (pos >> 0) as u8; - (EncodedMetadata { raw_data: result }, metadata_hashes) + EncodedMetadata { raw_data: result } } pub fn get_repr_options<'a, 'tcx, 'gcx>(tcx: &TyCtxt<'a, 'tcx, 'gcx>, did: DefId) -> ReprOptions { diff --git a/src/librustc_metadata/index_builder.rs b/src/librustc_metadata/index_builder.rs index 1d2b6cc33d46a..f218268914300 100644 --- a/src/librustc_metadata/index_builder.rs +++ b/src/librustc_metadata/index_builder.rs @@ -62,7 +62,6 @@ use isolated_encoder::IsolatedEncoder; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::middle::cstore::EncodedMetadataHash; use rustc::ty::TyCtxt; use syntax::ast; @@ -128,19 +127,10 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> { // unclear whether that would be a win since hashing is cheap enough. let _task = tcx.dep_graph.in_ignore(); - let ecx: &'x mut EncodeContext<'b, 'tcx> = &mut *self.ecx; - let mut entry_builder = IsolatedEncoder::new(ecx); + let mut entry_builder = IsolatedEncoder::new(self.ecx); let entry = op(&mut entry_builder, data); let entry = entry_builder.lazy(&entry); - let (fingerprint, ecx) = entry_builder.finish(); - if let Some(hash) = fingerprint { - ecx.metadata_hashes.hashes.push(EncodedMetadataHash { - def_index: id.index, - hash, - }); - } - self.items.record(id, entry); } diff --git a/src/librustc_metadata/isolated_encoder.rs b/src/librustc_metadata/isolated_encoder.rs index 7dc50fe29df07..689c190966ee8 100644 --- a/src/librustc_metadata/isolated_encoder.rs +++ b/src/librustc_metadata/isolated_encoder.rs @@ -10,12 +10,7 @@ use encoder::EncodeContext; use schema::{Lazy, LazySeq}; - -use rustc::ich::{StableHashingContext, Fingerprint}; use rustc::ty::TyCtxt; - -use rustc_data_structures::accumulate_vec::AccumulateVec; -use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use rustc_serialize::Encodable; /// The IsolatedEncoder provides facilities to write to crate metadata while @@ -23,148 +18,47 @@ use rustc_serialize::Encodable; pub struct IsolatedEncoder<'a, 'b: 'a, 'tcx: 'b> { pub tcx: TyCtxt<'b, 'tcx, 'tcx>, ecx: &'a mut EncodeContext<'b, 'tcx>, - hcx: Option<(StableHashingContext<'tcx>, StableHasher)>, } impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { pub fn new(ecx: &'a mut EncodeContext<'b, 'tcx>) -> Self { let tcx = ecx.tcx; - let compute_ich = ecx.compute_ich; IsolatedEncoder { tcx, ecx, - hcx: if compute_ich { - // We are always hashing spans for things in metadata because - // don't know if a downstream crate will use them or not. - // Except when -Zquery-dep-graph is specified because we don't - // want to mess up our tests. - let hcx = if tcx.sess.opts.debugging_opts.query_dep_graph { - tcx.create_stable_hashing_context() - } else { - tcx.create_stable_hashing_context().force_span_hashing() - }; - - Some((hcx, StableHasher::new())) - } else { - None - } - } - } - - pub fn finish(self) -> (Option, &'a mut EncodeContext<'b, 'tcx>) { - if let Some((_, hasher)) = self.hcx { - (Some(hasher.finish()), self.ecx) - } else { - (None, self.ecx) } } pub fn lazy(&mut self, value: &T) -> Lazy - where T: Encodable + HashStable> + where T: Encodable { - if let Some((ref mut hcx, ref mut hasher)) = self.hcx { - value.hash_stable(hcx, hasher); - debug!("metadata-hash: {:?}", hasher); - } self.ecx.lazy(value) } pub fn lazy_seq(&mut self, iter: I) -> LazySeq where I: IntoIterator, - T: Encodable + HashStable> + T: Encodable { - if let Some((ref mut hcx, ref mut hasher)) = self.hcx { - let iter = iter.into_iter(); - let (lower_bound, upper_bound) = iter.size_hint(); - - if upper_bound == Some(lower_bound) { - lower_bound.hash_stable(hcx, hasher); - let mut num_items_hashed = 0; - let ret = self.ecx.lazy_seq(iter.inspect(|item| { - item.hash_stable(hcx, hasher); - num_items_hashed += 1; - })); - - // Sometimes items in a sequence are filtered out without being - // hashed (e.g. for &[ast::Attribute]) and this code path cannot - // handle that correctly, so we want to make sure we didn't hit - // it by accident. - if lower_bound != num_items_hashed { - bug!("Hashed a different number of items ({}) than expected ({})", - num_items_hashed, - lower_bound); - } - debug!("metadata-hash: {:?}", hasher); - ret - } else { - // Collect into a vec so we know the length of the sequence - let items: AccumulateVec<[T; 32]> = iter.collect(); - items.hash_stable(hcx, hasher); - debug!("metadata-hash: {:?}", hasher); - self.ecx.lazy_seq(items) - } - } else { - self.ecx.lazy_seq(iter) - } + self.ecx.lazy_seq(iter) } pub fn lazy_seq_ref<'x, I, T>(&mut self, iter: I) -> LazySeq where I: IntoIterator, - T: 'x + Encodable + HashStable> + T: 'x + Encodable { - if let Some((ref mut hcx, ref mut hasher)) = self.hcx { - let iter = iter.into_iter(); - let (lower_bound, upper_bound) = iter.size_hint(); - - if upper_bound == Some(lower_bound) { - lower_bound.hash_stable(hcx, hasher); - let mut num_items_hashed = 0; - let ret = self.ecx.lazy_seq_ref(iter.inspect(|item| { - item.hash_stable(hcx, hasher); - num_items_hashed += 1; - })); - - // Sometimes items in a sequence are filtered out without being - // hashed (e.g. for &[ast::Attribute]) and this code path cannot - // handle that correctly, so we want to make sure we didn't hit - // it by accident. - if lower_bound != num_items_hashed { - bug!("Hashed a different number of items ({}) than expected ({})", - num_items_hashed, - lower_bound); - } - debug!("metadata-hash: {:?}", hasher); - ret - } else { - // Collect into a vec so we know the length of the sequence - let items: AccumulateVec<[&'x T; 32]> = iter.collect(); - items.hash_stable(hcx, hasher); - debug!("metadata-hash: {:?}", hasher); - self.ecx.lazy_seq_ref(items.iter().map(|x| *x)) - } - } else { - self.ecx.lazy_seq_ref(iter) - } + self.ecx.lazy_seq_ref(iter) } pub fn lazy_seq_from_slice(&mut self, slice: &[T]) -> LazySeq - where T: Encodable + HashStable> + where T: Encodable { - if let Some((ref mut hcx, ref mut hasher)) = self.hcx { - slice.hash_stable(hcx, hasher); - debug!("metadata-hash: {:?}", hasher); - } self.ecx.lazy_seq_ref(slice.iter()) } pub fn lazy_seq_ref_from_slice(&mut self, slice: &[&T]) -> LazySeq - where T: Encodable + HashStable> + where T: Encodable { - if let Some((ref mut hcx, ref mut hasher)) = self.hcx { - slice.hash_stable(hcx, hasher); - debug!("metadata-hash: {:?}", hasher); - } self.ecx.lazy_seq_ref(slice.iter().map(|x| *x)) } } diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 54dbb68667b3a..6c1ca36232307 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -15,14 +15,15 @@ #![feature(box_patterns)] #![feature(conservative_impl_trait)] -#![feature(core_intrinsics)] #![feature(i128_type)] +#![feature(libc)] #![feature(proc_macro_internals)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(specialization)] #![feature(rustc_private)] +extern crate libc; #[macro_use] extern crate log; #[macro_use] @@ -55,6 +56,7 @@ mod link_args; pub mod creader; pub mod cstore; +pub mod dynamic_lib; pub mod locator; __build_diagnostic_array! { librustc_metadata, DIAGNOSTICS } diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index 19f7cb0ee238a..8abccb503d6fa 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -311,98 +311,96 @@ impl<'a> Context<'a> { &None => String::new(), &Some(ref r) => format!(" which `{}` depends on", r.ident), }; + let mut msg = "the following crate versions were found:".to_string(); let mut err = if !self.rejected_via_hash.is_empty() { - struct_span_err!(self.sess, - self.span, - E0460, - "found possibly newer version of crate `{}`{}", - self.ident, - add) - } else if !self.rejected_via_triple.is_empty() { - struct_span_err!(self.sess, - self.span, - E0461, - "couldn't find crate `{}` with expected target triple {}{}", - self.ident, - self.triple, - add) - } else if !self.rejected_via_kind.is_empty() { - struct_span_err!(self.sess, - self.span, - E0462, - "found staticlib `{}` instead of rlib or dylib{}", - self.ident, - add) - } else if !self.rejected_via_version.is_empty() { - struct_span_err!(self.sess, - self.span, - E0514, - "found crate `{}` compiled by an incompatible version of rustc{}", - self.ident, - add) - } else { let mut err = struct_span_err!(self.sess, self.span, - E0463, - "can't find crate for `{}`{}", + E0460, + "found possibly newer version of crate `{}`{}", self.ident, add); - - if (self.ident == "std" || self.ident == "core") - && self.triple != config::host_triple() { - err.note(&format!("the `{}` target may not be installed", self.triple)); - } - err.span_label(self.span, "can't find crate"); - err - }; - - if !self.rejected_via_triple.is_empty() { - let mismatches = self.rejected_via_triple.iter(); - for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() { - err.note(&format!("crate `{}`, path #{}, triple {}: {}", - self.ident, - i + 1, - got, - path.display())); - } - } - if !self.rejected_via_hash.is_empty() { err.note("perhaps that crate needs to be recompiled?"); let mismatches = self.rejected_via_hash.iter(); - for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display())); + for &CrateMismatch { ref path, .. } in mismatches { + msg.push_str(&format!("\ncrate `{}`: {}", self.ident, path.display())); } match self.root { &None => {} &Some(ref r) => { - for (i, path) in r.paths().iter().enumerate() { - err.note(&format!("crate `{}` path #{}: {}", - r.ident, - i + 1, - path.display())); + for path in r.paths().iter() { + msg.push_str(&format!("\ncrate `{}`: {}", r.ident, path.display())); } } } - } - if !self.rejected_via_kind.is_empty() { + err.note(&msg); + err + } else if !self.rejected_via_triple.is_empty() { + let mut err = struct_span_err!(self.sess, + self.span, + E0461, + "couldn't find crate `{}` \ + with expected target triple {}{}", + self.ident, + self.triple, + add); + let mismatches = self.rejected_via_triple.iter(); + for &CrateMismatch { ref path, ref got } in mismatches { + msg.push_str(&format!("\ncrate `{}`, target triple {}: {}", + self.ident, + got, + path.display())); + } + err.note(&msg); + err + } else if !self.rejected_via_kind.is_empty() { + let mut err = struct_span_err!(self.sess, + self.span, + E0462, + "found staticlib `{}` instead of rlib or dylib{}", + self.ident, + add); err.help("please recompile that crate using --crate-type lib"); let mismatches = self.rejected_via_kind.iter(); - for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display())); + for &CrateMismatch { ref path, .. } in mismatches { + msg.push_str(&format!("\ncrate `{}`: {}", self.ident, path.display())); } - } - if !self.rejected_via_version.is_empty() { + err.note(&msg); + err + } else if !self.rejected_via_version.is_empty() { + let mut err = struct_span_err!(self.sess, + self.span, + E0514, + "found crate `{}` compiled by an incompatible version \ + of rustc{}", + self.ident, + add); err.help(&format!("please recompile that crate using this compiler ({})", rustc_version())); let mismatches = self.rejected_via_version.iter(); - for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() { - err.note(&format!("crate `{}` path #{}: {} compiled by {:?}", - self.ident, - i + 1, - path.display(), - got)); + for &CrateMismatch { ref path, ref got } in mismatches { + msg.push_str(&format!("\ncrate `{}` compiled by {}: {}", + self.ident, + got, + path.display())); } - } + err.note(&msg); + err + } else { + let mut err = struct_span_err!(self.sess, + self.span, + E0463, + "can't find crate for `{}`{}", + self.ident, + add); + + if (self.ident == "std" || self.ident == "core") + && self.triple != config::host_triple() { + err.note(&format!("the `{}` target may not be installed", self.triple)); + } + err.span_label(self.span, "can't find crate"); + err + }; + if !self.rejected_via_filename.is_empty() { let dylibname = self.dylibname(); let mismatches = self.rejected_via_filename.iter(); @@ -534,16 +532,23 @@ impl<'a> Context<'a> { E0464, "multiple matching crates for `{}`", self.crate_name); - err.note("candidates:"); - for (_, lib) in libraries { - if let Some((ref p, _)) = lib.dylib { - err.note(&format!("path: {}", p.display())); - } - if let Some((ref p, _)) = lib.rlib { - err.note(&format!("path: {}", p.display())); + let candidates = libraries.iter().filter_map(|(_, lib)| { + let crate_name = &lib.metadata.get_root().name.as_str(); + match &(&lib.dylib, &lib.rlib) { + &(&Some((ref pd, _)), &Some((ref pr, _))) => { + Some(format!("\ncrate `{}`: {}\n{:>padding$}", + crate_name, + pd.display(), + pr.display(), + padding=8 + crate_name.len())) + } + &(&Some((ref p, _)), &None) | &(&None, &Some((ref p, _))) => { + Some(format!("\ncrate `{}`: {}", crate_name, p.display())) + } + &(&None, &None) => None, } - note_crate_name(&mut err, &lib.metadata.get_root().name.as_str()); - } + }).collect::(); + err.note(&format!("candidates:{}", candidates)); err.emit(); None } @@ -815,10 +820,6 @@ impl<'a> Context<'a> { } } -pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) { - err.note(&format!("crate name: {}", name)); -} - // Just a small wrapper to time how long reading metadata takes. fn get_metadata_section(target: &Target, flavor: CrateFlavor, diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index dad0d26d2715d..8ff327463917a 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -18,6 +18,7 @@ use rustc::ich::StableHashingContext; use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary}; use rustc::middle::lang_items; use rustc::mir; +use rustc::session::CrateDisambiguator; use rustc::ty::{self, Ty, ReprOptions}; use rustc_back::PanicStrategy; @@ -53,11 +54,6 @@ pub const METADATA_VERSION: u8 = 4; pub const METADATA_HEADER: &'static [u8; 12] = &[0, 0, 0, 0, b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; -/// The shorthand encoding uses an enum's variant index `usize` -/// and is offset by this value so it never matches a real variant. -/// This offset is also chosen so that the first byte is never < 0x80. -pub const SHORTHAND_OFFSET: usize = 0x80; - /// A value of type T referred to by its absolute position /// in the metadata, and which can be decoded lazily. /// @@ -191,7 +187,7 @@ pub struct CrateRoot { pub name: Symbol, pub triple: String, pub hash: hir::svh::Svh, - pub disambiguator: Symbol, + pub disambiguator: CrateDisambiguator, pub panic_strategy: PanicStrategy, pub has_global_allocator: bool, pub has_default_lib_allocator: bool, @@ -291,6 +287,7 @@ pub enum EntryKind<'tcx> { ForeignImmStatic, ForeignMutStatic, ForeignMod, + ForeignType, GlobalAsm, Type, Enum(ReprOptions), @@ -306,7 +303,7 @@ pub enum EntryKind<'tcx> { Generator(Lazy>), Trait(Lazy>), Impl(Lazy>), - DefaultImpl(Lazy>), + AutoImpl(Lazy>), Method(Lazy>), AssociatedType(AssociatedContainer), AssociatedConst(AssociatedContainer, u8), @@ -324,6 +321,7 @@ impl<'gcx> HashStable> for EntryKind<'gcx> { EntryKind::ForeignMutStatic | EntryKind::ForeignMod | EntryKind::GlobalAsm | + EntryKind::ForeignType | EntryKind::Field | EntryKind::Type => { // Nothing else to hash here. @@ -361,7 +359,7 @@ impl<'gcx> HashStable> for EntryKind<'gcx> { EntryKind::Trait(ref trait_data) => { trait_data.hash_stable(hcx, hasher); } - EntryKind::DefaultImpl(ref impl_data) | + EntryKind::AutoImpl(ref impl_data) | EntryKind::Impl(ref impl_data) => { impl_data.hash_stable(hcx, hasher); } @@ -428,14 +426,14 @@ impl_stable_hash_for!(struct VariantData<'tcx> { pub struct TraitData<'tcx> { pub unsafety: hir::Unsafety, pub paren_sugar: bool, - pub has_default_impl: bool, + pub has_auto_impl: bool, pub super_predicates: Lazy>, } impl_stable_hash_for!(struct TraitData<'tcx> { unsafety, paren_sugar, - has_default_impl, + has_auto_impl, super_predicates }); @@ -514,14 +512,12 @@ impl_stable_hash_for!(struct MethodData<'tcx> { fn_data, container, has_self }); #[derive(RustcEncodable, RustcDecodable)] pub struct ClosureData<'tcx> { - pub kind: ty::ClosureKind, pub sig: Lazy>, } -impl_stable_hash_for!(struct ClosureData<'tcx> { kind, sig }); +impl_stable_hash_for!(struct ClosureData<'tcx> { sig }); #[derive(RustcEncodable, RustcDecodable)] pub struct GeneratorData<'tcx> { - pub sig: ty::PolyGenSig<'tcx>, pub layout: mir::GeneratorLayout<'tcx>, } -impl_stable_hash_for!(struct GeneratorData<'tcx> { sig, layout }); +impl_stable_hash_for!(struct GeneratorData<'tcx> { layout }); diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 936fd5a774d3c..b7a576babeb67 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -17,5 +17,6 @@ rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } +serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs deleted file mode 100644 index 902e2de841f26..0000000000000 --- a/src/librustc_mir/borrow_check.rs +++ /dev/null @@ -1,1274 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! This query borrow-checks the MIR to (further) ensure it is not broken. - -use rustc::hir::def_id::{DefId}; -use rustc::infer::{InferCtxt}; -use rustc::ty::{self, TyCtxt, ParamEnv}; -use rustc::ty::maps::Providers; -use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue}; -use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; -use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind}; -use rustc::mir::transform::{MirSource}; - -use rustc_data_structures::indexed_set::{self, IdxSetBuf}; -use rustc_data_structures::indexed_vec::{Idx}; - -use syntax::ast::{self}; -use syntax_pos::{DUMMY_SP, Span}; - -use dataflow::{do_dataflow}; -use dataflow::{MoveDataParamEnv}; -use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; -use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; -use dataflow::{Borrows, BorrowData, BorrowIndex}; -use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; -use util::borrowck_errors::{BorrowckErrors, Origin}; - -use self::MutateMode::{JustWrite, WriteAndRead}; -use self::ConsumeKind::{Consume}; - - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - mir_borrowck, - ..*providers - }; -} - -fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { - let mir = tcx.mir_validated(def_id); - let src = MirSource::from_local_def_id(tcx, def_id); - debug!("run query mir_borrowck: {}", tcx.node_path_str(src.item_id())); - - let mir: &Mir<'tcx> = &mir.borrow(); - if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.debugging_opts.borrowck_mir { - return; - } - - let id = src.item_id(); - let attributes = tcx.get_attrs(def_id); - let param_env = tcx.param_env(def_id); - tcx.infer_ctxt().enter(|_infcx| { - - let move_data = MoveData::gather_moves(mir, tcx, param_env); - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; - let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir), - |bd, i| bd.location(i)); - let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeUninitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - - let mut mbcx = MirBorrowckCtxt { - tcx: tcx, - mir: mir, - node_id: id, - move_data: &mdpe.move_data, - param_env: param_env, - fake_infer_ctxt: &_infcx, - }; - - let mut state = InProgress::new(flow_borrows, - flow_inits, - flow_uninits); - - mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer - }); - - debug!("mir_borrowck done"); -} - -#[allow(dead_code)] -pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'gcx>, - mir: &'b Mir<'gcx>, - node_id: ast::NodeId, - move_data: &'b MoveData<'gcx>, - param_env: ParamEnv<'tcx>, - fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>, -} - -// (forced to be `pub` due to its use as an associated type below.) -pub struct InProgress<'b, 'tcx: 'b> { - borrows: FlowInProgress>, - inits: FlowInProgress>, - uninits: FlowInProgress>, -} - -struct FlowInProgress where BD: BitDenotation { - base_results: DataflowResults, - curr_state: IdxSetBuf, - stmt_gen: IdxSetBuf, - stmt_kill: IdxSetBuf, -} - -// Check that: -// 1. assignments are always made to mutable locations (FIXME: does that still really go here?) -// 2. loans made in overlapping scopes do not conflict -// 3. assignments do not affect things loaned out as immutable -// 4. moves do not affect things loaned out in any way -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx> - for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> -{ - type FlowState = InProgress<'b, 'gcx>; - - fn mir(&self) -> &'b Mir<'gcx> { self.mir } - - fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reset_to_entry_of(bb), - |i| i.reset_to_entry_of(bb), - |u| u.reset_to_entry_of(bb)); - } - - fn reconstruct_statement_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_statement_effect(location), - |i| i.reconstruct_statement_effect(location), - |u| u.reconstruct_statement_effect(location)); - } - - fn apply_local_effect(&mut self, - _location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.apply_local_effect(), - |i| i.apply_local_effect(), - |u| u.apply_local_effect()); - } - - fn reconstruct_terminator_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_terminator_effect(location), - |i| i.reconstruct_terminator_effect(location), - |u| u.reconstruct_terminator_effect(location)); - } - - fn visit_block_entry(&mut self, - bb: BasicBlock, - flow_state: &Self::FlowState) { - let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary); - } - - fn visit_statement_entry(&mut self, - location: Location, - stmt: &Statement<'gcx>, - flow_state: &Self::FlowState) { - let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary); - let span = stmt.source_info.span; - match stmt.kind { - StatementKind::Assign(ref lhs, ref rhs) => { - // NOTE: NLL RFC calls for *shallow* write; using Deep - // for short-term compat w/ AST-borrowck. Also, switch - // to shallow requires to dataflow: "if this is an - // assignment `lv = `, then any loan for some - // path P of which `lv` is a prefix is killed." - self.mutate_lvalue(ContextKind::AssignLhs.new(location), - (lhs, span), Deep, JustWrite, flow_state); - - self.consume_rvalue(ContextKind::AssignRhs.new(location), - (rhs, span), location, flow_state); - } - StatementKind::SetDiscriminant { ref lvalue, variant_index: _ } => { - self.mutate_lvalue(ContextKind::SetDiscrim.new(location), - (lvalue, span), - Shallow(Some(ArtificialField::Discriminant)), - JustWrite, - flow_state); - } - StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { - for (o, output) in asm.outputs.iter().zip(outputs) { - if o.is_indirect { - self.consume_lvalue(ContextKind::InlineAsm.new(location), - Consume, - (output, span), - flow_state); - } else { - self.mutate_lvalue(ContextKind::InlineAsm.new(location), - (output, span), - Deep, - if o.is_rw { WriteAndRead } else { JustWrite }, - flow_state); - } - } - for input in inputs { - self.consume_operand(ContextKind::InlineAsm.new(location), - Consume, - (input, span), flow_state); - } - } - StatementKind::EndRegion(ref _rgn) => { - // ignored when consuming results (update to - // flow_state already handled). - } - StatementKind::Nop | - StatementKind::Validate(..) | - StatementKind::StorageLive(..) => { - // `Nop`, `Validate`, and `StorageLive` are irrelevant - // to borrow check. - } - - StatementKind::StorageDead(local) => { - self.access_lvalue(ContextKind::StorageDead.new(location), - (&Lvalue::Local(local), span), - (Shallow(None), Write(WriteKind::StorageDead)), - flow_state); - } - } - } - - fn visit_terminator_entry(&mut self, - location: Location, - term: &Terminator<'gcx>, - flow_state: &Self::FlowState) { - let loc = location; - let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary); - let span = term.source_info.span; - match term.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { - self.consume_operand(ContextKind::SwitchInt.new(loc), - Consume, - (discr, span), flow_state); - } - TerminatorKind::Drop { location: ref drop_lvalue, target: _, unwind: _ } => { - self.consume_lvalue(ContextKind::Drop.new(loc), - ConsumeKind::Drop, - (drop_lvalue, span), flow_state); - } - TerminatorKind::DropAndReplace { location: ref drop_lvalue, - value: ref new_value, - target: _, - unwind: _ } => { - self.mutate_lvalue(ContextKind::DropAndReplace.new(loc), - (drop_lvalue, span), - Deep, - JustWrite, - flow_state); - self.consume_operand(ContextKind::DropAndReplace.new(loc), - ConsumeKind::Drop, - (new_value, span), flow_state); - } - TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.consume_operand(ContextKind::CallOperator.new(loc), - Consume, - (func, span), flow_state); - for arg in args { - self.consume_operand(ContextKind::CallOperand.new(loc), - Consume, - (arg, span), flow_state); - } - if let Some((ref dest, _/*bb*/)) = *destination { - self.mutate_lvalue(ContextKind::CallDest.new(loc), - (dest, span), - Deep, - JustWrite, - flow_state); - } - } - TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { - self.consume_operand(ContextKind::Assert.new(loc), - Consume, - (cond, span), flow_state); - match *msg { - AssertMessage::BoundsCheck { ref len, ref index } => { - self.consume_operand(ContextKind::Assert.new(loc), - Consume, - (len, span), flow_state); - self.consume_operand(ContextKind::Assert.new(loc), - Consume, - (index, span), flow_state); - } - AssertMessage::Math(_/*const_math_err*/) => {} - AssertMessage::GeneratorResumedAfterReturn => {} - AssertMessage::GeneratorResumedAfterPanic => {} - } - } - - TerminatorKind::Yield { ref value, resume: _, drop: _} => { - self.consume_operand(ContextKind::Yield.new(loc), - Consume, (value, span), flow_state); - } - - TerminatorKind::Goto { target: _ } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable => { - // no data used, thus irrelevant to borrowck - } - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum MutateMode { JustWrite, WriteAndRead } - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ConsumeKind { Drop, Consume } - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum Control { Continue, Break } - -use self::ShallowOrDeep::{Shallow, Deep}; -use self::ReadOrWrite::{Read, Write}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ArtificialField { - Discriminant, - ArrayLength, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ShallowOrDeep { - /// From the RFC: "A *shallow* access means that the immediate - /// fields reached at LV are accessed, but references or pointers - /// found within are not dereferenced. Right now, the only access - /// that is shallow is an assignment like `x = ...;`, which would - /// be a *shallow write* of `x`." - Shallow(Option), - - /// From the RFC: "A *deep* access means that all data reachable - /// through the given lvalue may be invalidated or accesses by - /// this action." - Deep, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ReadOrWrite { - /// From the RFC: "A *read* means that the existing data may be - /// read, but will not be changed." - Read(ReadKind), - - /// From the RFC: "A *write* means that the data may be mutated to - /// new values or otherwise invalidated (for example, it could be - /// de-initialized, as in a move operation). - Write(WriteKind), -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ReadKind { - Borrow(BorrowKind), - Copy, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum WriteKind { - StorageDead, - MutableBorrow(BorrowKind), - Mutate, - Move, -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - fn access_lvalue(&mut self, - context: Context, - lvalue_span: (&Lvalue<'gcx>, Span), - kind: (ShallowOrDeep, ReadOrWrite), - flow_state: &InProgress<'b, 'gcx>) { - // FIXME: also need to check permissions (e.g. reject mut - // borrow of immutable ref, moves through non-`Box`-ref) - let (sd, rw) = kind; - self.each_borrow_involving_path( - context, (sd, lvalue_span.0), flow_state, |this, _index, borrow| { - match (rw, borrow.kind) { - (Read(_), BorrowKind::Shared) => { - Control::Continue - } - (Read(kind), BorrowKind::Unique) | - (Read(kind), BorrowKind::Mut) => { - match kind { - ReadKind::Copy => - this.report_use_while_mutably_borrowed( - context, lvalue_span, borrow), - ReadKind::Borrow(bk) => - this.report_conflicting_borrow( - context, lvalue_span, - (lvalue_span.0, bk), (&borrow.lvalue, borrow.kind)), - } - Control::Break - } - (Write(kind), _) => { - match kind { - WriteKind::MutableBorrow(bk) => - this.report_conflicting_borrow( - context, lvalue_span, - (lvalue_span.0, bk), (&borrow.lvalue, borrow.kind)), - WriteKind::StorageDead | - WriteKind::Mutate => - this.report_illegal_mutation_of_borrowed( - context, lvalue_span, borrow), - WriteKind::Move => - this.report_move_out_while_borrowed( - context, lvalue_span, borrow), - } - Control::Break - } - } - }); - } - - fn mutate_lvalue(&mut self, - context: Context, - lvalue_span: (&Lvalue<'gcx>, Span), - kind: ShallowOrDeep, - mode: MutateMode, - flow_state: &InProgress<'b, 'gcx>) { - // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. - match mode { - MutateMode::WriteAndRead => { - self.check_if_path_is_moved(context, "update", lvalue_span, flow_state); - } - MutateMode::JustWrite => { - self.check_if_assigned_path_is_moved(context, lvalue_span, flow_state); - } - } - - self.access_lvalue(context, lvalue_span, (kind, Write(WriteKind::Mutate)), flow_state); - - // check for reassignments to immutable local variables - self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state); - } - - fn consume_rvalue(&mut self, - context: Context, - (rvalue, span): (&Rvalue<'gcx>, Span), - _location: Location, - flow_state: &InProgress<'b, 'gcx>) { - match *rvalue { - Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => { - let access_kind = match bk { - BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), - BorrowKind::Unique | - BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))), - }; - self.access_lvalue(context, (lvalue, span), access_kind, flow_state); - self.check_if_path_is_moved(context, "borrow", (lvalue, span), flow_state); - } - - Rvalue::Use(ref operand) | - Rvalue::Repeat(ref operand, _) | - Rvalue::UnaryOp(_/*un_op*/, ref operand) | - Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => { - self.consume_operand(context, Consume, (operand, span), flow_state) - } - - Rvalue::Len(ref lvalue) | - Rvalue::Discriminant(ref lvalue) => { - let af = match *rvalue { - Rvalue::Len(..) => ArtificialField::ArrayLength, - Rvalue::Discriminant(..) => ArtificialField::Discriminant, - _ => unreachable!(), - }; - self.access_lvalue( - context, (lvalue, span), (Shallow(Some(af)), Read(ReadKind::Copy)), flow_state); - self.check_if_path_is_moved(context, "use", (lvalue, span), flow_state); - } - - Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | - Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { - self.consume_operand(context, Consume, (operand1, span), flow_state); - self.consume_operand(context, Consume, (operand2, span), flow_state); - } - - Rvalue::NullaryOp(_op, _ty) => { - // nullary ops take no dynamic input; no borrowck effect. - // - // FIXME: is above actually true? Do we want to track - // the fact that uninitialized data can be created via - // `NullOp::Box`? - } - - Rvalue::Aggregate(ref _aggregate_kind, ref operands) => { - for operand in operands { - self.consume_operand(context, Consume, (operand, span), flow_state); - } - } - } - } - - fn consume_operand(&mut self, - context: Context, - consume_via_drop: ConsumeKind, - (operand, span): (&Operand<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { - match *operand { - Operand::Consume(ref lvalue) => { - self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state) - } - Operand::Constant(_) => {} - } - } - - fn consume_lvalue(&mut self, - context: Context, - consume_via_drop: ConsumeKind, - lvalue_span: (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { - let lvalue = lvalue_span.0; - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - let moves_by_default = - self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP); - if moves_by_default { - // move of lvalue: check if this is move of already borrowed path - self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state); - } else { - // copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs) - self.access_lvalue(context, lvalue_span, (Deep, Read(ReadKind::Copy)), flow_state); - } - - // Finally, check if path was already moved. - match consume_via_drop { - ConsumeKind::Drop => { - // If path is merely being dropped, then we'll already - // check the drop flag to see if it is moved (thus we - // skip this check in that case). - } - ConsumeKind::Consume => { - self.check_if_path_is_moved(context, "use", lvalue_span, flow_state); - } - } - } -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - fn check_if_reassignment_to_immutable_state(&mut self, - context: Context, - (lvalue, span): (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { - let move_data = flow_state.inits.base_results.operator().move_data(); - - // determine if this path has a non-mut owner (and thus needs checking). - let mut l = lvalue; - loop { - match *l { - Lvalue::Projection(ref proj) => { - l = &proj.base; - continue; - } - Lvalue::Local(local) => { - match self.mir.local_decls[local].mutability { - Mutability::Not => break, // needs check - Mutability::Mut => return, - } - } - Lvalue::Static(_) => { - // mutation of non-mut static is always illegal, - // independent of dataflow. - self.report_assignment_to_static(context, (lvalue, span)); - return; - } - } - } - - if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) { - if flow_state.inits.curr_state.contains(&mpi) { - // may already be assigned before reaching this statement; - // report error. - // FIXME: Not ideal, it only finds the assignment that lexically comes first - let assigned_lvalue = &move_data.move_paths[mpi].lvalue; - let assignment_stmt = self.mir.basic_blocks().iter().filter_map(|bb| { - bb.statements.iter().find(|stmt| { - if let StatementKind::Assign(ref lv, _) = stmt.kind { - *lv == *assigned_lvalue - } else { - false - } - }) - }).next().unwrap(); - self.report_illegal_reassignment( - context, (lvalue, span), assignment_stmt.source_info.span); - } - } - } - - fn check_if_path_is_moved(&mut self, - context: Context, - desired_action: &str, - lvalue_span: (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { - // FIXME: analogous code in check_loans first maps `lvalue` to - // its base_path ... but is that what we want here? - let lvalue = self.base_path(lvalue_span.0); - - let maybe_uninits = &flow_state.uninits; - let move_data = maybe_uninits.base_results.operator().move_data(); - if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) { - if maybe_uninits.curr_state.contains(&mpi) { - // find and report move(s) that could cause this to be uninitialized - self.report_use_of_moved(context, desired_action, lvalue_span); - } else { - // sanity check: initialized on *some* path, right? - assert!(flow_state.inits.curr_state.contains(&mpi)); - } - } - } - - fn move_path_for_lvalue(&mut self, - _context: Context, - move_data: &MoveData<'gcx>, - lvalue: &Lvalue<'gcx>) - -> Option - { - // If returns None, then there is no move path corresponding - // to a direct owner of `lvalue` (which means there is nothing - // that borrowck tracks for its analysis). - - match move_data.rev_lookup.find(lvalue) { - LookupResult::Parent(_) => None, - LookupResult::Exact(mpi) => Some(mpi), - } - } - - fn check_if_assigned_path_is_moved(&mut self, - context: Context, - (lvalue, span): (&Lvalue<'gcx>, Span), - flow_state: &InProgress<'b, 'gcx>) { - // recur down lvalue; dispatch to check_if_path_is_moved when necessary - let mut lvalue = lvalue; - loop { - match *lvalue { - Lvalue::Local(_) | Lvalue::Static(_) => { - // assigning to `x` does not require `x` be initialized. - break; - } - Lvalue::Projection(ref proj) => { - let Projection { ref base, ref elem } = **proj; - match *elem { - ProjectionElem::Deref | - // assigning to *P requires `P` initialized. - ProjectionElem::Index(_/*operand*/) | - ProjectionElem::ConstantIndex { .. } | - // assigning to P[i] requires `P` initialized. - ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => - // assigning to (P->variant) is okay if assigning to `P` is okay - // - // FIXME: is this true even if P is a adt with a dtor? - { } - - ProjectionElem::Subslice { .. } => { - panic!("we dont allow assignments to subslices, context: {:?}", - context); - } - - ProjectionElem::Field(..) => { - // if type of `P` has a dtor, then - // assigning to `P.f` requires `P` itself - // be already initialized - let tcx = self.tcx; - match base.ty(self.mir, tcx).to_ty(tcx).sty { - ty::TyAdt(def, _) if def.has_dtor(tcx) => { - - // FIXME: analogous code in - // check_loans.rs first maps - // `base` to its base_path. - - self.check_if_path_is_moved( - context, "assignment", (base, span), flow_state); - - // (base initialized; no need to - // recur further) - break; - } - _ => {} - } - } - } - - lvalue = base; - continue; - } - } - } - } -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - fn each_borrow_involving_path(&mut self, - _context: Context, - access_lvalue: (ShallowOrDeep, &Lvalue<'gcx>), - flow_state: &InProgress<'b, 'gcx>, - mut op: F) - where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>) -> Control - { - let (access, lvalue) = access_lvalue; - - // FIXME: analogous code in check_loans first maps `lvalue` to - // its base_path. - - let domain = flow_state.borrows.base_results.operator(); - let data = domain.borrows(); - - // check for loan restricting path P being used. Accounts for - // borrows of P, P.a.b, etc. - 'next_borrow: for i in flow_state.borrows.elems_incoming() { - let borrowed = &data[i]; - - // Is `lvalue` (or a prefix of it) already borrowed? If - // so, that's relevant. - // - // FIXME: Differs from AST-borrowck; includes drive-by fix - // to #38899. Will probably need back-compat mode flag. - for accessed_prefix in self.prefixes(lvalue, PrefixSet::All) { - if *accessed_prefix == borrowed.lvalue { - // FIXME: pass in prefix here too? And/or enum - // describing case we are in? - let ctrl = op(self, i, borrowed); - if ctrl == Control::Break { return; } - } - } - - // Is `lvalue` a prefix (modulo access type) of the - // `borrowed.lvalue`? If so, that's relevant. - - let prefix_kind = match access { - Shallow(Some(ArtificialField::Discriminant)) | - Shallow(Some(ArtificialField::ArrayLength)) => { - // The discriminant and array length are like - // additional fields on the type; they do not - // overlap any existing data there. Furthermore, - // they cannot actually be a prefix of any - // borrowed lvalue (at least in MIR as it is - // currently.) - continue 'next_borrow; - } - Shallow(None) => PrefixSet::Shallow, - Deep => PrefixSet::Supporting, - }; - - for borrowed_prefix in self.prefixes(&borrowed.lvalue, prefix_kind) { - if borrowed_prefix == lvalue { - // FIXME: pass in prefix here too? And/or enum - // describing case we are in? - let ctrl = op(self, i, borrowed); - if ctrl == Control::Break { return; } - } - } - } - } -} - -use self::prefixes::PrefixSet; - -/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an -/// lvalue are formed by stripping away fields and derefs, except that -/// we stop when we reach the deref of a shared reference. [...] " -/// -/// "Shallow prefixes are found by stripping away fields, but stop at -/// any dereference. So: writing a path like `a` is illegal if `a.b` -/// is borrowed. But: writing `a` is legal if `*a` is borrowed, -/// whether or not `a` is a shared or mutable reference. [...] " -mod prefixes { - use super::{MirBorrowckCtxt}; - - use rustc::hir; - use rustc::ty::{self, TyCtxt}; - use rustc::mir::{Lvalue, Mir, ProjectionElem}; - - pub(super) struct Prefixes<'c, 'tcx: 'c> { - mir: &'c Mir<'tcx>, - tcx: TyCtxt<'c, 'tcx, 'tcx>, - kind: PrefixSet, - next: Option<&'c Lvalue<'tcx>>, - } - - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - pub(super) enum PrefixSet { - All, - Shallow, - Supporting, - } - - impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - pub(super) fn prefixes<'d>(&self, - lvalue: &'d Lvalue<'gcx>, - kind: PrefixSet) - -> Prefixes<'d, 'gcx> where 'b: 'd - { - Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx } - } - } - - impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> { - type Item = &'c Lvalue<'tcx>; - fn next(&mut self) -> Option { - let mut cursor = match self.next { - None => return None, - Some(lvalue) => lvalue, - }; - - // Post-processing `lvalue`: Enqueue any remaining - // work. Also, `lvalue` may not be a prefix itself, but - // may hold one further down (e.g. we never return - // downcasts here, but may return a base of a downcast). - - 'cursor: loop { - let proj = match *cursor { - Lvalue::Local(_) | // search yielded this leaf - Lvalue::Static(_) => { - self.next = None; - return Some(cursor); - } - - Lvalue::Projection(ref proj) => proj, - }; - - match proj.elem { - ProjectionElem::Field(_/*field*/, _/*ty*/) => { - // FIXME: add union handling - self.next = Some(&proj.base); - return Some(cursor); - } - ProjectionElem::Downcast(..) | - ProjectionElem::Subslice { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Index(_) => { - cursor = &proj.base; - continue 'cursor; - } - ProjectionElem::Deref => { - // (handled below) - } - } - - assert_eq!(proj.elem, ProjectionElem::Deref); - - match self.kind { - PrefixSet::Shallow => { - // shallow prefixes are found by stripping away - // fields, but stop at *any* dereference. - // So we can just stop the traversal now. - self.next = None; - return Some(cursor); - } - PrefixSet::All => { - // all prefixes: just blindly enqueue the base - // of the projection - self.next = Some(&proj.base); - return Some(cursor); - } - PrefixSet::Supporting => { - // fall through! - } - } - - assert_eq!(self.kind, PrefixSet::Supporting); - // supporting prefixes: strip away fields and - // derefs, except we stop at the deref of a shared - // reference. - - let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - match ty.sty { - ty::TyRawPtr(_) | - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { - // don't continue traversing over derefs of raw pointers or shared borrows. - self.next = None; - return Some(cursor); - } - - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { - self.next = Some(&proj.base); - return Some(cursor); - } - - ty::TyAdt(..) if ty.is_box() => { - self.next = Some(&proj.base); - return Some(cursor); - } - - _ => panic!("unknown type fed to Projection Deref."), - } - } - } - } -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - fn report_use_of_moved(&mut self, - _context: Context, - desired_action: &str, - (lvalue, span): (&Lvalue, Span)) { - self.tcx.cannot_act_on_uninitialized_variable(span, - desired_action, - &self.describe_lvalue(lvalue), - Origin::Mir) - .span_label(span, format!("use of possibly uninitialized `{}`", - self.describe_lvalue(lvalue))) - .emit(); - } - - fn report_move_out_while_borrowed(&mut self, - _context: Context, - (lvalue, span): (&Lvalue, Span), - borrow: &BorrowData) { - self.tcx.cannot_move_when_borrowed(span, - &self.describe_lvalue(lvalue), - Origin::Mir) - .span_label(self.retrieve_borrow_span(borrow), - format!("borrow of `{}` occurs here", - self.describe_lvalue(&borrow.lvalue))) - .span_label(span, format!("move out of `{}` occurs here", - self.describe_lvalue(lvalue))) - .emit(); - } - - fn report_use_while_mutably_borrowed(&mut self, - _context: Context, - (lvalue, span): (&Lvalue, Span), - borrow : &BorrowData) { - let described_lvalue = self.describe_lvalue(lvalue); - let borrow_span = self.retrieve_borrow_span(borrow); - - let mut err = self.tcx.cannot_use_when_mutably_borrowed( - span, &described_lvalue, Origin::Mir); - - err.span_label(borrow_span, format!("borrow of `{}` occurs here", described_lvalue)); - err.span_label(span, format!("use of borrowed `{}`", described_lvalue)); - - err.emit(); - } - - fn report_conflicting_borrow(&mut self, - _context: Context, - (lvalue, span): (&Lvalue, Span), - loan1: (&Lvalue, BorrowKind), - loan2: (&Lvalue, BorrowKind)) { - let (loan1_lvalue, loan1_kind) = loan1; - let (loan2_lvalue, loan2_kind) = loan2; - // FIXME: obviously falsifiable. Generalize for non-eq lvalues later. - assert_eq!(loan1_lvalue, loan2_lvalue); - - // FIXME: supply non-"" `opt_via` when appropriate - let mut err = match (loan1_kind, "immutable", "mutable", - loan2_kind, "immutable", "mutable") { - (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | - (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => - self.tcx.cannot_reborrow_already_borrowed( - span, &self.describe_lvalue(lvalue), - "", lft, "it", rgt, "", Origin::Mir), - - (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => - self.tcx.cannot_mutably_borrow_multiply( - span, &self.describe_lvalue(lvalue), "", Origin::Mir), - - (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_uniquely_borrow_by_two_closures( - span, &self.describe_lvalue(lvalue), Origin::Mir), - - (BorrowKind::Unique, _, _, _, _, _) => - self.tcx.cannot_uniquely_borrow_by_one_closure( - span, &self.describe_lvalue(lvalue), "it", "", Origin::Mir), - - (_, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_reborrow_already_uniquely_borrowed( - span, &self.describe_lvalue(lvalue), "it", "", Origin::Mir), - - (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => - unreachable!(), - - // FIXME: add span labels for first and second mutable borrows, as well as - // end point for first. - }; - err.emit(); - } - - fn report_illegal_mutation_of_borrowed(&mut self, - _: Context, - (lvalue, span): (&Lvalue, Span), - loan: &BorrowData) { - let describe_lvalue = self.describe_lvalue(lvalue); - let borrow_span = self.retrieve_borrow_span(loan); - - let mut err = self.tcx.cannot_assign_to_borrowed( - span, &self.describe_lvalue(lvalue), Origin::Mir); - - err.span_label(borrow_span, format!("borrow of `{}` occurs here", describe_lvalue)); - err.span_label(span, format!("assignment to borrowed `{}` occurs here", describe_lvalue)); - - err.emit(); - } - - fn report_illegal_reassignment(&mut self, - _context: Context, - (lvalue, span): (&Lvalue, Span), - assigned_span: Span) { - self.tcx.cannot_reassign_immutable(span, - &self.describe_lvalue(lvalue), - Origin::Mir) - .span_label(span, "re-assignment of immutable variable") - .span_label(assigned_span, format!("first assignment to `{}`", - self.describe_lvalue(lvalue))) - .emit(); - } - - fn report_assignment_to_static(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) { - let mut err = self.tcx.cannot_assign_static( - span, &self.describe_lvalue(lvalue), Origin::Mir); - // FIXME: add span labels for borrow and assignment points - err.emit(); - } -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - // End-user visible description of `lvalue` - fn describe_lvalue(&self, lvalue: &Lvalue) -> String { - let mut buf = String::new(); - self.append_lvalue_to_string(lvalue, &mut buf); - buf - } - - // Appends end-user visible description of `lvalue` to `buf`. - fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String) { - match *lvalue { - Lvalue::Local(local) => { - let local = &self.mir.local_decls[local]; - match local.name { - Some(name) => buf.push_str(&format!("{}", name)), - None => buf.push_str("_"), - } - } - Lvalue::Static(ref static_) => { - buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); - } - Lvalue::Projection(ref proj) => { - let (prefix, suffix, index_operand) = match proj.elem { - ProjectionElem::Deref => - ("(*", format!(")"), None), - ProjectionElem::Downcast(..) => - ("", format!(""), None), // (dont emit downcast info) - ProjectionElem::Field(field, _ty) => - ("", format!(".{}", field.index()), None), // FIXME: report name of field - ProjectionElem::Index(index) => - ("", format!(""), Some(index)), - ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => - ("", format!("[{} of {}]", offset, min_length), None), - ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => - ("", format!("[-{} of {}]", offset, min_length), None), - ProjectionElem::Subslice { from, to: 0 } => - ("", format!("[{}:]", from), None), - ProjectionElem::Subslice { from: 0, to } => - ("", format!("[:-{}]", to), None), - ProjectionElem::Subslice { from, to } => - ("", format!("[{}:-{}]", from, to), None), - }; - buf.push_str(prefix); - self.append_lvalue_to_string(&proj.base, buf); - if let Some(index) = index_operand { - buf.push_str("["); - self.append_lvalue_to_string(&Lvalue::Local(index), buf); - buf.push_str("]"); - } else { - buf.push_str(&suffix); - } - } - } - } - - // Retrieve span of given borrow from the current MIR representation - fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { - self.mir.basic_blocks()[borrow.location.block] - .statements[borrow.location.statement_index] - .source_info.span - } -} - -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { - // FIXME (#16118): function intended to allow the borrow checker - // to be less precise in its handling of Box while still allowing - // moves out of a Box. They should be removed when/if we stop - // treating Box specially (e.g. when/if DerefMove is added...) - - fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> { - //! Returns the base of the leftmost (deepest) dereference of an - //! Box in `lvalue`. If there is no dereference of an Box - //! in `lvalue`, then it just returns `lvalue` itself. - - let mut cursor = lvalue; - let mut deepest = lvalue; - loop { - let proj = match *cursor { - Lvalue::Local(..) | Lvalue::Static(..) => return deepest, - Lvalue::Projection(ref proj) => proj, - }; - if proj.elem == ProjectionElem::Deref && - lvalue.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() - { - deepest = &proj.base; - } - cursor = &proj.base; - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -struct Context { - kind: ContextKind, - loc: Location, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ContextKind { - AssignLhs, - AssignRhs, - SetDiscrim, - InlineAsm, - SwitchInt, - Drop, - DropAndReplace, - CallOperator, - CallOperand, - CallDest, - Assert, - Yield, - StorageDead, -} - -impl ContextKind { - fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } } -} - -impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> { - pub(super) fn new(borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>) - -> Self { - InProgress { - borrows: FlowInProgress::new(borrows), - inits: FlowInProgress::new(inits), - uninits: FlowInProgress::new(uninits), - } - } - - fn each_flow(&mut self, - mut xform_borrows: XB, - mut xform_inits: XI, - mut xform_uninits: XU) where - XB: FnMut(&mut FlowInProgress>), - XI: FnMut(&mut FlowInProgress>), - XU: FnMut(&mut FlowInProgress>), - { - xform_borrows(&mut self.borrows); - xform_inits(&mut self.inits); - xform_uninits(&mut self.uninits); - } - - fn summary(&self) -> String { - let mut s = String::new(); - - s.push_str("borrows in effect: ["); - let mut saw_one = false; - self.borrows.each_state_bit(|borrow| { - if saw_one { s.push_str(", "); }; - saw_one = true; - let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; - s.push_str(&format!("{}", borrow_data)); - }); - s.push_str("] "); - - s.push_str("borrows generated: ["); - let mut saw_one = false; - self.borrows.each_gen_bit(|borrow| { - if saw_one { s.push_str(", "); }; - saw_one = true; - let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; - s.push_str(&format!("{}", borrow_data)); - }); - s.push_str("] "); - - s.push_str("inits: ["); - let mut saw_one = false; - self.inits.each_state_bit(|mpi_init| { - if saw_one { s.push_str(", "); }; - saw_one = true; - let move_path = - &self.inits.base_results.operator().move_data().move_paths[mpi_init]; - s.push_str(&format!("{}", move_path)); - }); - s.push_str("] "); - - s.push_str("uninits: ["); - let mut saw_one = false; - self.uninits.each_state_bit(|mpi_uninit| { - if saw_one { s.push_str(", "); }; - saw_one = true; - let move_path = - &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; - s.push_str(&format!("{}", move_path)); - }); - s.push_str("]"); - - return s; - } -} - -impl FlowInProgress where BD: BitDenotation { - fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f) - } - - fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f) - } - - fn new(results: DataflowResults) -> Self { - let bits_per_block = results.sets().bits_per_block(); - let curr_state = IdxSetBuf::new_empty(bits_per_block); - let stmt_gen = IdxSetBuf::new_empty(bits_per_block); - let stmt_kill = IdxSetBuf::new_empty(bits_per_block); - FlowInProgress { - base_results: results, - curr_state: curr_state, - stmt_gen: stmt_gen, - stmt_kill: stmt_kill, - } - } - - fn reset_to_entry_of(&mut self, bb: BasicBlock) { - (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index())); - } - - fn reconstruct_statement_effect(&mut self, loc: Location) { - self.stmt_gen.reset_to_empty(); - self.stmt_kill.reset_to_empty(); - let mut ignored = IdxSetBuf::new_empty(0); - let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, - }; - self.base_results.operator().statement_effect(&mut sets, loc); - } - - fn reconstruct_terminator_effect(&mut self, loc: Location) { - self.stmt_gen.reset_to_empty(); - self.stmt_kill.reset_to_empty(); - let mut ignored = IdxSetBuf::new_empty(0); - let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, - }; - self.base_results.operator().terminator_effect(&mut sets, loc); - } - - fn apply_local_effect(&mut self) { - self.curr_state.union(&self.stmt_gen); - self.curr_state.subtract(&self.stmt_kill); - } - - fn elems_incoming(&self) -> indexed_set::Elems { - let univ = self.base_results.sets().bits_per_block(); - self.curr_state.elems(univ) - } -} diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs new file mode 100644 index 0000000000000..446aba3d3d72c --- /dev/null +++ b/src/librustc_mir/borrow_check/mod.rs @@ -0,0 +1,2575 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This query borrow-checks the MIR to (further) ensure it is not broken. + +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::infer::InferCtxt; +use rustc::ty::{self, ParamEnv, TyCtxt}; +use rustc::ty::maps::Providers; +use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place}; +use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; +use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::indexed_set::{self, IdxSetBuf}; +use rustc_data_structures::indexed_vec::Idx; + +use syntax::ast; +use syntax_pos::Span; + +use dataflow::do_dataflow; +use dataflow::MoveDataParamEnv; +use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; +use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; +use dataflow::{EverInitializedLvals, MovingOutStatements}; +use dataflow::{BorrowData, BorrowIndex, Borrows}; +use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; +use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex}; +use util::borrowck_errors::{BorrowckErrors, Origin}; + +use self::MutateMode::{JustWrite, WriteAndRead}; + +pub(crate) mod nll; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + mir_borrowck, + ..*providers + }; +} + +fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { + let input_mir = tcx.mir_validated(def_id); + debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); + + if { + !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir() + && !tcx.sess.opts.debugging_opts.nll + } { + return; + } + + tcx.infer_ctxt().enter(|infcx| { + let input_mir: &Mir = &input_mir.borrow(); + do_mir_borrowck(&infcx, input_mir, def_id); + }); + debug!("mir_borrowck done"); +} + +fn do_mir_borrowck<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + input_mir: &Mir<'gcx>, + def_id: DefId, +) { + let tcx = infcx.tcx; + let attributes = tcx.get_attrs(def_id); + let param_env = tcx.param_env(def_id); + let id = tcx.hir + .as_local_node_id(def_id) + .expect("do_mir_borrowck: non-local DefId"); + + // Make our own copy of the MIR. This copy will be modified (in place) to + // contain non-lexical lifetimes. It will have a lifetime tied + // to the inference context. + let mut mir: Mir<'tcx> = input_mir.clone(); + let free_regions = if !tcx.sess.opts.debugging_opts.nll { + None + } else { + let mir = &mut mir; + + // Replace all regions with fresh inference variables. + Some(nll::replace_regions_in_mir(infcx, def_id, mir)) + }; + let mir = &mir; + + let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { + Ok(move_data) => move_data, + Err((move_data, move_errors)) => { + for move_error in move_errors { + let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { + MoveError::UnionMove { .. } => { + unimplemented!("dont know how to report union move errors yet.") + } + MoveError::IllegalMove { + cannot_move_out_of: o, + } => (o.span, o.kind), + }; + let origin = Origin::Mir; + let mut err = match kind { + IllegalMoveOriginKind::Static => { + tcx.cannot_move_out_of(span, "static item", origin) + } + IllegalMoveOriginKind::BorrowedContent => { + tcx.cannot_move_out_of(span, "borrowed content", origin) + } + IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + tcx.cannot_move_out_of_interior_of_drop(span, ty, origin) + } + IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin) + } + }; + err.emit(); + } + move_data + } + }; + + let mdpe = MoveDataParamEnv { + move_data: move_data, + param_env: param_env, + }; + let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); + let mut flow_inits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + )); + let flow_uninits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeUninitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + )); + let flow_move_outs = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MovingOutStatements::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().moves[i], + )); + let flow_ever_inits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + EverInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().inits[i], + )); + + // If we are in non-lexical mode, compute the non-lexical lifetimes. + let opt_regioncx = if let Some(free_regions) = free_regions { + Some(nll::compute_regions( + infcx, + def_id, + free_regions, + mir, + param_env, + &mut flow_inits, + &mdpe.move_data, + )) + } else { + assert!(!tcx.sess.opts.debugging_opts.nll); + None + }; + let flow_inits = flow_inits; // remove mut + + let mut mbcx = MirBorrowckCtxt { + tcx: tcx, + mir: mir, + node_id: id, + move_data: &mdpe.move_data, + param_env: param_env, + storage_dead_or_drop_error_reported: FxHashSet(), + }; + + let flow_borrows = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + Borrows::new(tcx, mir, opt_regioncx), + |bd, i| bd.location(i), + )); + + let mut state = InProgress::new( + flow_borrows, + flow_inits, + flow_uninits, + flow_move_outs, + flow_ever_inits, + ); + + mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer +} + +#[allow(dead_code)] +pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + mir: &'cx Mir<'tcx>, + node_id: ast::NodeId, + move_data: &'cx MoveData<'tcx>, + param_env: ParamEnv<'gcx>, + /// This field keeps track of when storage dead or drop errors are reported + /// in order to stop duplicate error reporting and identify the conditions required + /// for a "temporary value dropped here while still borrowed" error. See #45360. + storage_dead_or_drop_error_reported: FxHashSet, +} + +// (forced to be `pub` due to its use as an associated type below.) +pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, + ever_inits: FlowInProgress>, +} + +struct FlowInProgress +where + BD: BitDenotation, +{ + base_results: DataflowResults, + curr_state: IdxSetBuf, + stmt_gen: IdxSetBuf, + stmt_kill: IdxSetBuf, +} + +// Check that: +// 1. assignments are always made to mutable locations (FIXME: does that still really go here?) +// 2. loans made in overlapping scopes do not conflict +// 3. assignments do not affect things loaned out as immutable +// 4. moves do not affect things loaned out in any way +impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + type FlowState = InProgress<'cx, 'gcx, 'tcx>; + + fn mir(&self) -> &'cx Mir<'tcx> { + self.mir + } + + fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { + flow_state.each_flow( + |b| b.reset_to_entry_of(bb), + |i| i.reset_to_entry_of(bb), + |u| u.reset_to_entry_of(bb), + |m| m.reset_to_entry_of(bb), + |e| e.reset_to_entry_of(bb), + ); + } + + fn reconstruct_statement_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_statement_effect(location), + |i| i.reconstruct_statement_effect(location), + |u| u.reconstruct_statement_effect(location), + |m| m.reconstruct_statement_effect(location), + |e| e.reconstruct_statement_effect(location), + ); + } + + fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) { + flow_state.each_flow( + |b| b.apply_local_effect(), + |i| i.apply_local_effect(), + |u| u.apply_local_effect(), + |m| m.apply_local_effect(), + |e| e.apply_local_effect(), + ); + } + + fn reconstruct_terminator_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_terminator_effect(location), + |i| i.reconstruct_terminator_effect(location), + |u| u.reconstruct_terminator_effect(location), + |m| m.reconstruct_terminator_effect(location), + |e| e.reconstruct_terminator_effect(location), + ); + } + + fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) { + let summary = flow_state.summary(); + debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary); + } + + fn visit_statement_entry( + &mut self, + location: Location, + stmt: &Statement<'tcx>, + flow_state: &Self::FlowState, + ) { + let summary = flow_state.summary(); + debug!( + "MirBorrowckCtxt::process_statement({:?}, {:?}): {}", + location, + stmt, + summary + ); + let span = stmt.source_info.span; + match stmt.kind { + StatementKind::Assign(ref lhs, ref rhs) => { + // NOTE: NLL RFC calls for *shallow* write; using Deep + // for short-term compat w/ AST-borrowck. Also, switch + // to shallow requires to dataflow: "if this is an + // assignment `place = `, then any loan for some + // path P of which `place` is a prefix is killed." + self.mutate_place( + ContextKind::AssignLhs.new(location), + (lhs, span), + Deep, + JustWrite, + flow_state, + ); + + self.consume_rvalue( + ContextKind::AssignRhs.new(location), + (rhs, span), + location, + flow_state, + ); + } + StatementKind::SetDiscriminant { + ref place, + variant_index: _, + } => { + self.mutate_place( + ContextKind::SetDiscrim.new(location), + (place, span), + Shallow(Some(ArtificialField::Discriminant)), + JustWrite, + flow_state, + ); + } + StatementKind::InlineAsm { + ref asm, + ref outputs, + ref inputs, + } => { + let context = ContextKind::InlineAsm.new(location); + for (o, output) in asm.outputs.iter().zip(outputs) { + if o.is_indirect { + // FIXME(eddyb) indirect inline asm outputs should + // be encoeded through MIR place derefs instead. + self.access_place( + context, + (output, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (output, span), + flow_state, + ); + } else { + self.mutate_place( + context, + (output, span), + Deep, + if o.is_rw { WriteAndRead } else { JustWrite }, + flow_state, + ); + } + } + for input in inputs { + self.consume_operand(context, (input, span), flow_state); + } + } + StatementKind::EndRegion(ref _rgn) => { + // ignored when consuming results (update to + // flow_state already handled). + } + StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { + // `Nop`, `Validate`, and `StorageLive` are irrelevant + // to borrow check. + } + + StatementKind::StorageDead(local) => { + self.access_place( + ContextKind::StorageDead.new(location), + (&Place::Local(local), span), + (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + } + } + } + + fn visit_terminator_entry( + &mut self, + location: Location, + term: &Terminator<'tcx>, + flow_state: &Self::FlowState, + ) { + let loc = location; + let summary = flow_state.summary(); + debug!( + "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", + location, + term, + summary + ); + let span = term.source_info.span; + match term.kind { + TerminatorKind::SwitchInt { + ref discr, + switch_ty: _, + values: _, + targets: _, + } => { + self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state); + } + TerminatorKind::Drop { + location: ref drop_place, + target: _, + unwind: _, + } => { + self.access_place( + ContextKind::Drop.new(loc), + (drop_place, span), + (Deep, Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + } + TerminatorKind::DropAndReplace { + location: ref drop_place, + value: ref new_value, + target: _, + unwind: _, + } => { + self.mutate_place( + ContextKind::DropAndReplace.new(loc), + (drop_place, span), + Deep, + JustWrite, + flow_state, + ); + self.consume_operand( + ContextKind::DropAndReplace.new(loc), + (new_value, span), + flow_state, + ); + } + TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup: _, + } => { + self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state); + for arg in args { + self.consume_operand( + ContextKind::CallOperand.new(loc), + (arg, span), + flow_state, + ); + } + if let Some((ref dest, _ /*bb*/)) = *destination { + self.mutate_place( + ContextKind::CallDest.new(loc), + (dest, span), + Deep, + JustWrite, + flow_state, + ); + } + } + TerminatorKind::Assert { + ref cond, + expected: _, + ref msg, + target: _, + cleanup: _, + } => { + self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state); + match *msg { + AssertMessage::BoundsCheck { ref len, ref index } => { + self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state); + self.consume_operand( + ContextKind::Assert.new(loc), + (index, span), + flow_state, + ); + } + AssertMessage::Math(_ /*const_math_err*/) => {} + AssertMessage::GeneratorResumedAfterReturn => {} + AssertMessage::GeneratorResumedAfterPanic => {} + } + } + + TerminatorKind::Yield { + ref value, + resume: _, + drop: _, + } => { + self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state); + } + + TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { + // Returning from the function implicitly kills storage for all locals and statics. + // Often, the storage will already have been killed by an explicit + // StorageDead, but we don't always emit those (notably on unwind paths), + // so this "extra check" serves as a kind of backup. + let domain = flow_state.borrows.base_results.operator(); + let data = domain.borrows(); + flow_state.borrows.with_elems_outgoing(|borrows| { + for i in borrows { + let borrow = &data[i]; + + if self.place_is_invalidated_at_exit(&borrow.place) { + debug!("borrow conflicts at exit {:?}", borrow); + let borrow_span = self.mir.source_info(borrow.location).span; + // FIXME: should be talking about the region lifetime instead + // of just a span here. + let end_span = domain.opt_region_end_span(&borrow.region); + + self.report_borrowed_value_does_not_live_long_enough( + ContextKind::StorageDead.new(loc), + (&borrow.place, borrow_span), + end_span, + ) + } + } + }); + } + TerminatorKind::Goto { target: _ } | + TerminatorKind::Unreachable | + TerminatorKind::FalseEdges { .. } => { + // no data used, thus irrelevant to borrowck + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum MutateMode { + JustWrite, + WriteAndRead, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum Control { + Continue, + Break, +} + +use self::ShallowOrDeep::{Deep, Shallow}; +use self::ReadOrWrite::{Read, Write}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ArtificialField { + Discriminant, + ArrayLength, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ShallowOrDeep { + /// From the RFC: "A *shallow* access means that the immediate + /// fields reached at LV are accessed, but references or pointers + /// found within are not dereferenced. Right now, the only access + /// that is shallow is an assignment like `x = ...;`, which would + /// be a *shallow write* of `x`." + Shallow(Option), + + /// From the RFC: "A *deep* access means that all data reachable + /// through the given place may be invalidated or accesses by + /// this action." + Deep, +} + +/// Kind of access to a value: read or write +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ReadOrWrite { + /// From the RFC: "A *read* means that the existing data may be + /// read, but will not be changed." + Read(ReadKind), + + /// From the RFC: "A *write* means that the data may be mutated to + /// new values or otherwise invalidated (for example, it could be + /// de-initialized, as in a move operation). + Write(WriteKind), +} + +/// Kind of read access to a value +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ReadKind { + Borrow(BorrowKind), + Copy, +} + +/// Kind of write access to a value +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum WriteKind { + StorageDeadOrDrop, + MutableBorrow(BorrowKind), + Mutate, + Move, +} + +/// When checking permissions for a place access, this flag is used to indicate that an immutable +/// local place can be mutated. +/// +/// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: +/// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()` +/// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and +/// `is_declared_mutable()` +/// - Take flow state into consideration in `is_assignable()` for local variables +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum LocalMutationIsAllowed { + Yes, + No, +} + +#[derive(Copy, Clone)] +enum InitializationRequiringAction { + Update, + Borrow, + Use, + Assignment, +} + +impl InitializationRequiringAction { + fn as_noun(self) -> &'static str { + match self { + InitializationRequiringAction::Update => "update", + InitializationRequiringAction::Borrow => "borrow", + InitializationRequiringAction::Use => "use", + InitializationRequiringAction::Assignment => "assign", + } + } + + fn as_verb_in_past_tense(self) -> &'static str { + match self { + InitializationRequiringAction::Update => "updated", + InitializationRequiringAction::Borrow => "borrowed", + InitializationRequiringAction::Use => "used", + InitializationRequiringAction::Assignment => "assigned", + } + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + /// Checks an access to the given place to see if it is allowed. Examines the set of borrows + /// that are in scope, as well as which paths have been initialized, to ensure that (a) the + /// place is initialized and (b) it is not borrowed in some way that would prevent this + /// access. + /// + /// Returns true if an error is reported, false otherwise. + fn access_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: (ShallowOrDeep, ReadOrWrite), + is_local_mutation_allowed: LocalMutationIsAllowed, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + let (sd, rw) = kind; + + let storage_dead_or_drop_local = match (place_span.0, rw) { + (&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local), + _ => None, + }; + + // Check if error has already been reported to stop duplicate reporting. + if let Some(local) = storage_dead_or_drop_local { + if self.storage_dead_or_drop_error_reported.contains(&local) { + return; + } + } + + // Check permissions + let mut error_reported = + self.check_access_permissions(place_span, rw, is_local_mutation_allowed); + + self.each_borrow_involving_path( + context, + (sd, place_span.0), + flow_state, + |this, _index, borrow, common_prefix| match (rw, borrow.kind) { + (Read(_), BorrowKind::Shared) => Control::Continue, + (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { + match kind { + ReadKind::Copy => { + error_reported = true; + this.report_use_while_mutably_borrowed(context, place_span, borrow) + } + ReadKind::Borrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) + } + } + Control::Break + } + (Write(kind), _) => { + match kind { + WriteKind::MutableBorrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) + } + WriteKind::StorageDeadOrDrop => { + let end_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_borrowed_value_does_not_live_long_enough( + context, + place_span, + end_span, + ) + } + WriteKind::Mutate => { + error_reported = true; + this.report_illegal_mutation_of_borrowed(context, place_span, borrow) + } + WriteKind::Move => { + error_reported = true; + this.report_move_out_while_borrowed(context, place_span, &borrow) + } + } + Control::Break + } + }, + ); + + if error_reported { + if let Some(local) = storage_dead_or_drop_local { + self.storage_dead_or_drop_error_reported.insert(local); + } + } + } + + fn mutate_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: ShallowOrDeep, + mode: MutateMode, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. + match mode { + MutateMode::WriteAndRead => { + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Update, + place_span, + flow_state, + ); + } + MutateMode::JustWrite => { + self.check_if_assigned_path_is_moved(context, place_span, flow_state); + } + } + + self.access_place( + context, + place_span, + (kind, Write(WriteKind::Mutate)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + + // check for reassignments to immutable local variables + self.check_if_reassignment_to_immutable_state(context, place_span, flow_state); + } + + fn consume_rvalue( + &mut self, + context: Context, + (rvalue, span): (&Rvalue<'tcx>, Span), + _location: Location, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + match *rvalue { + Rvalue::Ref(_ /*rgn*/, bk, ref place) => { + let access_kind = match bk { + BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), + BorrowKind::Unique | BorrowKind::Mut => { + (Deep, Write(WriteKind::MutableBorrow(bk))) + } + }; + self.access_place( + context, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Borrow, + (place, span), + flow_state, + ); + } + + Rvalue::Use(ref operand) | + Rvalue::Repeat(ref operand, _) | + Rvalue::UnaryOp(_ /*un_op*/, ref operand) | + Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { + self.consume_operand(context, (operand, span), flow_state) + } + + Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { + let af = match *rvalue { + Rvalue::Len(..) => ArtificialField::ArrayLength, + Rvalue::Discriminant(..) => ArtificialField::Discriminant, + _ => unreachable!(), + }; + self.access_place( + context, + (place, span), + (Shallow(Some(af)), Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); + } + + Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | + Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { + self.consume_operand(context, (operand1, span), flow_state); + self.consume_operand(context, (operand2, span), flow_state); + } + + Rvalue::NullaryOp(_op, _ty) => { + // nullary ops take no dynamic input; no borrowck effect. + // + // FIXME: is above actually true? Do we want to track + // the fact that uninitialized data can be created via + // `NullOp::Box`? + } + + Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands { + self.consume_operand(context, (operand, span), flow_state); + }, + } + } + + fn consume_operand( + &mut self, + context: Context, + (operand, span): (&Operand<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + match *operand { + Operand::Copy(ref place) => { + // copy of place: check if this is "copy of frozen path" + // (FIXME: see check_loans.rs) + self.access_place( + context, + (place, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + + // Finally, check if path was already moved. + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); + } + Operand::Move(ref place) => { + // move of place: check if this is move of already borrowed path + self.access_place( + context, + (place, span), + (Deep, Write(WriteKind::Move)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + + // Finally, check if path was already moved. + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); + } + Operand::Constant(_) => {} + } + } + + /// Returns whether a borrow of this place is invalidated when the function + /// exits + fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool { + debug!("place_is_invalidated_at_exit({:?})", place); + let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); + + // FIXME(nll-rfc#40): do more precise destructor tracking here. For now + // we just know that all locals are dropped at function exit (otherwise + // we'll have a memory leak) and assume that all statics have a destructor. + let (might_be_alive, will_be_dropped) = match root_place { + Place::Static(statik) => { + // Thread-locals might be dropped after the function exits, but + // "true" statics will never be. + let is_thread_local = self.tcx + .get_attrs(statik.def_id) + .iter() + .any(|attr| attr.check_name("thread_local")); + + (true, is_thread_local) + } + Place::Local(_) => { + // Locals are always dropped at function exit, and if they + // have a destructor it would've been called already. + (false, true) + } + Place::Projection(..) => { + bug!("root of {:?} is a projection ({:?})?", place, root_place) + } + }; + + if !will_be_dropped { + debug!( + "place_is_invalidated_at_exit({:?}) - won't be dropped", + place + ); + return false; + } + + // FIXME: replace this with a proper borrow_conflicts_with_place when + // that is merged. + let prefix_set = if might_be_alive { + PrefixSet::Supporting + } else { + PrefixSet::Shallow + }; + + self.prefixes(place, prefix_set) + .any(|prefix| prefix == root_place) + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + fn check_if_reassignment_to_immutable_state( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + let move_data = self.move_data; + + // determine if this path has a non-mut owner (and thus needs checking). + if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) { + return; + } + + if let Err(_) = self.is_mutable(place, LocalMutationIsAllowed::Yes) { + return; + } + + match self.move_path_closest_to(place) { + Ok(mpi) => for ii in &move_data.init_path_map[mpi] { + if flow_state.ever_inits.curr_state.contains(ii) { + let first_assign_span = self.move_data.inits[*ii].span; + self.report_illegal_reassignment(context, (place, span), first_assign_span); + break; + } + }, + Err(NoMovePathFound::ReachedStatic) => { + let item_msg = match self.describe_place(place) { + Some(name) => format!("immutable static item `{}`", name), + None => "immutable static item".to_owned(), + }; + self.tcx.sess.delay_span_bug( + span, + &format!( + "cannot assign to {}, should have been caught by \ + `check_access_permissions()`", + item_msg + ), + ); + } + } + } + + fn check_if_path_is_moved( + &mut self, + context: Context, + desired_action: InitializationRequiringAction, + place_span: (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + // FIXME: analogous code in check_loans first maps `place` to + // its base_path ... but is that what we want here? + let place = self.base_path(place_span.0); + + let maybe_uninits = &flow_state.uninits; + let curr_move_outs = &flow_state.move_outs.curr_state; + + // Bad scenarios: + // + // 1. Move of `a.b.c`, use of `a.b.c` + // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`) + // 3. Move of `a.b.c`, use of `a` or `a.b` + // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with + // partial initialization support, one might have `a.x` + // initialized but not `a.b`. + // + // OK scenarios: + // + // 5. Move of `a.b.c`, use of `a.b.d` + // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b` + // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` + // must have been initialized for the use to be sound. + // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` + + // The dataflow tracks shallow prefixes distinctly (that is, + // field-accesses on P distinctly from P itself), in order to + // track substructure initialization separately from the whole + // structure. + // + // E.g., when looking at (*a.b.c).d, if the closest prefix for + // which we have a MovePath is `a.b`, then that means that the + // initialization state of `a.b` is all we need to inspect to + // know if `a.b.c` is valid (and from that we infer that the + // dereference and `.d` access is also valid, since we assume + // `a.b.c` is assigned a reference to a initialized and + // well-formed record structure.) + + // Therefore, if we seek out the *closest* prefix for which we + // have a MovePath, that should capture the initialization + // state for the place scenario. + // + // This code covers scenarios 1, 2, and 4. + + debug!("check_if_path_is_moved part1 place: {:?}", place); + match self.move_path_closest_to(place) { + Ok(mpi) => { + if maybe_uninits.curr_state.contains(&mpi) { + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + mpi, + curr_move_outs, + ); + return; // don't bother finding other problems. + } + } + Err(NoMovePathFound::ReachedStatic) => { + // Okay: we do not build MoveData for static variables + } // Only query longest prefix with a MovePath, not further + // ancestors; dataflow recurs on children when parents + // move (to support partial (re)inits). + // + // (I.e. querying parents breaks scenario 8; but may want + // to do such a query based on partial-init feature-gate.) + } + + // A move of any shallow suffix of `place` also interferes + // with an attempt to use `place`. This is scenario 3 above. + // + // (Distinct from handling of scenarios 1+2+4 above because + // `place` does not interfere with suffixes of its prefixes, + // e.g. `a.b.c` does not interfere with `a.b.d`) + + debug!("check_if_path_is_moved part2 place: {:?}", place); + if let Some(mpi) = self.move_path_for_place(place) { + if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) { + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + child_mpi, + curr_move_outs, + ); + return; // don't bother finding other problems. + } + } + } + + /// Currently MoveData does not store entries for all places in + /// the input MIR. For example it will currently filter out + /// places that are Copy; thus we do not track places of shared + /// reference type. This routine will walk up a place along its + /// prefixes, searching for a foundational place that *is* + /// tracked in the MoveData. + /// + /// An Err result includes a tag indicated why the search failed. + /// Currenly this can only occur if the place is built off of a + /// static variable, as we do not track those in the MoveData. + fn move_path_closest_to( + &mut self, + place: &Place<'tcx>, + ) -> Result { + let mut last_prefix = place; + for prefix in self.prefixes(place, PrefixSet::All) { + if let Some(mpi) = self.move_path_for_place(prefix) { + return Ok(mpi); + } + last_prefix = prefix; + } + match *last_prefix { + Place::Local(_) => panic!("should have move path for every Local"), + Place::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"), + Place::Static(_) => return Err(NoMovePathFound::ReachedStatic), + } + } + + fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option { + // If returns None, then there is no move path corresponding + // to a direct owner of `place` (which means there is nothing + // that borrowck tracks for its analysis). + + match self.move_data.rev_lookup.find(place) { + LookupResult::Parent(_) => None, + LookupResult::Exact(mpi) => Some(mpi), + } + } + + fn check_if_assigned_path_is_moved( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { + // recur down place; dispatch to check_if_path_is_moved when necessary + let mut place = place; + loop { + match *place { + Place::Local(_) | Place::Static(_) => { + // assigning to `x` does not require `x` be initialized. + break; + } + Place::Projection(ref proj) => { + let Projection { ref base, ref elem } = **proj; + match *elem { + ProjectionElem::Deref | + // assigning to *P requires `P` initialized. + ProjectionElem::Index(_/*operand*/) | + ProjectionElem::ConstantIndex { .. } | + // assigning to P[i] requires `P` initialized. + ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => + // assigning to (P->variant) is okay if assigning to `P` is okay + // + // FIXME: is this true even if P is a adt with a dtor? + { } + + ProjectionElem::Subslice { .. } => { + panic!("we dont allow assignments to subslices, context: {:?}", + context); + } + + ProjectionElem::Field(..) => { + // if type of `P` has a dtor, then + // assigning to `P.f` requires `P` itself + // be already initialized + let tcx = self.tcx; + match base.ty(self.mir, tcx).to_ty(tcx).sty { + ty::TyAdt(def, _) if def.has_dtor(tcx) => { + + // FIXME: analogous code in + // check_loans.rs first maps + // `base` to its base_path. + + self.check_if_path_is_moved( + context, InitializationRequiringAction::Assignment, + (base, span), flow_state); + + // (base initialized; no need to + // recur further) + break; + } + _ => {} + } + } + } + + place = base; + continue; + } + } + } + } + + /// Check the permissions for the given place and read or write kind + /// + /// Returns true if an error is reported, false otherwise. + fn check_access_permissions( + &self, + (place, span): (&Place<'tcx>, Span), + kind: ReadOrWrite, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> bool { + debug!( + "check_access_permissions({:?}, {:?}, {:?})", + place, + kind, + is_local_mutation_allowed + ); + let mut error_reported = false; + match kind { + Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { + if let Err(_place_err) = self.is_unique(place) { + span_bug!(span, "&unique borrow for {:?} should not fail", place); + } + } + Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) = + self.is_mutable(place, is_local_mutation_allowed) + { + error_reported = true; + + let item_msg = match self.describe_place(place) { + Some(name) => format!("immutable item `{}`", name), + None => "immutable item".to_owned(), + }; + + let mut err = self.tcx + .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir); + err.span_label(span, "cannot borrow as mutable"); + + if place != place_err { + if let Some(name) = self.describe_place(place_err) { + err.note(&format!("Value not mutable causing this error: `{}`", name)); + } + } + + err.emit(); + }, + Write(WriteKind::Mutate) => { + if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { + error_reported = true; + + let item_msg = match self.describe_place(place) { + Some(name) => format!("immutable item `{}`", name), + None => "immutable item".to_owned(), + }; + + let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir); + err.span_label(span, "cannot mutate"); + + if place != place_err { + if let Some(name) = self.describe_place(place_err) { + err.note(&format!("Value not mutable causing this error: `{}`", name)); + } + } + + err.emit(); + } + } + Write(WriteKind::Move) | + Write(WriteKind::StorageDeadOrDrop) | + Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { + if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { + self.tcx.sess.delay_span_bug( + span, + &format!( + "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", + place, + kind + ), + ); + } + } + Read(ReadKind::Borrow(BorrowKind::Unique)) | + Read(ReadKind::Borrow(BorrowKind::Mut)) | + Read(ReadKind::Borrow(BorrowKind::Shared)) | + Read(ReadKind::Copy) => {} // Access authorized + } + + error_reported + } + + /// Can this value be written or borrowed mutably + fn is_mutable<'d>( + &self, + place: &'d Place<'tcx>, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> Result<(), &'d Place<'tcx>> { + match *place { + Place::Local(local) => { + let local = &self.mir.local_decls[local]; + match local.mutability { + Mutability::Not => match is_local_mutation_allowed { + LocalMutationIsAllowed::Yes => Ok(()), + LocalMutationIsAllowed::No => Err(place), + }, + Mutability::Mut => Ok(()), + } + } + Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) { + Err(place) + } else { + Ok(()) + }, + Place::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => { + let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + + // Check the kind of deref to decide + match base_ty.sty { + ty::TyRef(_, tnm) => { + match tnm.mutbl { + // Shared borrowed data is never mutable + hir::MutImmutable => Err(place), + // Mutably borrowed data is mutable, but only if we have a + // unique path to the `&mut` + hir::MutMutable => { + if self.is_upvar_field_projection(&proj.base).is_some() { + self.is_mutable(&proj.base, is_local_mutation_allowed) + } else { + self.is_unique(&proj.base) + } + } + } + } + ty::TyRawPtr(tnm) => { + match tnm.mutbl { + // `*const` raw pointers are not mutable + hir::MutImmutable => Err(place), + // `*mut` raw pointers are always mutable, regardless of context + // The users have to check by themselve. + hir::MutMutable => Ok(()), + } + } + // `Box` owns its content, so mutable if its location is mutable + _ if base_ty.is_box() => { + self.is_mutable(&proj.base, LocalMutationIsAllowed::No) + } + // Deref should only be for reference, pointers or boxes + _ => bug!("Deref of unexpected type: {:?}", base_ty), + } + } + // All other projections are owned by their base path, so mutable if + // base path is mutable + ProjectionElem::Field(..) | + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | + ProjectionElem::Downcast(..) => { + let field_projection = self.is_upvar_field_projection(place); + + if let Some(field) = field_projection { + let decl = &self.mir.upvar_decls[field.index()]; + + return match decl.mutability { + Mutability::Mut => self.is_unique(&proj.base), + Mutability::Not => Err(place), + }; + } + + self.is_mutable(&proj.base, LocalMutationIsAllowed::No) + } + } + } + } + } + + /// Does this place have a unique path + fn is_unique<'d>(&self, place: &'d Place<'tcx>) -> Result<(), &'d Place<'tcx>> { + match *place { + Place::Local(..) => { + // Local variables are unique + Ok(()) + } + Place::Static(..) => { + // Static variables are not + Err(place) + } + Place::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => { + let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + + // `Box` referent is unique if box is a unique spot + if base_ty.is_box() { + return self.is_unique(&proj.base); + } + + // Otherwise we check the kind of deref to decide + match base_ty.sty { + ty::TyRef(_, tnm) => { + match tnm.mutbl { + // place represent an aliased location + hir::MutImmutable => Err(place), + // `&mut T` is as unique as the context in which it is found + hir::MutMutable => self.is_unique(&proj.base), + } + } + ty::TyRawPtr(tnm) => { + match tnm.mutbl { + // `*mut` can be aliased, but we leave it to user + hir::MutMutable => Ok(()), + // `*const` is treated the same as `*mut` + hir::MutImmutable => Ok(()), + } + } + // Deref should only be for reference, pointers or boxes + _ => bug!("Deref of unexpected type: {:?}", base_ty), + } + } + // Other projections are unique if the base is unique + ProjectionElem::Field(..) | + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | + ProjectionElem::Downcast(..) => self.is_unique(&proj.base), + } + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum NoMovePathFound { + ReachedStatic, +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + fn each_borrow_involving_path( + &mut self, + _context: Context, + access_place: (ShallowOrDeep, &Place<'tcx>), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + mut op: F, + ) where + F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control, + { + let (access, place) = access_place; + + // FIXME: analogous code in check_loans first maps `place` to + // its base_path. + + let domain = flow_state.borrows.base_results.operator(); + let data = domain.borrows(); + + // check for loan restricting path P being used. Accounts for + // borrows of P, P.a.b, etc. + 'next_borrow: for i in flow_state.borrows.elems_incoming() { + let borrowed = &data[i]; + + // Is `place` (or a prefix of it) already borrowed? If + // so, that's relevant. + // + // FIXME: Differs from AST-borrowck; includes drive-by fix + // to #38899. Will probably need back-compat mode flag. + for accessed_prefix in self.prefixes(place, PrefixSet::All) { + if *accessed_prefix == borrowed.place { + // FIXME: pass in enum describing case we are in? + let ctrl = op(self, i, borrowed, accessed_prefix); + if ctrl == Control::Break { + return; + } + } + } + + // Is `place` a prefix (modulo access type) of the + // `borrowed.place`? If so, that's relevant. + + let prefix_kind = match access { + Shallow(Some(ArtificialField::Discriminant)) | + Shallow(Some(ArtificialField::ArrayLength)) => { + // The discriminant and array length are like + // additional fields on the type; they do not + // overlap any existing data there. Furthermore, + // they cannot actually be a prefix of any + // borrowed place (at least in MIR as it is + // currently.) + continue 'next_borrow; + } + Shallow(None) => PrefixSet::Shallow, + Deep => PrefixSet::Supporting, + }; + + for borrowed_prefix in self.prefixes(&borrowed.place, prefix_kind) { + if borrowed_prefix == place { + // FIXME: pass in enum describing case we are in? + let ctrl = op(self, i, borrowed, borrowed_prefix); + if ctrl == Control::Break { + return; + } + } + } + } + } +} + +use self::prefixes::PrefixSet; + +/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an +/// place are formed by stripping away fields and derefs, except that +/// we stop when we reach the deref of a shared reference. [...] " +/// +/// "Shallow prefixes are found by stripping away fields, but stop at +/// any dereference. So: writing a path like `a` is illegal if `a.b` +/// is borrowed. But: writing `a` is legal if `*a` is borrowed, +/// whether or not `a` is a shared or mutable reference. [...] " +mod prefixes { + use super::MirBorrowckCtxt; + + use rustc::hir; + use rustc::ty::{self, TyCtxt}; + use rustc::mir::{Mir, Place, ProjectionElem}; + + pub trait IsPrefixOf<'tcx> { + fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; + } + + impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { + fn is_prefix_of(&self, other: &Place<'tcx>) -> bool { + let mut cursor = other; + loop { + if self == cursor { + return true; + } + + match *cursor { + Place::Local(_) | Place::Static(_) => return false, + Place::Projection(ref proj) => { + cursor = &proj.base; + } + } + } + } + } + + + pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + mir: &'cx Mir<'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + kind: PrefixSet, + next: Option<&'cx Place<'tcx>>, + } + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub(super) enum PrefixSet { + /// Doesn't stop until it returns the base case (a Local or + /// Static prefix). + All, + /// Stops at any dereference. + Shallow, + /// Stops at the deref of a shared reference. + Supporting, + } + + impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + /// Returns an iterator over the prefixes of `place` + /// (inclusive) from longest to smallest, potentially + /// terminating the iteration early based on `kind`. + pub(super) fn prefixes( + &self, + place: &'cx Place<'tcx>, + kind: PrefixSet, + ) -> Prefixes<'cx, 'gcx, 'tcx> { + Prefixes { + next: Some(place), + kind, + mir: self.mir, + tcx: self.tcx, + } + } + } + + impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { + type Item = &'cx Place<'tcx>; + fn next(&mut self) -> Option { + let mut cursor = match self.next { + None => return None, + Some(place) => place, + }; + + // Post-processing `place`: Enqueue any remaining + // work. Also, `place` may not be a prefix itself, but + // may hold one further down (e.g. we never return + // downcasts here, but may return a base of a downcast). + + 'cursor: loop { + let proj = match *cursor { + Place::Local(_) | // search yielded this leaf + Place::Static(_) => { + self.next = None; + return Some(cursor); + } + + Place::Projection(ref proj) => proj, + }; + + match proj.elem { + ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { + // FIXME: add union handling + self.next = Some(&proj.base); + return Some(cursor); + } + ProjectionElem::Downcast(..) | + ProjectionElem::Subslice { .. } | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Index(_) => { + cursor = &proj.base; + continue 'cursor; + } + ProjectionElem::Deref => { + // (handled below) + } + } + + assert_eq!(proj.elem, ProjectionElem::Deref); + + match self.kind { + PrefixSet::Shallow => { + // shallow prefixes are found by stripping away + // fields, but stop at *any* dereference. + // So we can just stop the traversal now. + self.next = None; + return Some(cursor); + } + PrefixSet::All => { + // all prefixes: just blindly enqueue the base + // of the projection + self.next = Some(&proj.base); + return Some(cursor); + } + PrefixSet::Supporting => { + // fall through! + } + } + + assert_eq!(self.kind, PrefixSet::Supporting); + // supporting prefixes: strip away fields and + // derefs, except we stop at the deref of a shared + // reference. + + let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + match ty.sty { + ty::TyRawPtr(_) | + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutImmutable, + }, + ) => { + // don't continue traversing over derefs of raw pointers or shared borrows. + self.next = None; + return Some(cursor); + } + + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutMutable, + }, + ) => { + self.next = Some(&proj.base); + return Some(cursor); + } + + ty::TyAdt(..) if ty.is_box() => { + self.next = Some(&proj.base); + return Some(cursor); + } + + _ => panic!("unknown type fed to Projection Deref."), + } + } + } + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + fn report_use_of_moved_or_uninitialized( + &mut self, + _context: Context, + desired_action: InitializationRequiringAction, + (place, span): (&Place<'tcx>, Span), + mpi: MovePathIndex, + curr_move_out: &IdxSetBuf, + ) { + let mois = self.move_data.path_map[mpi] + .iter() + .filter(|moi| curr_move_out.contains(moi)) + .collect::>(); + + if mois.is_empty() { + let item_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + self.tcx + .cannot_act_on_uninitialized_variable( + span, + desired_action.as_noun(), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label(span, format!("use of possibly uninitialized {}", item_msg)) + .emit(); + } else { + let msg = ""; //FIXME: add "partially " or "collaterally " + + let mut err = self.tcx.cannot_act_on_moved_value( + span, + desired_action.as_noun(), + msg, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.span_label( + span, + format!( + "value {} here after move", + desired_action.as_verb_in_past_tense() + ), + ); + for moi in mois { + let move_msg = ""; //FIXME: add " (into closure)" + let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span; + if span == move_span { + err.span_label( + span, + format!("value moved{} here in previous iteration of loop", move_msg), + ); + } else { + err.span_label(move_span, format!("value moved{} here", move_msg)); + }; + } + //FIXME: add note for closure + err.emit(); + } + } + + fn report_move_out_while_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + let value_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + let borrow_msg = match self.describe_place(&borrow.place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + self.tcx + .cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label( + self.retrieve_borrow_span(borrow), + format!("borrow of {} occurs here", borrow_msg), + ) + .span_label(span, format!("move out of {} occurs here", value_msg)) + .emit(); + } + + fn report_use_while_mutably_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + let mut err = self.tcx.cannot_use_when_mutably_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + self.retrieve_borrow_span(borrow), + &self.describe_place(&borrow.place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.emit(); + } + + /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of + /// the local assigned at `location`. + /// This is done by searching in statements succeeding `location` + /// and originating from `maybe_closure_span`. + fn find_closure_span( + &self, + maybe_closure_span: Span, + location: Location, + ) -> Option<(Span, Span)> { + use rustc::hir::ExprClosure; + use rustc::mir::AggregateKind; + + let local = if let StatementKind::Assign(Place::Local(local), _) = + self.mir[location.block].statements[location.statement_index].kind + { + local + } else { + return None; + }; + + for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { + if maybe_closure_span != stmt.source_info.span { + break; + } + + if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind { + if let AggregateKind::Closure(def_id, _) = **kind { + debug!("find_closure_span: found closure {:?}", places); + + return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + let args_span = if let ExprClosure(_, _, _, span, _) = + self.tcx.hir.expect_expr(node_id).node + { + span + } else { + return None; + }; + + self.tcx + .with_freevars(node_id, |freevars| { + for (v, place) in freevars.iter().zip(places) { + match *place { + Operand::Copy(Place::Local(l)) | + Operand::Move(Place::Local(l)) if local == l => + { + debug!( + "find_closure_span: found captured local {:?}", + l + ); + return Some(v.span); + } + _ => {} + } + } + None + }) + .map(|var_span| (args_span, var_span)) + } else { + None + }; + } + } + } + + None + } + + fn report_conflicting_borrow( + &mut self, + context: Context, + common_prefix: &Place<'tcx>, + (place, span): (&Place<'tcx>, Span), + gen_borrow_kind: BorrowKind, + issued_borrow: &BorrowData, + end_issued_loan_span: Option, + ) { + use self::prefixes::IsPrefixOf; + + assert!(common_prefix.is_prefix_of(place)); + assert!(common_prefix.is_prefix_of(&issued_borrow.place)); + + let issued_span = self.retrieve_borrow_span(issued_borrow); + + let new_closure_span = self.find_closure_span(span, context.loc); + let span = new_closure_span.map(|(args, _)| args).unwrap_or(span); + let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location); + let issued_span = old_closure_span + .map(|(args, _)| args) + .unwrap_or(issued_span); + + let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); + + // FIXME: supply non-"" `opt_via` when appropriate + let mut err = match ( + gen_borrow_kind, + "immutable", + "mutable", + issued_borrow.kind, + "immutable", + "mutable", + ) { + (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | + (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx + .cannot_reborrow_already_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "it", + rgt, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx + .cannot_mutably_borrow_multiply( + span, + &desc_place, + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_uniquely_borrow_by_two_closures( + span, + &desc_place, + issued_span, + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure( + span, + &desc_place, + "", + issued_span, + "it", + "", + end_issued_loan_span, + Origin::Mir, + ), + + (_, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_reborrow_already_uniquely_borrowed( + span, + &desc_place, + "it", + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(), + }; + + if let Some((_, var_span)) = old_closure_span { + err.span_label( + var_span, + format!( + "previous borrow occurs due to use of `{}` in closure", + desc_place + ), + ); + } + + if let Some((_, var_span)) = new_closure_span { + err.span_label( + var_span, + format!("borrow occurs due to use of `{}` in closure", desc_place), + ); + } + + err.emit(); + } + + fn report_borrowed_value_does_not_live_long_enough( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + end_span: Option, + ) { + let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); + let proper_span = match *root_place { + Place::Local(local) => self.mir.local_decls[local].source_info.span, + _ => span, + }; + let mut err = self.tcx + .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); + err.span_label(proper_span, "temporary value created here"); + err.span_label(span, "temporary value dropped here while still borrowed"); + err.note("consider using a `let` binding to increase its lifetime"); + + if let Some(end) = end_span { + err.span_label(end, "temporary value needs to live until here"); + } + + err.emit(); + } + + fn report_illegal_mutation_of_borrowed( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + loan: &BorrowData, + ) { + let mut err = self.tcx.cannot_assign_to_borrowed( + span, + self.retrieve_borrow_span(loan), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.emit(); + } + + fn report_illegal_reassignment( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + assigned_span: Span, + ) { + let mut err = self.tcx.cannot_reassign_immutable( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + err.span_label(span, "cannot assign twice to immutable variable"); + if span != assigned_span { + let value_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + err.span_label(assigned_span, format!("first assignment to {}", value_msg)); + } + err.emit(); + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + // End-user visible description of `place` if one can be found. If the + // place is a temporary for instance, None will be returned. + fn describe_place(&self, place: &Place<'tcx>) -> Option { + let mut buf = String::new(); + match self.append_place_to_string(place, &mut buf, false) { + Ok(()) => Some(buf), + Err(()) => None, + } + } + + /// If this is a field projection, and the field is being projected from a closure type, + /// then returns the index of the field being projected. Note that this closure will always + /// be `self` in the current MIR, because that is the only time we directly access the fields + /// of a closure type. + fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option { + match *place { + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _ty) => { + let is_projection_from_ty_closure = proj.base + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_closure(); + + if is_projection_from_ty_closure { + Some(field) + } else { + None + } + } + _ => None, + }, + _ => None, + } + } + + // Appends end-user visible description of `place` to `buf`. + fn append_place_to_string( + &self, + place: &Place<'tcx>, + buf: &mut String, + mut autoderef: bool, + ) -> Result<(), ()> { + match *place { + Place::Local(local) => { + self.append_local_to_string(local, buf)?; + } + Place::Static(ref static_) => { + buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); + } + Place::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => { + if let Some(field) = self.is_upvar_field_projection(&proj.base) { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.to_string(); + if self.mir.upvar_decls[var_index].by_ref { + buf.push_str(&name); + } else { + buf.push_str(&format!("*{}", &name)); + } + } else { + if autoderef { + self.append_place_to_string(&proj.base, buf, autoderef)?; + } else { + buf.push_str(&"*"); + self.append_place_to_string(&proj.base, buf, autoderef)?; + } + } + } + ProjectionElem::Downcast(..) => { + self.append_place_to_string(&proj.base, buf, autoderef)?; + } + ProjectionElem::Field(field, _ty) => { + autoderef = true; + + if let Some(field) = self.is_upvar_field_projection(place) { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.to_string(); + buf.push_str(&name); + } else { + let field_name = self.describe_field(&proj.base, field); + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str(&format!(".{}", field_name)); + } + } + ProjectionElem::Index(index) => { + autoderef = true; + + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str("["); + if let Err(_) = self.append_local_to_string(index, buf) { + buf.push_str(".."); + } + buf.push_str("]"); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + autoderef = true; + // Since it isn't possible to borrow an element on a particular index and + // then use another while the borrow is held, don't output indices details + // to avoid confusing the end-user + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str(&"[..]"); + } + }; + } + } + + Ok(()) + } + + // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have + // a name, then `Err` is returned + fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> { + let local = &self.mir.local_decls[local_index]; + match local.name { + Some(name) => { + buf.push_str(&format!("{}", name)); + Ok(()) + } + None => Err(()), + } + } + + // End-user visible description of the `field`nth field of `base` + fn describe_field(&self, base: &Place, field: Field) -> String { + match *base { + Place::Local(local) => { + let local = &self.mir.local_decls[local]; + self.describe_field_from_ty(&local.ty, field) + } + Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Deref => self.describe_field(&proj.base, field), + ProjectionElem::Downcast(def, variant_index) => { + format!("{}", def.variants[variant_index].fields[field.index()].name) + } + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field) + } + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => { + format!("{}", self.describe_field(&proj.base, field)) + } + }, + } + } + + // End-user visible description of the `field_index`nth field of `ty` + fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String { + if ty.is_box() { + // If the type is a box, the field is described from the boxed type + self.describe_field_from_ty(&ty.boxed_ty(), field) + } else { + match ty.sty { + ty::TyAdt(def, _) => if def.is_enum() { + format!("{}", field.index()) + } else { + format!("{}", def.struct_variant().fields[field.index()].name) + }, + ty::TyTuple(_, _) => format!("{}", field.index()), + ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => { + self.describe_field_from_ty(&tnm.ty, field) + } + ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field), + ty::TyClosure(closure_def_id, _) => { + // Convert the def-id into a node-id. node-ids are only valid for + // the local code in the current crate, so this returns an `Option` in case + // the closure comes from another crate. But in that case we wouldn't + // be borrowck'ing it, so we can just unwrap: + let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap(); + let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); + + self.tcx.hir.name(freevar.var_id()).to_string() + } + _ => { + // Might need a revision when the fields in trait RFC is implemented + // (https://github.com/rust-lang/rfcs/pull/1546) + bug!( + "End-user description not implemented for field access on `{:?}`", + ty.sty + ); + } + } + } + } + + // Retrieve span of given borrow from the current MIR representation + fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { + self.mir.source_info(borrow.location).span + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + // FIXME (#16118): function intended to allow the borrow checker + // to be less precise in its handling of Box while still allowing + // moves out of a Box. They should be removed when/if we stop + // treating Box specially (e.g. when/if DerefMove is added...) + + fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> { + //! Returns the base of the leftmost (deepest) dereference of an + //! Box in `place`. If there is no dereference of an Box + //! in `place`, then it just returns `place` itself. + + let mut cursor = place; + let mut deepest = place; + loop { + let proj = match *cursor { + Place::Local(..) | Place::Static(..) => return deepest, + Place::Projection(ref proj) => proj, + }; + if proj.elem == ProjectionElem::Deref + && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() + { + deepest = &proj.base; + } + cursor = &proj.base; + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Context { + kind: ContextKind, + loc: Location, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ContextKind { + AssignLhs, + AssignRhs, + SetDiscrim, + InlineAsm, + SwitchInt, + Drop, + DropAndReplace, + CallOperator, + CallOperand, + CallDest, + Assert, + Yield, + StorageDead, +} + +impl ContextKind { + fn new(self, loc: Location) -> Context { + Context { + kind: self, + loc: loc, + } + } +} + +impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { + fn new( + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, + ever_inits: FlowInProgress>, + ) -> Self { + InProgress { + borrows, + inits, + uninits, + move_outs, + ever_inits, + } + } + + fn each_flow( + &mut self, + mut xform_borrows: XB, + mut xform_inits: XI, + mut xform_uninits: XU, + mut xform_move_outs: XM, + mut xform_ever_inits: XE, + ) where + XB: FnMut(&mut FlowInProgress>), + XI: FnMut(&mut FlowInProgress>), + XU: FnMut(&mut FlowInProgress>), + XM: FnMut(&mut FlowInProgress>), + XE: FnMut(&mut FlowInProgress>), + { + xform_borrows(&mut self.borrows); + xform_inits(&mut self.inits); + xform_uninits(&mut self.uninits); + xform_move_outs(&mut self.move_outs); + xform_ever_inits(&mut self.ever_inits); + } + + fn summary(&self) -> String { + let mut s = String::new(); + + s.push_str("borrows in effect: ["); + let mut saw_one = false; + self.borrows.each_state_bit(|borrow| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; + s.push_str(&format!("{}", borrow_data)); + }); + s.push_str("] "); + + s.push_str("borrows generated: ["); + let mut saw_one = false; + self.borrows.each_gen_bit(|borrow| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; + s.push_str(&format!("{}", borrow_data)); + }); + s.push_str("] "); + + s.push_str("inits: ["); + let mut saw_one = false; + self.inits.each_state_bit(|mpi_init| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init]; + s.push_str(&format!("{}", move_path)); + }); + s.push_str("] "); + + s.push_str("uninits: ["); + let mut saw_one = false; + self.uninits.each_state_bit(|mpi_uninit| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_path = + &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; + s.push_str(&format!("{}", move_path)); + }); + s.push_str("] "); + + s.push_str("move_out: ["); + let mut saw_one = false; + self.move_outs.each_state_bit(|mpi_move_out| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; + s.push_str(&format!("{:?}", move_out)); + }); + s.push_str("] "); + + s.push_str("ever_init: ["); + let mut saw_one = false; + self.ever_inits.each_state_bit(|mpi_ever_init| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let ever_init = + &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init]; + s.push_str(&format!("{:?}", ever_init)); + }); + s.push_str("]"); + + return s; + } +} + +impl<'tcx, T> FlowInProgress +where + T: HasMoveData<'tcx> + BitDenotation, +{ + fn has_any_child_of(&self, mpi: T::Idx) -> Option { + let move_data = self.base_results.operator().move_data(); + + let mut todo = vec![mpi]; + let mut push_siblings = false; // don't look at siblings of original `mpi`. + while let Some(mpi) = todo.pop() { + if self.curr_state.contains(&mpi) { + return Some(mpi); + } + let move_path = &move_data.move_paths[mpi]; + if let Some(child) = move_path.first_child { + todo.push(child); + } + if push_siblings { + if let Some(sibling) = move_path.next_sibling { + todo.push(sibling); + } + } else { + // after we've processed the original `mpi`, we should + // always traverse the siblings of any of its + // children. + push_siblings = true; + } + } + return None; + } +} + +impl FlowInProgress +where + BD: BitDenotation, +{ + fn each_state_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.curr_state + .each_bit(self.base_results.operator().bits_per_block(), f) + } + + fn each_gen_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.stmt_gen + .each_bit(self.base_results.operator().bits_per_block(), f) + } + + fn new(results: DataflowResults) -> Self { + let bits_per_block = results.sets().bits_per_block(); + let curr_state = IdxSetBuf::new_empty(bits_per_block); + let stmt_gen = IdxSetBuf::new_empty(bits_per_block); + let stmt_kill = IdxSetBuf::new_empty(bits_per_block); + FlowInProgress { + base_results: results, + curr_state: curr_state, + stmt_gen: stmt_gen, + stmt_kill: stmt_kill, + } + } + + fn reset_to_entry_of(&mut self, bb: BasicBlock) { + (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index())); + } + + fn reconstruct_statement_effect(&mut self, loc: Location) { + self.stmt_gen.reset_to_empty(); + self.stmt_kill.reset_to_empty(); + let mut ignored = IdxSetBuf::new_empty(0); + let mut sets = BlockSets { + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, + }; + self.base_results + .operator() + .statement_effect(&mut sets, loc); + } + + fn reconstruct_terminator_effect(&mut self, loc: Location) { + self.stmt_gen.reset_to_empty(); + self.stmt_kill.reset_to_empty(); + let mut ignored = IdxSetBuf::new_empty(0); + let mut sets = BlockSets { + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, + }; + self.base_results + .operator() + .terminator_effect(&mut sets, loc); + } + + fn apply_local_effect(&mut self) { + self.curr_state.union(&self.stmt_gen); + self.curr_state.subtract(&self.stmt_kill); + } + + fn elems_incoming(&self) -> indexed_set::Elems { + let univ = self.base_results.sets().bits_per_block(); + self.curr_state.elems(univ) + } + + fn with_elems_outgoing(&self, f: F) + where + F: FnOnce(indexed_set::Elems), + { + let mut curr_state = self.curr_state.clone(); + curr_state.union(&self.stmt_gen); + curr_state.subtract(&self.stmt_kill); + let univ = self.base_results.sets().bits_per_block(); + f(curr_state.elems(univ)); + } +} diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs new file mode 100644 index 0000000000000..42225536357da --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -0,0 +1,337 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; +use rustc::mir::visit::Visitor; +use rustc::mir::Place::Projection; +use rustc::mir::{Local, PlaceProjection, ProjectionElem}; +use rustc::mir::visit::TyContext; +use rustc::infer::InferCtxt; +use rustc::traits::{self, ObligationCause}; +use rustc::ty::{self, ClosureSubsts, Ty}; +use rustc::ty::subst::Substs; +use rustc::ty::fold::TypeFoldable; +use rustc::util::common::ErrorReported; +use rustc_data_structures::fx::FxHashSet; +use syntax::codemap::DUMMY_SP; +use borrow_check::FlowInProgress; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::{HasMoveData, MoveData}; + +use super::LivenessResults; +use super::ToRegionVid; +use super::region_infer::RegionInferenceContext; + +pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, + regioncx: &mut RegionInferenceContext<'tcx>, + mir: &Mir<'tcx>, + param_env: ty::ParamEnv<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowInProgress>, + move_data: &MoveData<'tcx>, +) { + let mut cg = ConstraintGeneration { + infcx, + regioncx, + mir, + liveness, + param_env, + flow_inits, + move_data, + }; + + for (bb, data) in mir.basic_blocks().iter_enumerated() { + cg.visit_basic_block_data(bb, data); + } +} + +/// 'cg = the duration of the constraint generation process itself. +struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>, + regioncx: &'cg mut RegionInferenceContext<'tcx>, + mir: &'cg Mir<'tcx>, + liveness: &'cg LivenessResults, + param_env: ty::ParamEnv<'tcx>, + flow_inits: &'cg mut FlowInProgress>, + move_data: &'cg MoveData<'tcx>, +} + + +impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { + fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { + self.add_liveness_constraints(bb); + self.super_basic_block_data(bb, data); + } + + /// We sometimes have `substs` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_substs(&mut self, substs: &&'tcx Substs<'tcx>, location: Location) { + self.add_regular_live_constraint(*substs, location); + self.super_substs(substs); + } + + /// We sometimes have `region` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) { + self.add_regular_live_constraint(*region, location); + self.super_region(region); + } + + /// We sometimes have `ty` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_ty(&mut self, ty: &ty::Ty<'tcx>, ty_context: TyContext) { + match ty_context { + TyContext::ReturnTy(source_info) | + TyContext::LocalDecl { source_info, .. } => { + span_bug!(source_info.span, + "should not be visiting outside of the CFG: {:?}", + ty_context); + } + TyContext::Location(location) => { + self.add_regular_live_constraint(*ty, location); + } + } + + self.super_ty(ty); + } + + /// We sometimes have `closure_substs` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Location) { + self.add_regular_live_constraint(*substs, location); + self.super_closure_substs(substs); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location); + + // Look for an rvalue like: + // + // & L + // + // where L is the path that is borrowed. In that case, we have + // to add the reborrow constraints (which don't fall out + // naturally from the type-checker). + if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue { + self.add_reborrow_constraint(location, region, borrowed_lv); + } + + self.super_rvalue(rvalue, location); + } +} + +impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { + /// Liveness constraints: + /// + /// > If a variable V is live at point P, then all regions R in the type of V + /// > must include the point P. + fn add_liveness_constraints(&mut self, bb: BasicBlock) { + debug!("add_liveness_constraints(bb={:?})", bb); + + self.liveness + .regular + .simulate_block(self.mir, bb, |location, live_locals| { + for live_local in live_locals.iter() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_regular_live_constraint(live_local_ty, location); + } + }); + + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + self.liveness + .drop + .simulate_block(self.mir, bb, |location, live_locals| { + all_live_locals.push((location, live_locals.iter().collect())); + }); + debug!( + "add_liveness_constraints: all_live_locals={:#?}", + all_live_locals + ); + + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); + self.flow_inits.reset_to_entry_of(bb); + while let Some((location, live_locals)) = all_live_locals.pop() { + for live_local in live_locals { + debug!( + "add_liveness_constraints: location={:?} live_local={:?}", + location, + live_local + ); + + self.flow_inits.each_state_bit(|mpi_init| { + debug!( + "add_liveness_constraints: location={:?} initialized={:?}", + location, + &self.flow_inits + .base_results + .operator() + .move_data() + .move_paths[mpi_init] + ); + }); + + let mpi = self.move_data.rev_lookup.find_local(live_local); + if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { + debug!( + "add_liveness_constraints: mpi={:?} has initialized child {:?}", + self.move_data.move_paths[mpi], + self.move_data.move_paths[initialized_child] + ); + + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); + } + } + + if location.statement_index == terminator_index { + debug!( + "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_terminator_effect(location); + } else { + debug!( + "add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_statement_effect(location); + } + self.flow_inits.apply_local_effect(); + } + } + + /// Some variable with type `live_ty` is "regular live" at + /// `location` -- i.e., it may be used later. This means that all + /// regions appearing in the type `live_ty` must be live at + /// `location`. + fn add_regular_live_constraint(&mut self, live_ty: T, location: Location) + where + T: TypeFoldable<'tcx>, + { + debug!( + "add_regular_live_constraint(live_ty={:?}, location={:?})", + live_ty, + location + ); + + self.infcx + .tcx + .for_each_free_region(&live_ty, |live_region| { + let vid = live_region.to_region_vid(); + self.regioncx.add_live_point(vid, location); + }); + } + + /// Some variable with type `live_ty` is "drop live" at `location` + /// -- i.e., it may be dropped later. This means that *some* of + /// the regions in its type must be live at `location`. The + /// precise set will depend on the dropck constraints, and in + /// particular this takes `#[may_dangle]` into account. + fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + debug!( + "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + dropped_ty, + location + ); + + let tcx = self.infcx.tcx; + let mut types = vec![(dropped_ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let span = DUMMY_SP; // FIXME + let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { + Ok(result) => result, + Err(ErrorReported) => { + continue; + } + }; + + let ty::DtorckConstraint { + outlives, + dtorck_types, + } = result; + + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + for outlive in outlives { + self.add_regular_live_constraint(outlive, location); + } + + // However, there may also be some types that + // `dtorck_constraint_for_ty` could not resolve (e.g., + // associated types and parameters). We need to normalize + // associated types here and possibly recursively process. + for ty in dtorck_types { + let cause = ObligationCause::dummy(); + // We know that our original `dropped_ty` is well-formed, + // so region obligations resulting from this normalization + // should always hold. + // + // Therefore we ignore them instead of trying to match + // them up with a location. + let fulfillcx = traits::FulfillmentContext::new_ignoring_regions(); + match traits::fully_normalize_with_fulfillcx( + self.infcx, fulfillcx, cause, self.param_env, &ty + ) { + Ok(ty) => match ty.sty { + ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { + self.add_regular_live_constraint(ty, location); + } + + _ => if known.insert(ty) { + types.push((ty, depth + 1)); + }, + }, + + Err(errors) => { + self.infcx.report_fulfillment_errors(&errors, None); + } + } + } + } + } + + fn add_reborrow_constraint( + &mut self, + location: Location, + borrow_region: ty::Region<'tcx>, + borrowed_place: &Place<'tcx>, + ) { + if let Projection(ref proj) = *borrowed_place { + let PlaceProjection { ref base, ref elem } = **proj; + + if let ProjectionElem::Deref = *elem { + let tcx = self.infcx.tcx; + let base_ty = base.ty(self.mir, tcx).to_ty(tcx); + let base_sty = &base_ty.sty; + + if let ty::TyRef(base_region, ty::TypeAndMut { ty: _, mutbl }) = *base_sty { + match mutbl { + hir::Mutability::MutImmutable => {} + + hir::Mutability::MutMutable => { + self.add_reborrow_constraint(location, borrow_region, base); + } + } + + let span = self.mir.source_info(location).span; + self.regioncx.add_outlives( + span, + base_region.to_region_vid(), + borrow_region.to_region_vid(), + location.successor_within_block(), + ); + } + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs new file mode 100644 index 0000000000000..804f5e2687597 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -0,0 +1,236 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::def_id::DefId; +use rustc::mir::Mir; +use rustc::infer::InferCtxt; +use rustc::ty::{self, RegionKind, RegionVid}; +use rustc::util::nodemap::FxHashMap; +use std::collections::BTreeSet; +use transform::MirSource; +use transform::type_check; +use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; +use borrow_check::FlowInProgress; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::MoveData; + +use util as mir_util; +use self::mir_util::PassWhere; + +mod constraint_generation; +mod subtype_constraint_generation; +mod universal_regions; +use self::universal_regions::UniversalRegions; + +pub(crate) mod region_infer; +use self::region_infer::RegionInferenceContext; + +mod renumber; + +/// Rewrites the regions in the MIR to use NLL variables, also +/// scraping out the set of free regions (e.g., region parameters) +/// declared on the function. That set will need to be given to +/// `compute_regions`. +pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, + def_id: DefId, + mir: &mut Mir<'tcx>, +) -> UniversalRegions<'tcx> { + // Compute named region information. + let universal_regions = universal_regions::universal_regions(infcx, def_id); + + // Replace all regions with fresh inference variables. + renumber::renumber_mir(infcx, &universal_regions, mir); + + universal_regions +} + +/// Computes the (non-lexical) regions from the input MIR. +/// +/// This may result in errors being reported. +pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, + def_id: DefId, + universal_regions: UniversalRegions<'tcx>, + mir: &Mir<'tcx>, + param_env: ty::ParamEnv<'gcx>, + flow_inits: &mut FlowInProgress>, + move_data: &MoveData<'tcx>, +) -> RegionInferenceContext<'tcx> { + // Run the MIR type-checker. + let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); + let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir); + + // Create the region inference context, taking ownership of the region inference + // data that was contained in `infcx`. + let var_origins = infcx.take_region_var_origins(); + let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir); + subtype_constraint_generation::generate( + &mut regioncx, + &universal_regions, + mir, + constraint_sets, + ); + + // Compute what is live where. + let liveness = &LivenessResults { + regular: liveness::liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: true, + include_drops: false, + }, + ), + + drop: liveness::liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), + }; + + // Generate non-subtyping constraints. + constraint_generation::generate_constraints( + infcx, + &mut regioncx, + &mir, + param_env, + liveness, + flow_inits, + move_data, + ); + + // Solve the region constraints. + regioncx.solve(infcx, &mir); + + // Dump MIR results into a file, if that is enabled. This let us + // write unit-tests. + dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, ®ioncx); + + regioncx +} + +struct LivenessResults { + regular: LivenessResult, + drop: LivenessResult, +} + +fn dump_mir_results<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + liveness: &LivenessResults, + source: MirSource, + mir: &Mir<'tcx>, + regioncx: &RegionInferenceContext, +) { + if !mir_util::dump_enabled(infcx.tcx, "nll", source) { + return; + } + + let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() + .indices() + .flat_map(|bb| { + let mut results = vec![]; + liveness + .regular + .simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); + results + }) + .collect(); + + let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() + .indices() + .flat_map(|bb| { + let mut results = vec![]; + liveness + .drop + .simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); + results + }) + .collect(); + + mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| { + match pass_where { + // Before the CFG, dump out the values for each region variable. + PassWhere::BeforeCFG => for region in regioncx.regions() { + writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?; + }, + + // Before each basic block, dump out the values + // that are live on entry to the basic block. + PassWhere::BeforeBlock(bb) => { + let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]); + writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?; + } + + PassWhere::BeforeLocation(location) => { + let s = live_variable_set( + ®ular_liveness_per_location[&location], + &drop_liveness_per_location[&location], + ); + writeln!(out, " | Live variables at {:?}: {}", location, s)?; + } + + PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} + } + Ok(()) + }); +} + +/// Right now, we piggy back on the `ReVar` to store our NLL inference +/// regions. These are indexed with `RegionVid`. This method will +/// assert that the region is a `ReVar` and extract its interal index. +/// This is reasonable because in our MIR we replace all free regions +/// with inference variables. +pub trait ToRegionVid { + fn to_region_vid(&self) -> RegionVid; +} + +impl ToRegionVid for RegionKind { + fn to_region_vid(&self) -> RegionVid { + if let &ty::ReVar(vid) = self { + vid + } else { + bug!("region is not an ReVar: {:?}", self) + } + } +} + +fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { + // sort and deduplicate: + let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect(); + + // construct a string with each local, including `(drop)` if it is + // only dropped, versus a regular use. + let mut string = String::new(); + for local in all_locals { + string.push_str(&format!("{:?}", local)); + + if !regular.contains(&local) { + assert!(drops.contains(&local)); + string.push_str(" (drop)"); + } + + string.push_str(", "); + } + + let len = if string.is_empty() { + 0 + } else { + string.len() - 2 + }; + + format!("[{}]", &string[..len]) +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs new file mode 100644 index 0000000000000..d1faaf75a5323 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -0,0 +1,553 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::universal_regions::UniversalRegions; +use rustc::infer::InferCtxt; +use rustc::infer::RegionVariableOrigin; +use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::region_constraints::VarOrigins; +use rustc::infer::outlives::free_region_map::FreeRegionMap; +use rustc::mir::{Location, Mir}; +use rustc::ty::{self, RegionVid}; +use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::bitvec::BitMatrix; +use rustc_data_structures::indexed_vec::Idx; +use std::collections::BTreeMap; +use std::fmt; +use syntax_pos::Span; + +pub struct RegionInferenceContext<'tcx> { + /// Contains the definition for every region variable. Region + /// variables are identified by their index (`RegionVid`). The + /// definition contains information about where the region came + /// from as well as its final inferred value. + definitions: IndexVec>, + + /// The liveness constraints added to each region. For most + /// regions, these start out empty and steadily grow, though for + /// each universally quantified region R they start out containing + /// the entire CFG and `end(R)`. + /// + /// In this `BitMatrix` representation, the rows are the region + /// variables and the columns are the free regions and MIR locations. + liveness_constraints: BitMatrix, + + /// The final inferred values of the inference variables; `None` + /// until `solve` is invoked. + inferred_values: Option, + + /// The constraints we have accumulated and used during solving. + constraints: Vec, + + /// A map from each MIR Location to its column index in + /// `liveness_constraints`/`inferred_values`. (The first N columns are + /// the free regions.) + point_indices: BTreeMap, + + /// Number of universally quantified regions. This is used to + /// determine the meaning of the bits in `inferred_values` and + /// friends. + num_universal_regions: usize, + + free_region_map: &'tcx FreeRegionMap<'tcx>, +} + +struct RegionDefinition<'tcx> { + /// Why we created this variable. Mostly these will be + /// `RegionVariableOrigin::NLL`, but some variables get created + /// elsewhere in the code with other causes (e.g., instantiation + /// late-bound-regions). + origin: RegionVariableOrigin, + + /// If this is a free-region, then this is `Some(X)` where `X` is + /// the name of the region. + name: Option>, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Constraint { + // NB. The ordering here is not significant for correctness, but + // it is for convenience. Before we dump the constraints in the + // debugging logs, we sort them, and we'd like the "super region" + // to be first, etc. (In particular, span should remain last.) + /// The region SUP must outlive SUB... + sup: RegionVid, + + /// Region that must be outlived. + sub: RegionVid, + + /// At this location. + point: Location, + + /// Where did this constraint arise? + span: Span, +} + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Creates a new region inference context with a total of + /// `num_region_variables` valid inference variables; the first N + /// of those will be constant regions representing the free + /// regions defined in `universal_regions`. + pub fn new( + var_origins: VarOrigins, + universal_regions: &UniversalRegions<'tcx>, + mir: &Mir<'tcx>, + ) -> Self { + let num_region_variables = var_origins.len(); + let num_universal_regions = universal_regions.indices.len(); + + let mut num_points = 0; + let mut point_indices = BTreeMap::new(); + + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + for statement_index in 0..block_data.statements.len() + 1 { + let location = Location { + block, + statement_index, + }; + point_indices.insert(location, num_universal_regions + num_points); + num_points += 1; + } + } + + // Create a RegionDefinition for each inference variable. + let definitions = var_origins + .into_iter() + .map(|origin| RegionDefinition::new(origin)) + .collect(); + + let mut result = Self { + definitions, + liveness_constraints: BitMatrix::new( + num_region_variables, + num_universal_regions + num_points, + ), + inferred_values: None, + constraints: Vec::new(), + point_indices, + num_universal_regions, + free_region_map: universal_regions.free_region_map, + }; + + result.init_universal_regions(universal_regions); + + result + } + + /// Initializes the region variables for each universally + /// quantified region (lifetime parameter). The first N variables + /// always correspond to the regions appearing in the function + /// signature (both named and anonymous) and where clauses. This + /// function iterates over those regions and initializes them with + /// minimum values. + /// + /// For example: + /// + /// fn foo<'a, 'b>(..) where 'a: 'b + /// + /// would initialize two variables like so: + /// + /// R0 = { CFG, R0 } // 'a + /// R1 = { CFG, R0, R1 } // 'b + /// + /// Here, R0 represents `'a`, and it contains (a) the entire CFG + /// and (b) any free regions that it outlives, which in this case + /// is just itself. R1 (`'b`) in contrast also outlives `'a` and + /// hence contains R0 and R1. + fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) { + let UniversalRegions { + indices, + free_region_map: _, + } = universal_regions; + + // For each universally quantified region X: + for (free_region, &variable) in indices { + // These should be free-region variables. + assert!(match self.definitions[variable].origin { + RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true, + _ => false, + }); + + // Initialize the name and a few other details. + self.definitions[variable].name = Some(free_region); + + // Add all nodes in the CFG to liveness constraints + for (_location, point_index) in &self.point_indices { + self.liveness_constraints + .add(variable.index(), *point_index); + } + + // Add `end(X)` into the set for X. + self.liveness_constraints + .add(variable.index(), variable.index()); + } + } + + /// Returns an iterator over all the region indices. + pub fn regions(&self) -> impl Iterator { + self.definitions.indices() + } + + /// Returns true if the region `r` contains the point `p`. + /// + /// Panics if called before `solve()` executes, + pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + self.region_contains_point_in_matrix(inferred_values, r, p) + } + + /// True if given region `r` contains the point `p`, when + /// evaluated in the set of region values `matrix`. + fn region_contains_point_in_matrix( + &self, + matrix: &BitMatrix, + r: RegionVid, + p: Location, + ) -> bool { + let point_index = self.point_indices + .get(&p) + .expect("point index should be known"); + matrix.contains(r.index(), *point_index) + } + + /// True if given region `r` contains the `end(s)`, when + /// evaluated in the set of region values `matrix`. + fn region_contains_region_in_matrix( + &self, + matrix: &BitMatrix, + r: RegionVid, + s: RegionVid, + ) -> bool { + matrix.contains(r.index(), s.index()) + } + + /// Returns access to the value of `r` for debugging purposes. + pub(super) fn region_value_str(&self, r: RegionVid) -> String { + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + + let mut result = String::new(); + result.push_str("{"); + let mut sep = ""; + + for &point in self.point_indices.keys() { + if self.region_contains_point_in_matrix(inferred_values, r, point) { + result.push_str(&format!("{}{:?}", sep, point)); + sep = ", "; + } + } + + for fr in (0..self.num_universal_regions).map(RegionVid::new) { + if self.region_contains_region_in_matrix(inferred_values, r, fr) { + result.push_str(&format!("{}{:?}", sep, fr)); + sep = ", "; + } + } + + result.push_str("}"); + + result + } + + /// Indicates that the region variable `v` is live at the point `point`. + pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool { + debug!("add_live_point({:?}, {:?})", v, point); + assert!(self.inferred_values.is_none(), "values already inferred"); + let point_index = self.point_indices + .get(&point) + .expect("point index should be known"); + self.liveness_constraints.add(v.index(), *point_index) + } + + /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`. + pub(super) fn add_outlives( + &mut self, + span: Span, + sup: RegionVid, + sub: RegionVid, + point: Location, + ) { + debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); + assert!(self.inferred_values.is_none(), "values already inferred"); + self.constraints.push(Constraint { + span, + sup, + sub, + point, + }); + } + + /// Perform region inference. + pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { + assert!(self.inferred_values.is_none(), "values already inferred"); + + // Find the minimal regions that can solve the constraints. This is infallible. + self.propagate_constraints(mir); + + // Now, see whether any of the constraints were too strong. In + // particular, we want to check for a case where a universally + // quantified region exceeded its bounds. Consider: + // + // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + // + // In this case, returning `x` requires `&'a u32 <: &'b u32` + // and hence we establish (transitively) a constraint that + // `'a: 'b`. The `propagate_constraints` code above will + // therefore add `end('a)` into the region for `'b` -- but we + // have no evidence that `'a` outlives `'b`, so we want to report + // an error. + + // The universal regions are always found in a prefix of the + // full list. + let free_region_definitions = self.definitions + .iter_enumerated() + .take_while(|(_, fr_definition)| fr_definition.name.is_some()); + + for (fr, fr_definition) in free_region_definitions { + self.check_free_region(infcx, fr, fr_definition); + } + } + + fn check_free_region( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + longer_fr: RegionVid, + longer_definition: &RegionDefinition<'tcx>, + ) { + let inferred_values = self.inferred_values.as_ref().unwrap(); + let longer_name = longer_definition.name.unwrap(); + let longer_value = inferred_values.iter(longer_fr.index()); + + // Find every region `shorter` such that `longer: shorter` + // (because `longer` includes `end(shorter)`). + for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) { + let shorter_fr = RegionVid::new(shorter_fr); + + // `fr` includes `end(fr)`, that's not especially + // interesting. + if longer_fr == shorter_fr { + continue; + } + + let shorter_definition = &self.definitions[shorter_fr]; + let shorter_name = shorter_definition.name.unwrap(); + + // Check that `o <= fr`. If not, report an error. + if !self.free_region_map + .sub_free_regions(shorter_name, longer_name) + { + // FIXME: worst error msg ever + let blame_span = self.blame_span(longer_fr, shorter_fr); + infcx.tcx.sess.span_err( + blame_span, + &format!( + "free region `{}` does not outlive `{}`", + longer_name, + shorter_name + ), + ); + } + } + } + + /// Propagate the region constraints: this will grow the values + /// for each region variable until all the constraints are + /// satisfied. Note that some values may grow **too** large to be + /// feasible, but we check this later. + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { + let mut changed = true; + + debug!("propagate_constraints()"); + debug!("propagate_constraints: constraints={:#?}", { + let mut constraints: Vec<_> = self.constraints.iter().collect(); + constraints.sort(); + constraints + }); + + // The initial values for each region are derived from the liveness + // constraints we have accumulated. + let mut inferred_values = self.liveness_constraints.clone(); + + while changed { + changed = false; + debug!("propagate_constraints: --------------------"); + for constraint in &self.constraints { + debug!("propagate_constraints: constraint={:?}", constraint); + + // Grow the value as needed to accommodate the + // outlives constraint. + + if self.copy( + &mut inferred_values, + mir, + constraint.sub, + constraint.sup, + constraint.point, + ) { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); + changed = true; + } + } + debug!("\n"); + } + + self.inferred_values = Some(inferred_values); + } + + fn copy( + &self, + inferred_values: &mut BitMatrix, + mir: &Mir<'tcx>, + from_region: RegionVid, + to_region: RegionVid, + start_point: Location, + ) -> bool { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(start_point); + while let Some(p) = stack.pop() { + debug!(" copy: p={:?}", p); + + if !self.region_contains_point_in_matrix(inferred_values, from_region, p) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + let point_index = self.point_indices.get(&p).unwrap(); + changed |= inferred_values.add(to_region.index(), *point_index); + + let block_data = &mir[p.block]; + let successor_points = if p.statement_index < block_data.statements.len() { + vec![ + Location { + statement_index: p.statement_index + 1, + ..p + }, + ] + } else { + block_data + .terminator() + .successors() + .iter() + .map(|&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } + }) + .collect::>() + }; + + if successor_points.is_empty() { + // If we reach the END point in the graph, then copy + // over any skolemized end points in the `from_region` + // and make sure they are included in the `to_region`. + let universal_region_indices = inferred_values + .iter(from_region.index()) + .take_while(|&i| i < self.num_universal_regions) + .collect::>(); + for fr in &universal_region_indices { + changed |= inferred_values.add(to_region.index(), *fr); + } + } else { + stack.extend(successor_points); + } + } + + changed + } + + /// Tries to finds a good span to blame for the fact that `fr1` + /// contains `fr2`. + fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { + // Find everything that influenced final value of `fr`. + let influenced_fr1 = self.dependencies(fr1); + + // Try to find some outlives constraint `'X: fr2` where `'X` + // influenced `fr1`. Blame that. + // + // NB, this is a pretty bad choice most of the time. In + // particular, the connection between `'X` and `fr1` may not + // be obvious to the user -- not to mention the naive notion + // of dependencies, which doesn't account for the locations of + // contraints at all. But it will do for now. + for constraint in &self.constraints { + if constraint.sub == fr2 && influenced_fr1[constraint.sup] { + return constraint.span; + } + } + + bug!( + "could not find any constraint to blame for {:?}: {:?}", + fr1, + fr2 + ); + } + + /// Finds all regions whose values `'a` may depend on in some way. + /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` + /// and `dependencies('b)` will be in the final set. + /// + /// Used during error reporting, extremely naive and inefficient. + fn dependencies(&self, r0: RegionVid) -> IndexVec { + let mut result_set = IndexVec::from_elem(false, &self.definitions); + let mut changed = true; + result_set[r0] = true; + + while changed { + changed = false; + for constraint in &self.constraints { + if result_set[constraint.sup] { + if !result_set[constraint.sub] { + result_set[constraint.sub] = true; + changed = true; + } + } + } + } + + result_set + } +} + +impl<'tcx> RegionDefinition<'tcx> { + fn new(origin: RegionVariableOrigin) -> Self { + // Create a new region definition. Note that, for free + // regions, these fields get updated later in + // `init_universal_regions`. + Self { origin, name: None } + } +} + +impl fmt::Debug for Constraint { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + formatter, + "({:?}: {:?} @ {:?}) due to {:?}", + self.sup, + self.sub, + self.point, + self.span + ) + } +} diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs new file mode 100644 index 0000000000000..371419da02448 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -0,0 +1,165 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::ty::subst::Substs; +use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable}; +use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; +use rustc::mir::visit::{MutVisitor, TyContext}; +use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; + +use super::ToRegionVid; +use super::universal_regions::UniversalRegions; + +/// Replaces all free regions appearing in the MIR with fresh +/// inference variables, returning the number of variables created. +pub fn renumber_mir<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + universal_regions: &UniversalRegions<'tcx>, + mir: &mut Mir<'tcx>, +) { + // Create inference variables for each of the free regions + // declared on the function signature. + let free_region_inference_vars = (0..universal_regions.indices.len()) + .map(RegionVid::new) + .map(|vid_expected| { + let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion); + assert_eq!(vid_expected, r.to_region_vid()); + r + }) + .collect(); + + debug!("renumber_mir()"); + debug!("renumber_mir: universal_regions={:#?}", universal_regions); + debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); + + let mut visitor = NLLVisitor { + infcx, + universal_regions, + free_region_inference_vars, + arg_count: mir.arg_count, + }; + visitor.visit_mir(mir); +} + +struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + universal_regions: &'a UniversalRegions<'tcx>, + free_region_inference_vars: IndexVec>, + arg_count: usize, +} + +impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { + /// Replaces all regions appearing in `value` with fresh inference + /// variables. This is what we do for almost the entire MIR, with + /// the exception of the declared types of our arguments. + fn renumber_regions(&mut self, ty_context: TyContext, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + debug!("renumber_regions(value={:?})", value); + + self.infcx + .tcx + .fold_regions(value, &mut false, |_region, _depth| { + let origin = NLLRegionVariableOrigin::Inferred(ty_context); + self.infcx.next_nll_region_var(origin) + }) + } + + /// Renumbers the regions appearing in `value`, but those regions + /// are expected to be free regions from the function signature. + fn renumber_universal_regions(&mut self, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + debug!("renumber_universal_regions(value={:?})", value); + + self.infcx + .tcx + .fold_regions(value, &mut false, |region, _depth| { + let index = self.universal_regions.indices[®ion]; + self.free_region_inference_vars[index] + }) + } + + fn is_argument_or_return_slot(&self, local: Local) -> bool { + // The first argument is return slot, next N are arguments. + local.index() <= self.arg_count + } +} + +impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { + let is_arg = match ty_context { + TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), + TyContext::ReturnTy(..) => true, + TyContext::Location(..) => false, + }; + debug!( + "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", + ty, + is_arg, + ty_context + ); + + let old_ty = *ty; + *ty = if is_arg { + self.renumber_universal_regions(&old_ty) + } else { + self.renumber_regions(ty_context, &old_ty) + }; + debug!("visit_ty: ty={:?}", ty); + } + + fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { + debug!("visit_substs(substs={:?}, location={:?})", substs, location); + + let ty_context = TyContext::Location(location); + *substs = self.renumber_regions(ty_context, &{ *substs }); + + debug!("visit_substs: substs={:?}", substs); + } + + fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) { + debug!("visit_region(region={:?}, location={:?})", region, location); + + let old_region = *region; + let ty_context = TyContext::Location(location); + *region = self.renumber_regions(ty_context, &old_region); + + debug!("visit_region: region={:?}", region); + } + + fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) { + debug!( + "visit_closure_substs(substs={:?}, location={:?})", + substs, + location + ); + + let ty_context = TyContext::Location(location); + *substs = self.renumber_regions(ty_context, substs); + + debug!("visit_closure_substs: substs={:?}", substs); + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location, + ) { + if let StatementKind::EndRegion(_) = statement.kind { + statement.kind = StatementKind::Nop; + } + self.super_statement(block, statement, location); + } +} diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs new file mode 100644 index 0000000000000..dbae40be6fe1d --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -0,0 +1,112 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir::Mir; +use rustc::infer::region_constraints::Constraint; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::ty; +use transform::type_check::MirTypeckRegionConstraints; +use transform::type_check::OutlivesSet; + +use super::universal_regions::UniversalRegions; +use super::region_infer::RegionInferenceContext; + +/// When the MIR type-checker executes, it validates all the types in +/// the MIR, and in the process generates a set of constraints that +/// must hold regarding the regions in the MIR, along with locations +/// *where* they must hold. This code takes those constriants and adds +/// them into the NLL `RegionInferenceContext`. +pub(super) fn generate<'tcx>( + regioncx: &mut RegionInferenceContext<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + mir: &Mir<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, +) { + SubtypeConstraintGenerator { + regioncx, + universal_regions, + mir, + }.generate(constraints); +} + +struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { + regioncx: &'cx mut RegionInferenceContext<'tcx>, + universal_regions: &'cx UniversalRegions<'tcx>, + mir: &'cx Mir<'tcx>, +} + +impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { + fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) { + let MirTypeckRegionConstraints { + liveness_set, + outlives_sets, + } = constraints; + + debug!( + "generate(liveness_set={} items, outlives_sets={} items)", + liveness_set.len(), + outlives_sets.len() + ); + + for (region, location) in liveness_set { + debug!("generate: {:#?} is live at {:#?}", region, location); + let region_vid = self.to_region_vid(region); + self.regioncx.add_live_point(region_vid, *location); + } + + for OutlivesSet { locations, data } in outlives_sets { + debug!("generate: constraints at: {:#?}", locations); + let RegionConstraintData { + constraints, + verifys, + givens, + } = data; + + for constraint in constraints.keys() { + debug!("generate: constraint: {:?}", constraint); + let (a_vid, b_vid) = match constraint { + Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid), + Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid), + Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)), + Constraint::RegSubReg(a_r, b_r) => { + (self.to_region_vid(a_r), self.to_region_vid(b_r)) + } + }; + + // We have the constraint that `a_vid <= b_vid`. Add + // `b_vid: a_vid` to our region checker. Note that we + // reverse direction, because `regioncx` talks about + // "outlives" (`>=`) whereas the region constraints + // talk about `<=`. + let span = self.mir.source_info(locations.from_location).span; + self.regioncx + .add_outlives(span, b_vid, a_vid, locations.at_location); + } + + assert!(verifys.is_empty(), "verifys not yet implemented"); + assert!( + givens.is_empty(), + "MIR type-checker does not use givens (thank goodness)" + ); + } + } + + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { + // Every region that we see in the constraints came from the + // MIR or from the parameter environment. If the former, it + // will be a region variable. If the latter, it will be in + // the set of universal regions *somewhere*. + if let ty::ReVar(vid) = r { + *vid + } else { + self.universal_regions.indices[&r] + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs new file mode 100644 index 0000000000000..3be95a114c3bc --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -0,0 +1,90 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code to extract the universally quantified regions declared on a +//! function and the relationships between them. For example: +//! +//! ``` +//! fn foo<'a, 'b, 'c: 'b>() { } +//! ``` +//! +//! here we would be returning a map assigning each of `{'a, 'b, 'c}` +//! to an index, as well as the `FreeRegionMap` which can compute +//! relationships between them. +//! +//! The code in this file doesn't *do anything* with those results; it +//! just returns them for other code to use. + +use rustc::hir::def_id::DefId; +use rustc::infer::InferCtxt; +use rustc::infer::outlives::free_region_map::FreeRegionMap; +use rustc::ty::{self, RegionVid}; +use rustc::ty::subst::Substs; +use rustc::util::nodemap::FxHashMap; +use rustc_data_structures::indexed_vec::Idx; + +#[derive(Debug)] +pub struct UniversalRegions<'tcx> { + /// Given a universally quantified region defined on this function + /// (either early- or late-bound), this maps it to its internal + /// region index. When the region context is created, the first N + /// variables will be created based on these indices. + pub indices: FxHashMap, RegionVid>, + + /// The map from the typeck tables telling us how to relate universal regions. + pub free_region_map: &'tcx FreeRegionMap<'tcx>, +} + +pub fn universal_regions<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + item_def_id: DefId, +) -> UniversalRegions<'tcx> { + debug!("universal_regions(item_def_id={:?})", item_def_id); + + let mut indices = FxHashMap(); + + // `'static` is always free. + insert_free_region(&mut indices, infcx.tcx.types.re_static); + + // Extract the early regions. + let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id); + for item_subst in item_substs { + if let Some(region) = item_subst.as_region() { + insert_free_region(&mut indices, region); + } + } + + // Extract the late-bound regions. Use the liberated fn sigs, + // where the late-bound regions will have been converted into free + // regions, and add them to the map. + let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap(); + let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id); + let tables = infcx.tcx.typeck_tables_of(item_def_id); + let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone(); + infcx + .tcx + .for_each_free_region(&fn_sig.inputs_and_output, |region| { + if let ty::ReFree(_) = *region { + insert_free_region(&mut indices, region); + } + }); + + debug!("universal_regions: indices={:#?}", indices); + + UniversalRegions { indices, free_region_map: &tables.free_region_map } +} + +fn insert_free_region<'tcx>( + universal_regions: &mut FxHashMap, RegionVid>, + region: ty::Region<'tcx>, +) { + let next = RegionVid::new(universal_regions.len()); + universal_regions.entry(region).or_insert(next); +} diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 1fc96dbf45197..b2b615d29a5b8 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -16,7 +16,7 @@ use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn ast_block(&mut self, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, block: BasicBlock, ast_block: &'tcx hir::Block, source_info: SourceInfo) @@ -53,7 +53,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } fn ast_block_stmts(&mut self, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, mut block: BasicBlock, span: Span, stmts: Vec>, diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index dfddbfe485dd9..d1bb1f39e221c 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -61,18 +61,18 @@ impl<'tcx> CFG<'tcx> { pub fn push_assign(&mut self, block: BasicBlock, source_info: SourceInfo, - lvalue: &Lvalue<'tcx>, + place: &Place<'tcx>, rvalue: Rvalue<'tcx>) { self.push(block, Statement { source_info, - kind: StatementKind::Assign(lvalue.clone(), rvalue) + kind: StatementKind::Assign(place.clone(), rvalue) }); } pub fn push_assign_constant(&mut self, block: BasicBlock, source_info: SourceInfo, - temp: &Lvalue<'tcx>, + temp: &Place<'tcx>, constant: Constant<'tcx>) { self.push_assign(block, source_info, temp, Rvalue::Use(Operand::Constant(box constant))); @@ -81,8 +81,8 @@ impl<'tcx> CFG<'tcx> { pub fn push_assign_unit(&mut self, block: BasicBlock, source_info: SourceInfo, - lvalue: &Lvalue<'tcx>) { - self.push_assign(block, source_info, lvalue, Rvalue::Aggregate( + place: &Place<'tcx>) { + self.push_assign(block, source_info, place, Rvalue::Aggregate( box AggregateKind::Tuple, vec![] )); } diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index ea6e4342098bc..7eae414a39137 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -32,7 +32,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } /// Compile `expr` into a value that can be used as an operand. - /// If `expr` is an lvalue like `x`, this will introduce a + /// If `expr` is a place like `x`, this will introduce a /// temporary `tmp = x`, so that we capture the value of `x` at /// this time. /// @@ -70,11 +70,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let constant = this.as_constant(expr); block.and(Operand::Constant(box constant)) } - Category::Lvalue | + Category::Place | Category::Rvalue(..) => { let operand = unpack!(block = this.as_temp(block, scope, expr)); - block.and(Operand::Consume(Lvalue::Local(operand))) + block.and(Operand::Move(Place::Local(operand))) } } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_place.rs similarity index 73% rename from src/librustc_mir/build/expr/as_lvalue.rs rename to src/librustc_mir/build/expr/as_place.rs index 69d0dd992281e..9e21790851167 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -18,22 +18,22 @@ use rustc::mir::*; use rustc_data_structures::indexed_vec::Idx; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { - /// Compile `expr`, yielding an lvalue that we can move from etc. - pub fn as_lvalue(&mut self, + /// Compile `expr`, yielding a place that we can move from etc. + pub fn as_place(&mut self, block: BasicBlock, expr: M) - -> BlockAnd> + -> BlockAnd> where M: Mirror<'tcx, Output=Expr<'tcx>> { let expr = self.hir.mirror(expr); - self.expr_as_lvalue(block, expr) + self.expr_as_place(block, expr) } - fn expr_as_lvalue(&mut self, + fn expr_as_place(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) - -> BlockAnd> { - debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr); + -> BlockAnd> { + debug!("expr_as_place(block={:?}, expr={:?})", block, expr); let this = self; let expr_span = expr.span; @@ -41,24 +41,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match expr.kind { ExprKind::Scope { region_scope, lint_level, value } => { this.in_scope((region_scope, source_info), lint_level, block, |this| { - this.as_lvalue(block, value) + this.as_place(block, value) }) } ExprKind::Field { lhs, name } => { - let lvalue = unpack!(block = this.as_lvalue(block, lhs)); - let lvalue = lvalue.field(name, expr.ty); - block.and(lvalue) + let place = unpack!(block = this.as_place(block, lhs)); + let place = place.field(name, expr.ty); + block.and(place) } ExprKind::Deref { arg } => { - let lvalue = unpack!(block = this.as_lvalue(block, arg)); - let lvalue = lvalue.deref(); - block.and(lvalue) + let place = unpack!(block = this.as_place(block, arg)); + let place = place.deref(); + block.and(place) } ExprKind::Index { lhs, index } => { let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty()); - let slice = unpack!(block = this.as_lvalue(block, lhs)); - // region_scope=None so lvalue indexes live forever. They are scalars so they + let slice = unpack!(block = this.as_place(block, lhs)); + // region_scope=None so place indexes live forever. They are scalars so they // do not need storage annotations, and they are often copied between // places. let idx = unpack!(block = this.as_temp(block, None, index)); @@ -70,26 +70,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &len, Rvalue::Len(slice.clone())); this.cfg.push_assign(block, source_info, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, - Operand::Consume(Lvalue::Local(idx)), - Operand::Consume(len.clone()))); + Operand::Copy(Place::Local(idx)), + Operand::Copy(len.clone()))); let msg = AssertMessage::BoundsCheck { - len: Operand::Consume(len), - index: Operand::Consume(Lvalue::Local(idx)) + len: Operand::Move(len), + index: Operand::Copy(Place::Local(idx)) }; - let success = this.assert(block, Operand::Consume(lt), true, + let success = this.assert(block, Operand::Move(lt), true, msg, expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => { - block.and(Lvalue::Local(Local::new(1))) + block.and(Place::Local(Local::new(1))) } ExprKind::VarRef { id } => { let index = this.var_indices[&id]; - block.and(Lvalue::Local(index)) + block.and(Place::Local(index)) } ExprKind::StaticRef { id } => { - block.and(Lvalue::Static(Box::new(Static { def_id: id, ty: expr.ty }))) + block.and(Place::Static(Box::new(Static { def_id: id, ty: expr.ty }))) } ExprKind::Array { .. } | @@ -122,13 +122,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::InlineAsm { .. } | ExprKind::Yield { .. } | ExprKind::Call { .. } => { - // these are not lvalues, so we need to make a temporary. + // these are not places, so we need to make a temporary. debug_assert!(match Category::of(&expr.kind) { - Some(Category::Lvalue) => false, + Some(Category::Place) => false, _ => true, }); let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr)); - block.and(Lvalue::Local(temp)) + block.and(Place::Local(temp)) } } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index d17f00b489c31..88f1fb4f57518 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -68,8 +68,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block.and(Rvalue::Repeat(value_operand, count)) } ExprKind::Borrow { region, borrow_kind, arg } => { - let arg_lvalue = unpack!(block = this.as_lvalue(block, arg)); - block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue)) + let arg_place = unpack!(block = this.as_place(block, arg)); + block.and(Rvalue::Ref(region, borrow_kind, arg_place)) } ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!(block = this.as_operand(block, scope, lhs)); @@ -90,7 +90,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval)); let err = ConstMathErr::Overflow(Op::Neg); - block = this.assert(block, Operand::Consume(is_min), false, + block = this.assert(block, Operand::Move(is_min), false, AssertMessage::Math(err), expr_span); } block.and(Rvalue::UnaryOp(op, arg)) @@ -108,16 +108,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }); if let Some(scope) = scope { // schedule a shallow free of that memory, lest we unwind: - this.schedule_drop(expr_span, scope, &Lvalue::Local(result), value.ty); + this.schedule_drop(expr_span, scope, &Place::Local(result), value.ty); } // malloc some memory of suitable type (thus far, uninitialized): let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty); - this.cfg.push_assign(block, source_info, &Lvalue::Local(result), box_); + this.cfg.push_assign(block, source_info, &Place::Local(result), box_); // initialize the box contents: - unpack!(block = this.into(&Lvalue::Local(result).deref(), block, value)); - block.and(Rvalue::Use(Operand::Consume(Lvalue::Local(result)))) + unpack!(block = this.into(&Place::Local(result).deref(), block, value)); + block.and(Rvalue::Use(Operand::Move(Place::Local(result)))) } ExprKind::Cast { source } => { let source = this.hir.mirror(source); @@ -229,7 +229,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let field_names = this.hir.all_fields(adt_def, variant_index); let fields = if let Some(FruInfo { base, field_types }) = base { - let base = unpack!(block = this.as_lvalue(block, base)); + let base = unpack!(block = this.as_place(block, base)); // MIR does not natively support FRU, so for each // base-supplied field, generate an operand that @@ -238,7 +238,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .zip(field_types.into_iter()) .map(|(n, ty)| match fields_map.get(&n) { Some(v) => v.clone(), - None => Operand::Consume(base.clone().field(n, ty)) + None => this.consume_by_copy_or_move(base.clone().field(n, ty)) }) .collect() } else { @@ -325,10 +325,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } }); - block = self.assert(block, Operand::Consume(of), false, + block = self.assert(block, Operand::Move(of), false, AssertMessage::Math(err), span); - block.and(Rvalue::Use(Operand::Consume(val))) + block.and(Rvalue::Use(Operand::Move(val))) } else { if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) { // Checking division and remainder is more complex, since we 1. always check @@ -348,7 +348,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.push_assign(block, source_info, &is_zero, Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero)); - block = self.assert(block, Operand::Consume(is_zero), false, + block = self.assert(block, Operand::Move(is_zero), false, AssertMessage::Math(zero_err), span); // We only need to check for the overflow in one case: @@ -368,12 +368,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.push_assign(block, source_info, &is_min, Rvalue::BinaryOp(BinOp::Eq, lhs.clone(), min)); - let is_neg_1 = Operand::Consume(is_neg_1); - let is_min = Operand::Consume(is_min); + let is_neg_1 = Operand::Move(is_neg_1); + let is_min = Operand::Move(is_min); self.cfg.push_assign(block, source_info, &of, Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min)); - block = self.assert(block, Operand::Consume(of), false, + block = self.assert(block, Operand::Move(of), false, AssertMessage::Math(overflow_err), span); } } diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index ba422a8183160..1fc608c52c658 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -58,20 +58,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } // Careful here not to cause an infinite cycle. If we always - // called `into`, then for lvalues like `x.f`, it would + // called `into`, then for places like `x.f`, it would // eventually fallback to us, and we'd loop. There's a reason // for this: `as_temp` is the point where we bridge the "by - // reference" semantics of `as_lvalue` with the "by value" + // reference" semantics of `as_place` with the "by value" // semantics of `into`, `as_operand`, `as_rvalue`, and (of // course) `as_temp`. match Category::of(&expr.kind).unwrap() { - Category::Lvalue => { - let lvalue = unpack!(block = this.as_lvalue(block, expr)); - let rvalue = Rvalue::Use(Operand::Consume(lvalue)); - this.cfg.push_assign(block, source_info, &Lvalue::Local(temp), rvalue); + Category::Place => { + let place = unpack!(block = this.as_place(block, expr)); + let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); + this.cfg.push_assign(block, source_info, &Place::Local(temp), rvalue); } _ => { - unpack!(block = this.into(&Lvalue::Local(temp), block, expr)); + unpack!(block = this.into(&Place::Local(temp), block, expr)); } } @@ -79,7 +79,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // anything because no values with a destructor can be created in // a constant at this time, even if the type may need dropping. if let Some(temp_lifetime) = temp_lifetime { - this.schedule_drop(expr_span, temp_lifetime, &Lvalue::Local(temp), expr_ty); + this.schedule_drop(expr_span, temp_lifetime, &Place::Local(temp), expr_ty); } block.and(temp) diff --git a/src/librustc_mir/build/expr/category.rs b/src/librustc_mir/build/expr/category.rs index f05411aacab19..bce8e97d481f1 100644 --- a/src/librustc_mir/build/expr/category.rs +++ b/src/librustc_mir/build/expr/category.rs @@ -15,7 +15,7 @@ pub enum Category { // An assignable memory location like `x`, `x.f`, `foo()[3]`, that // sort of thing. Something that could appear on the LHS of an `=` // sign. - Lvalue, + Place, // A literal like `23` or `"foo"`. Does not include constant // expressions like `3 + 5`. @@ -51,7 +51,7 @@ impl Category { ExprKind::SelfRef | ExprKind::VarRef { .. } | ExprKind::StaticRef { .. } => - Some(Category::Lvalue), + Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::If { .. } | diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index cdbcb43370fe0..ed339110537f6 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -22,7 +22,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. pub fn into_expr(&mut self, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> @@ -241,7 +241,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { internal: true, is_user_variable: false }); - let ptr_temp = Lvalue::Local(ptr_temp); + let ptr_temp = Place::Local(ptr_temp); let block = unpack!(this.into(&ptr_temp, block, ptr)); this.into(&ptr_temp.deref(), block, val) } else { @@ -255,7 +255,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(block, source_info, TerminatorKind::Call { func: fun, args, - cleanup, + cleanup: Some(cleanup), destination: if diverges { None } else { @@ -273,7 +273,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Break { .. } | ExprKind::InlineAsm { .. } | ExprKind::Return {.. } => { - this.stmt_expr(block, expr) + unpack!(block = this.stmt_expr(block, expr)); + this.cfg.push_assign_unit(block, source_info, destination); + block.unit() } // these are the cases that are more naturally handled by some other mode diff --git a/src/librustc_mir/build/expr/mod.rs b/src/librustc_mir/build/expr/mod.rs index 17b34f4586e8b..025e77343e718 100644 --- a/src/librustc_mir/build/expr/mod.rs +++ b/src/librustc_mir/build/expr/mod.rs @@ -24,13 +24,13 @@ //! - `as_operand` -- evaluates the value and yields an `Operand`, //! suitable for use as an argument to an `Rvalue` //! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand` -//! except it always returns a fresh lvalue, even for constants +//! except it always returns a fresh place, even for constants //! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment; //! as of this writing, never needed outside of the `expr` module itself //! //! Sometimes though want the expression's *location*. An example //! would be during a match statement, or the operand of the `&` -//! operator. In that case, you want `as_lvalue`. This will create a +//! operator. In that case, you want `as_place`. This will create a //! temporary if necessary. //! //! Finally, if it's a constant you seek, then call @@ -46,7 +46,7 @@ //! struct expression (or other expression that creates a new value) //! is typically easiest to write in terms of `as_rvalue` or `into`, //! whereas a reference to a field is easiest to write in terms of -//! `as_lvalue`. (The exception to this is scope and paren +//! `as_place`. (The exception to this is scope and paren //! expressions, which have no category.) //! //! Therefore, the various functions above make use of one another in @@ -54,12 +54,12 @@ //! the most suitable spot to implement it, and then just let the //! other fns cycle around. The handoff works like this: //! -//! - `into(lv)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `lv` +//! - `into(place)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `place` //! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use` //! - `as_operand` -> either invokes `as_constant` or `as_temp` //! - `as_constant` -> (no fallback) -//! - `as_temp` -> creates a temporary and either calls `as_lvalue` or `into` -//! - `as_lvalue` -> for rvalues, falls back to `as_temp` and returns that +//! - `as_temp` -> creates a temporary and either calls `as_place` or `into` +//! - `as_place` -> for rvalues, falls back to `as_temp` and returns that //! //! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp` //! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact, @@ -68,10 +68,10 @@ //! Of those fallbacks, the most interesting one is `as_temp`, because //! it discriminates based on the category of the expression. This is //! basically the point where the "by value" operations are bridged -//! over to the "by reference" mode (`as_lvalue`). +//! over to the "by reference" mode (`as_place`). mod as_constant; -mod as_lvalue; +mod as_place; mod as_rvalue; mod as_operand; mod as_temp; diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index 3cfb0ff4010da..6f1fe8335780d 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -41,14 +41,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // dropped. if this.hir.needs_drop(lhs.ty) { let rhs = unpack!(block = this.as_local_operand(block, rhs)); - let lhs = unpack!(block = this.as_lvalue(block, lhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); unpack!(block = this.build_drop_and_replace( block, lhs_span, lhs, rhs )); block.unit() } else { let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); - let lhs = unpack!(block = this.as_lvalue(block, lhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); this.cfg.push_assign(block, source_info, &lhs, rhs); block.unit() } @@ -67,13 +67,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // As above, RTL. let rhs = unpack!(block = this.as_local_operand(block, rhs)); - let lhs = unpack!(block = this.as_lvalue(block, lhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); // we don't have to drop prior contents or anything // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty, - Operand::Consume(lhs.clone()), rhs)); + Operand::Copy(lhs.clone()), rhs)); this.cfg.push_assign(block, source_info, &lhs, result); block.unit() @@ -107,12 +107,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Return { value } => { block = match value { Some(value) => { - unpack!(this.into(&Lvalue::Local(RETURN_POINTER), block, value)) + unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)) } None => { this.cfg.push_assign_unit(block, source_info, - &Lvalue::Local(RETURN_POINTER)); + &Place::Local(RETURN_PLACE)); block } }; @@ -123,7 +123,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } ExprKind::InlineAsm { asm, outputs, inputs } => { let outputs = outputs.into_iter().map(|output| { - unpack!(block = this.as_lvalue(block, output)) + unpack!(block = this.as_place(block, output)) }).collect(); let inputs = inputs.into_iter().map(|input| { unpack!(block = this.as_local_operand(block, input)) diff --git a/src/librustc_mir/build/into.rs b/src/librustc_mir/build/into.rs index 0d912513c6c76..9c8d0b2aeb911 100644 --- a/src/librustc_mir/build/into.rs +++ b/src/librustc_mir/build/into.rs @@ -21,14 +21,14 @@ use rustc::mir::*; pub(in build) trait EvalInto<'tcx> { fn eval_into<'a, 'gcx>(self, builder: &mut Builder<'a, 'gcx, 'tcx>, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, block: BasicBlock) -> BlockAnd<()>; } impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn into(&mut self, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, block: BasicBlock, expr: E) -> BlockAnd<()> @@ -41,7 +41,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { fn eval_into<'a, 'gcx>(self, builder: &mut Builder<'a, 'gcx, 'tcx>, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, block: BasicBlock) -> BlockAnd<()> { let expr = builder.hir.mirror(self); @@ -52,7 +52,7 @@ impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { fn eval_into<'a, 'gcx>(self, builder: &mut Builder<'a, 'gcx, 'tcx>, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, block: BasicBlock) -> BlockAnd<()> { builder.into_expr(destination, block, self) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index f04dede6e4005..23095bc4269b5 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -30,13 +30,13 @@ mod util; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn match_expr(&mut self, - destination: &Lvalue<'tcx>, + destination: &Place<'tcx>, span: Span, mut block: BasicBlock, discriminant: ExprRef<'tcx>, arms: Vec>) -> BlockAnd<()> { - let discriminant_lvalue = unpack!(block = self.as_lvalue(block, discriminant)); + let discriminant_place = unpack!(block = self.as_place(block, discriminant)); let mut arm_blocks = ArmBlocks { blocks: arms.iter() @@ -54,11 +54,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { (body, scope.unwrap_or(self.visibility_scope)) }).collect(); + // create binding start block for link them by false edges + let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len()); + let pre_binding_blocks: Vec<_> = (0..candidate_count + 1) + .map(|_| self.cfg.start_new_block()).collect(); + // assemble a list of candidates: there is one candidate per // pattern, which means there may be more than one candidate // *per arm*. These candidates are kept sorted such that the // highest priority candidate comes first in the list. // (i.e. same order as in source) + let candidates: Vec<_> = arms.iter() .enumerate() @@ -66,18 +72,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { arm.patterns.iter() .map(move |pat| (arm_index, pat, arm.guard.clone())) }) - .map(|(arm_index, pattern, guard)| { + .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1))) + .map(|((arm_index, pattern, guard), + (pre_binding_block, next_candidate_pre_binding_block))| { Candidate { span: pattern.span, - match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)], + match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)], bindings: vec![], guard, arm_index, + pre_binding_block: *pre_binding_block, + next_candidate_pre_binding_block: *next_candidate_pre_binding_block, } }) .collect(); - // this will generate code to test discriminant_lvalue and + let outer_source_info = self.source_info(span); + self.cfg.terminate(*pre_binding_blocks.last().unwrap(), + outer_source_info, TerminatorKind::Unreachable); + + // this will generate code to test discriminant_place and // branch to the appropriate arm block let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block); @@ -125,22 +139,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { - let lvalue = self.storage_live_binding(block, var, irrefutable_pat.span); - unpack!(block = self.into(&lvalue, block, initializer)); + let place = self.storage_live_binding(block, var, irrefutable_pat.span); + unpack!(block = self.into(&place, block, initializer)); self.schedule_drop_for_binding(var, irrefutable_pat.span); block.unit() } _ => { - let lvalue = unpack!(block = self.as_lvalue(block, initializer)); - self.lvalue_into_pattern(block, irrefutable_pat, &lvalue) + let place = unpack!(block = self.as_place(block, initializer)); + self.place_into_pattern(block, irrefutable_pat, &place) } } } - pub fn lvalue_into_pattern(&mut self, + pub fn place_into_pattern(&mut self, mut block: BasicBlock, irrefutable_pat: Pattern<'tcx>, - initializer: &Lvalue<'tcx>) + initializer: &Place<'tcx>) -> BlockAnd<()> { // create a dummy candidate let mut candidate = Candidate { @@ -148,7 +162,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)], bindings: vec![], guard: None, - arm_index: 0, // since we don't call `match_candidates`, this field is unused + + // since we don't call `match_candidates`, next fields is unused + arm_index: 0, + pre_binding_block: block, + next_candidate_pre_binding_block: block }; // Simplify the candidate. Since the pattern is irrefutable, this should @@ -205,7 +223,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } pub fn storage_live_binding(&mut self, block: BasicBlock, var: NodeId, span: Span) - -> Lvalue<'tcx> + -> Place<'tcx> { let local_id = self.var_indices[&var]; let source_info = self.source_info(span); @@ -213,7 +231,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, kind: StatementKind::StorageLive(local_id) }); - Lvalue::Local(local_id) + Place::Local(local_id) } pub fn schedule_drop_for_binding(&mut self, var: NodeId, span: Span) { @@ -221,7 +239,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let var_ty = self.local_decls[local_id].ty; let hir_id = self.hir.tcx().hir.node_to_hir_id(var); let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id); - self.schedule_drop(span, region_scope, &Lvalue::Local(local_id), var_ty); + self.schedule_drop(span, region_scope, &Place::Local(local_id), var_ty); } pub fn visit_bindings(&mut self, pattern: &Pattern<'tcx>, f: &mut F) @@ -278,12 +296,16 @@ pub struct Candidate<'pat, 'tcx:'pat> { // ...and then we branch to arm with this index. arm_index: usize, + + // ...and the blocks for add false edges between candidates + pre_binding_block: BasicBlock, + next_candidate_pre_binding_block: BasicBlock, } #[derive(Clone, Debug)] struct Binding<'tcx> { span: Span, - source: Lvalue<'tcx>, + source: Place<'tcx>, name: Name, var_id: NodeId, var_ty: Ty<'tcx>, @@ -293,8 +315,8 @@ struct Binding<'tcx> { #[derive(Clone, Debug)] pub struct MatchPair<'pat, 'tcx:'pat> { - // this lvalue... - lvalue: Lvalue<'tcx>, + // this place... + place: Place<'tcx>, // ... must match this pattern. pattern: &'pat Pattern<'tcx>, @@ -398,6 +420,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); debug!("match_candidates: {:?} candidates fully matched", fully_matched); let mut unmatched_candidates = candidates.split_off(fully_matched); + + let fully_matched_with_guard = + candidates.iter().take_while(|c| c.guard.is_some()).count(); + + let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() { + candidates.split_off(fully_matched_with_guard + 1) + } else { + vec![] + }; + for candidate in candidates { // If so, apply any bindings, test the guard (if any), and // branch to the arm. @@ -406,7 +438,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } else { // if None is returned, then any remaining candidates // are unreachable (at least not through this path). - return vec![]; + // Link them with false edges. + debug!("match_candidates: add false edges for unreachable {:?} and unmatched {:?}", + unreachable_candidates, unmatched_candidates); + for candidate in unreachable_candidates { + let source_info = self.source_info(candidate.span); + let target = self.cfg.start_new_block(); + if let Some(otherwise) = self.bind_and_guard_matched_candidate(target, + arm_blocks, + candidate) { + self.cfg.terminate(otherwise, source_info, TerminatorKind::Unreachable); + } + } + + if unmatched_candidates.is_empty() { + return vec![] + } else { + let target = self.cfg.start_new_block(); + return self.match_candidates(span, arm_blocks, unmatched_candidates, target); + } } } @@ -421,9 +471,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.test_candidates(span, arm_blocks, &unmatched_candidates, block); // If the target candidates were exhaustive, then we are done. - if otherwise.is_empty() { - return vec![]; - } + // But for borrowck continue build decision tree. // If all candidates were sorted into `target_candidates` somewhere, then // the initial set was inexhaustive. @@ -587,7 +635,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match test.kind { TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { for candidate in candidates.iter() { - if !self.add_cases_to_switch(&match_pair.lvalue, + if !self.add_cases_to_switch(&match_pair.place, candidate, switch_ty, options, @@ -598,7 +646,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::Switch { adt_def: _, ref mut variants} => { for candidate in candidates.iter() { - if !self.add_variants_to_switch(&match_pair.lvalue, + if !self.add_variants_to_switch(&match_pair.place, candidate, variants) { break; @@ -613,7 +661,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); - let target_blocks = self.perform_test(block, &match_pair.lvalue, &test); + let target_blocks = self.perform_test(block, &match_pair.place, &test); let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect(); // Sort the candidates into the appropriate vector in @@ -622,7 +670,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // that point, we stop sorting. let tested_candidates = candidates.iter() - .take_while(|c| self.sort_candidate(&match_pair.lvalue, + .take_while(|c| self.sort_candidate(&match_pair.place, &test, c, &mut target_candidates)) @@ -671,9 +719,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { debug_assert!(candidate.match_pairs.is_empty()); - self.bind_matched_candidate(block, candidate.bindings); - let arm_block = arm_blocks.blocks[candidate.arm_index]; + let candidate_source_info = self.source_info(candidate.span); + + self.cfg.terminate(block, candidate_source_info, + TerminatorKind::Goto { target: candidate.pre_binding_block }); + + block = self.cfg.start_new_block(); + self.cfg.terminate(candidate.pre_binding_block, candidate_source_info, + TerminatorKind::FalseEdges { + real_target: block, + imaginary_targets: + vec![candidate.next_candidate_pre_binding_block]}); + + self.bind_matched_candidate(block, candidate.bindings); if let Some(guard) = candidate.guard { // the block to branch to if the guard fails; if there is no @@ -681,13 +740,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let guard = self.hir.mirror(guard); let source_info = self.source_info(guard.span); let cond = unpack!(block = self.as_local_operand(block, guard)); - let otherwise = self.cfg.start_new_block(); + + let false_edge_block = self.cfg.start_new_block(); self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise)); + TerminatorKind::if_(self.hir.tcx(), cond, arm_block, + false_edge_block)); + + let otherwise = self.cfg.start_new_block(); + self.cfg.terminate(false_edge_block, source_info, + TerminatorKind::FalseEdges { + real_target: otherwise, + imaginary_targets: + vec![candidate.next_candidate_pre_binding_block] }); Some(otherwise) } else { - let source_info = self.source_info(candidate.span); - self.cfg.terminate(block, source_info, + self.cfg.terminate(block, candidate_source_info, TerminatorKind::Goto { target: arm_block }); None } @@ -706,7 +773,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.schedule_drop_for_binding(binding.var_id, binding.span); let rvalue = match binding.binding_mode { BindingMode::ByValue => - Rvalue::Use(Operand::Consume(binding.source)), + Rvalue::Use(self.consume_by_copy_or_move(binding.source)), BindingMode::ByRef(region, borrow_kind) => Rvalue::Ref(region, borrow_kind, binding.source), }; diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 6e3eef5735233..4ae373c7c8223 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -10,11 +10,11 @@ //! Simplifying Candidates //! -//! *Simplifying* a match pair `lvalue @ pattern` means breaking it down +//! *Simplifying* a match pair `place @ pattern` means breaking it down //! into bindings or other, simpler match pairs. For example: //! -//! - `lvalue @ (P1, P2)` can be simplified to `[lvalue.0 @ P1, lvalue.1 @ P2]` -//! - `lvalue @ x` can be simplified to `[]` by binding `x` to `lvalue` +//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]` +//! - `place @ x` can be simplified to `[]` by binding `x` to `place` //! //! The `simplify_candidate` routine just repeatedly applies these //! sort of simplifications until there is nothing left to @@ -26,7 +26,6 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use build::matches::{Binding, MatchPair, Candidate}; use hair::*; use rustc::mir::*; -use rustc_data_structures::fx::FxHashMap; use std::mem; @@ -74,7 +73,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { name, mutability, span: match_pair.pattern.span, - source: match_pair.lvalue.clone(), + source: match_pair.place.clone(), var_id: var, var_ty: ty, binding_mode: mode, @@ -82,7 +81,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now - candidate.match_pairs.push(MatchPair::new(match_pair.lvalue, subpattern)); + candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); } Ok(()) @@ -99,24 +98,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { - if self.hir.tcx().sess.features.borrow().never_type { - let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { - i == variant_index || { - let mut visited = FxHashMap::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) + let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { + i == variant_index || { + self.hir.tcx().sess.features.borrow().never_type && + self.hir.tcx().is_variant_uninhabited_from_all_modules(v, substs) } + }); + if irrefutable { + let place = match_pair.place.downcast(adt_def, variant_index); + candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns)); + Ok(()) } else { Err(match_pair) } @@ -124,7 +115,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Array { ref prefix, ref slice, ref suffix } => { self.prefix_slice_suffix(&mut candidate.match_pairs, - &match_pair.lvalue, + &match_pair.place, prefix, slice.as_ref(), suffix); @@ -134,13 +125,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Leaf { ref subpatterns } => { // tuple struct, match subpats (if any) candidate.match_pairs - .extend(self.field_match_pairs(match_pair.lvalue, subpatterns)); + .extend(self.field_match_pairs(match_pair.place, subpatterns)); Ok(()) } PatternKind::Deref { ref subpattern } => { - let lvalue = match_pair.lvalue.deref(); - candidate.match_pairs.push(MatchPair::new(lvalue, subpattern)); + let place = match_pair.place.deref(); + candidate.match_pairs.push(MatchPair::new(place, subpattern)); Ok(()) } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 7b91c43aa3722..b2357b771572f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -39,7 +39,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: match_pair.pattern.span, kind: TestKind::Switch { adt_def: adt_def.clone(), - variants: BitVector::new(self.hir.num_variants(adt_def)), + variants: BitVector::new(adt_def.variants.len()), }, } } @@ -109,21 +109,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } pub fn add_cases_to_switch<'pat>(&mut self, - test_lvalue: &Lvalue<'tcx>, + test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, options: &mut Vec<&'tcx ty::Const<'tcx>>, indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>) -> bool { - let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) { + let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { Some(match_pair) => match_pair, _ => { return false; } }; match *match_pair.pattern.kind { PatternKind::Constant { value } => { - // if the lvalues match, the type should match + // if the places match, the type should match assert_eq!(match_pair.pattern.ty, switch_ty); indices.entry(value) @@ -150,12 +150,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } pub fn add_variants_to_switch<'pat>(&mut self, - test_lvalue: &Lvalue<'tcx>, + test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, variants: &mut BitVector) -> bool { - let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) { + let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { Some(match_pair) => match_pair, _ => { return false; } }; @@ -177,14 +177,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Generates the code to perform a test. pub fn perform_test(&mut self, block: BasicBlock, - lvalue: &Lvalue<'tcx>, + place: &Place<'tcx>, test: &Test<'tcx>) -> Vec { let source_info = self.source_info(test.span); match test.kind { TestKind::Switch { adt_def, ref variants } => { // Variants is a BitVec of indexes into adt_def.variants. - let num_enum_variants = self.hir.num_variants(adt_def); + let num_enum_variants = adt_def.variants.len(); let used_variants = variants.count(); let mut otherwise_block = None; let mut target_blocks = Vec::with_capacity(num_enum_variants); @@ -205,17 +205,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(otherwise_block) = otherwise_block { targets.push(otherwise_block); } else { - values.pop(); + targets.push(self.unreachable_block()); } debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}", num_enum_variants, values, variants); let discr_ty = adt_def.repr.discr_type().to_ty(tcx); let discr = self.temp(discr_ty, test.span); self.cfg.push_assign(block, source_info, &discr, - Rvalue::Discriminant(lvalue.clone())); + Rvalue::Discriminant(place.clone())); assert_eq!(values.len() + 1, targets.len()); self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), + discr: Operand::Move(discr), switch_ty: discr_ty, values: From::from(values), targets, @@ -233,7 +233,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ConstVal::Bool(false) => vec![false_bb, true_bb], v => span_bug!(test.span, "expected boolean value but got {:?}", v) }; - (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Consume(lvalue.clone()), + (ret, TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place.clone()), true_bb, false_bb)) } else { // The switch may be inexhaustive so we @@ -248,7 +248,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { v.val.to_const_int().expect("switching on integral") ).collect(); (targets.clone(), TerminatorKind::SwitchInt { - discr: Operand::Consume(lvalue.clone()), + discr: Operand::Copy(place.clone()), switch_ty, values: From::from(values), targets, @@ -259,21 +259,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } TestKind::Eq { value, mut ty } => { - let mut val = Operand::Consume(lvalue.clone()); + let mut val = Operand::Copy(place.clone()); // If we're using b"..." as a pattern, we need to insert an // unsizing coercion, as the byte string has the type &[u8; N]. let expect = if let ConstVal::ByteStr(bytes) = value.val { let tcx = self.hir.tcx(); - // Unsize the lvalue to &[u8], too, if necessary. + // Unsize the place to &[u8], too, if necessary. if let ty::TyRef(region, mt) = ty.sty { if let ty::TyArray(_, _) = mt.ty.sty { ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); let val_slice = self.temp(ty, test.span); self.cfg.push_assign(block, source_info, &val_slice, Rvalue::Cast(CastKind::Unsize, val, ty)); - val = Operand::Consume(val_slice); + val = Operand::Move(val_slice); } } @@ -288,7 +288,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let slice = self.temp(ty, test.span); self.cfg.push_assign(block, source_info, &slice, Rvalue::Cast(CastKind::Unsize, array, ty)); - Operand::Consume(slice) + Operand::Move(slice) } else { self.literal_operand(test.span, ty, Literal::Value { value @@ -315,14 +315,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }), args: vec![val, expect], destination: Some((eq_result.clone(), eq_block)), - cleanup, + cleanup: Some(cleanup), }); // check the result let block = self.cfg.start_new_block(); self.cfg.terminate(eq_block, source_info, TerminatorKind::if_(self.hir.tcx(), - Operand::Consume(eq_result), + Operand::Move(eq_result), block, fail)); vec![block, fail] } else { @@ -335,7 +335,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. let lo = self.literal_operand(test.span, ty.clone(), lo.clone()); let hi = self.literal_operand(test.span, ty.clone(), hi.clone()); - let val = Operand::Consume(lvalue.clone()); + let val = Operand::Copy(place.clone()); let fail = self.cfg.start_new_block(); let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone()); @@ -352,9 +352,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let (actual, result) = (self.temp(usize_ty, test.span), self.temp(bool_ty, test.span)); - // actual = len(lvalue) + // actual = len(place) self.cfg.push_assign(block, source_info, - &actual, Rvalue::Len(lvalue.clone())); + &actual, Rvalue::Len(place.clone())); // expected = let expected = self.push_usize(block, source_info, len); @@ -362,14 +362,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // result = actual == expected OR result = actual < expected self.cfg.push_assign(block, source_info, &result, Rvalue::BinaryOp(op, - Operand::Consume(actual), - Operand::Consume(expected))); + Operand::Move(actual), + Operand::Move(expected))); // branch based on result let (false_bb, true_bb) = (self.cfg.start_new_block(), self.cfg.start_new_block()); self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), true_bb, false_bb)); vec![true_bb, false_bb] } @@ -394,12 +394,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // branch based on result let target_block = self.cfg.start_new_block(); self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Consume(result), + TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), target_block, fail_block)); target_block } - /// Given that we are performing `test` against `test_lvalue`, + /// Given that we are performing `test` against `test_place`, /// this job sorts out what the status of `candidate` will be /// after the test. The `resulting_candidates` vector stores, for /// each possible outcome of `test`, a vector of the candidates @@ -430,12 +430,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// not apply to this candidate, but it might be we can get /// tighter match code if we do something a bit different. pub fn sort_candidate<'pat>(&mut self, - test_lvalue: &Lvalue<'tcx>, + test_place: &Place<'tcx>, test: &Test<'tcx>, candidate: &Candidate<'pat, 'tcx>, resulting_candidates: &mut [Vec>]) -> bool { - // Find the match_pair for this lvalue (if any). At present, + // Find the match_pair for this place (if any). At present, // afaik, there can be at most one. (In the future, if we // adopted a more general `@` operator, there might be more // than one, but it'd be very unusual to have two sides that @@ -443,12 +443,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // away.) let tested_match_pair = candidate.match_pairs.iter() .enumerate() - .filter(|&(_, mp)| mp.lvalue == *test_lvalue) + .filter(|&(_, mp)| mp.place == *test_place) .next(); let (match_pair_index, match_pair) = match tested_match_pair { Some(pair) => pair, None => { - // We are not testing this lvalue. Therefore, this + // We are not testing this place. Therefore, this // candidate applies to ALL outcomes. return false; } @@ -598,6 +598,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), arm_index: candidate.arm_index, + pre_binding_block: candidate.pre_binding_block, + next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block, } } @@ -612,7 +614,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.candidate_without_match_pair(match_pair_index, candidate); self.prefix_slice_suffix( &mut new_candidate.match_pairs, - &candidate.match_pairs[match_pair_index].lvalue, + &candidate.match_pairs[match_pair_index].place, prefix, opt_slice, suffix); @@ -633,15 +635,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. let elem = ProjectionElem::Downcast(adt_def, variant_index); - let downcast_lvalue = match_pair.lvalue.clone().elem(elem); // `(x as Variant)` + let downcast_place = match_pair.place.clone().elem(elem); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter() .map(|subpattern| { // e.g., `(x as Variant).0` - let lvalue = downcast_lvalue.clone().field(subpattern.field, + let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty); // e.g., `(x as Variant).0 @ P1` - MatchPair::new(lvalue, &subpattern.pattern) + MatchPair::new(place, &subpattern.pattern) }); // In addition, we need all the other match pairs from the old candidate. @@ -659,6 +661,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), arm_index: candidate.arm_index, + pre_binding_block: candidate.pre_binding_block, + next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block, } } diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index 3e303865ac423..cfd9100fc6ae7 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -16,21 +16,21 @@ use std::u32; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn field_match_pairs<'pat>(&mut self, - lvalue: Lvalue<'tcx>, + place: Place<'tcx>, subpatterns: &'pat [FieldPattern<'tcx>]) -> Vec> { subpatterns.iter() .map(|fieldpat| { - let lvalue = lvalue.clone().field(fieldpat.field, + let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty); - MatchPair::new(lvalue, &fieldpat.pattern) + MatchPair::new(place, &fieldpat.pattern) }) .collect() } pub fn prefix_slice_suffix<'pat>(&mut self, match_pairs: &mut Vec>, - lvalue: &Lvalue<'tcx>, + place: &Place<'tcx>, prefix: &'pat [Pattern<'tcx>], opt_slice: Option<&'pat Pattern<'tcx>>, suffix: &'pat [Pattern<'tcx>]) { @@ -47,13 +47,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { min_length, from_end: false, }; - let lvalue = lvalue.clone().elem(elem); - MatchPair::new(lvalue, subpattern) + let place = place.clone().elem(elem); + MatchPair::new(place, subpattern) }) ); if let Some(subslice_pat) = opt_slice { - let subslice = lvalue.clone().elem(ProjectionElem::Subslice { + let subslice = place.clone().elem(ProjectionElem::Subslice { from: prefix.len() as u32, to: suffix.len() as u32 }); @@ -70,17 +70,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { min_length, from_end: true, }; - let lvalue = lvalue.clone().elem(elem); - MatchPair::new(lvalue, subpattern) + let place = place.clone().elem(elem); + MatchPair::new(place, subpattern) }) ); } } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - pub fn new(lvalue: Lvalue<'tcx>, pattern: &'pat Pattern<'tcx>) -> MatchPair<'pat, 'tcx> { + pub fn new(place: Place<'tcx>, pattern: &'pat Pattern<'tcx>) -> MatchPair<'pat, 'tcx> { MatchPair { - lvalue, + place, pattern, slice_len_checked: false, } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 1976b70ac0a23..8486c63baac66 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -19,7 +19,7 @@ use rustc::ty::{self, Ty}; use rustc::mir::*; use syntax::ast; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Add a new temporary value of type `ty` storing the result of @@ -27,12 +27,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// /// NB: **No cleanup is scheduled for this temporary.** You should /// call `schedule_drop` once the temporary is initialized. - pub fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Lvalue<'tcx> { + pub fn temp(&mut self, ty: Ty<'tcx>, span: Span) -> Place<'tcx> { let temp = self.local_decls.push(LocalDecl::new_temp(ty, span)); - let lvalue = Lvalue::Local(temp); + let place = Place::Local(temp); debug!("temp: created temp {:?} with type {:?}", - lvalue, self.local_decls[temp].ty); - lvalue + place, self.local_decls[temp].ty); + place } pub fn literal_operand(&mut self, @@ -121,7 +121,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block: BasicBlock, source_info: SourceInfo, value: u64) - -> Lvalue<'tcx> { + -> Place<'tcx> { let usize_ty = self.hir.usize_ty(); let temp = self.temp(usize_ty, source_info.span); self.cfg.push_assign_constant( @@ -133,4 +133,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }); temp } + + pub fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { + let tcx = self.hir.tcx(); + let ty = place.ty(&self.local_decls, tcx).to_ty(tcx); + if self.hir.type_moves_by_default(ty, DUMMY_SP) { + Operand::Move(place) + } else { + Operand::Copy(place) + } + } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 68ef646184c2c..d814b092c9d69 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -13,14 +13,14 @@ use build; use hair::cx::Cx; use hair::LintLevel; use rustc::hir; -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{DefId, LocalDefId}; use rustc::middle::region; use rustc::mir::*; -use rustc::mir::transform::MirSource; -use rustc::mir::visit::{MutVisitor, Lookup}; +use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; +use rustc_const_eval::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; use std::mem; @@ -29,6 +29,7 @@ use syntax::abi::Abi; use syntax::ast; use syntax::symbol::keywords; use syntax_pos::Span; +use transform::MirSource; use util as mir_util; /// Construct the MIR for a given def-id. @@ -82,12 +83,11 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t _ => unsupported(), }; - let src = MirSource::from_node(tcx, id); tcx.infer_ctxt().enter(|infcx| { - let cx = Cx::new(&infcx, src); + let cx = Cx::new(&infcx, id); let mut mir = if cx.tables().tainted_by_errors { build::construct_error(cx, body_id) - } else if let MirSource::Fn(id) = src { + } else if let hir::BodyOwnerKind::Fn = cx.body_owner_kind { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) let fn_hir_id = tcx.hir.node_to_hir_id(id); @@ -100,10 +100,10 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t // HACK(eddyb) Avoid having RustCall on closures, // as it adds unnecessary (and wrong) auto-tupling. abi = Abi::Rust; - Some((closure_self_ty(tcx, id, body_id), None)) + Some((liberated_closure_env_ty(tcx, id, body_id), None)) } ty::TyGenerator(..) => { - let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id); + let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id); Some((gen_ty, None)) } _ => None, @@ -127,7 +127,12 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t let arguments = implicit_argument.into_iter().chain(explicit_arguments); let (yield_ty, return_ty) = if body.is_generator { - let gen_sig = cx.tables().generator_sigs()[fn_hir_id].clone().unwrap(); + let gen_sig = match ty.sty { + ty::TyGenerator(gen_def_id, gen_substs, ..) => + gen_substs.generator_sig(gen_def_id, tcx), + _ => + span_bug!(tcx.hir.span(id), "generator w/o generator type: {:?}", ty), + }; (Some(gen_sig.yield_ty), gen_sig.return_ty) } else { (None, fn_sig.output()) @@ -149,7 +154,8 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t mem::transmute::>(mir) }; - mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, None, "mir_map", &0, + MirSource::item(def_id), &mir, |_, _| Ok(()) ); mir }) @@ -164,7 +170,7 @@ struct GlobalizeMir<'a, 'gcx: 'a> { } impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) { + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { if let Some(lifted) = self.tcx.lift(ty) { *ty = lifted; } else { @@ -213,8 +219,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let span = tcx.hir.span(ctor_id); if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { tcx.infer_ctxt().enter(|infcx| { - let (mut mir, src) = - shim::build_adt_ctor(&infcx, ctor_id, fields, span); + let mut mir = shim::build_adt_ctor(&infcx, ctor_id, fields, span); // Convert the Mir to global types. let tcx = infcx.tcx.global_tcx(); @@ -227,7 +232,9 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mem::transmute::>(mir) }; - mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, None, "mir_map", &0, + MirSource::item(tcx.hir.local_def_id(ctor_id)), + &mir, |_, _| Ok(()) ); mir }) @@ -239,32 +246,20 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from -pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - closure_expr_id: ast::NodeId, - body_id: hir::BodyId) - -> Ty<'tcx> { +fn liberated_closure_env_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + closure_expr_id: ast::NodeId, + body_id: hir::BodyId) + -> Ty<'tcx> { let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id); let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id); - let closure_def_id = tcx.hir.local_def_id(closure_expr_id); - let region = ty::ReFree(ty::FreeRegion { - scope: closure_def_id, - bound_region: ty::BoundRegion::BrEnv, - }); - let region = tcx.mk_region(region); - - match tcx.closure_kind(closure_def_id) { - ty::ClosureKind::Fn => - tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutImmutable }), - ty::ClosureKind::FnMut => - tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutMutable }), - ty::ClosureKind::FnOnce => - closure_ty - } + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(closure_def_id, closure_substs) => (closure_def_id, closure_substs), + _ => bug!("closure expr does not have closure type: {:?}", closure_ty) + }; + + let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); + tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty) } struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { @@ -298,32 +293,22 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// Maps node ids of variable bindings to the `Local`s created for them. var_indices: NodeMap, local_decls: IndexVec>, - unit_temp: Option>, + unit_temp: Option>, /// cached block with the RESUME terminator; this is created /// when first set of cleanups are built. cached_resume_block: Option, /// cached block with the RETURN terminator cached_return_block: Option, + /// cached block with the UNREACHABLE terminator + cached_unreachable_block: Option, } struct CFG<'tcx> { basic_blocks: IndexVec>, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct ScopeId(u32); - -impl Idx for ScopeId { - fn new(index: usize) -> ScopeId { - assert!(index < (u32::MAX as usize)); - ScopeId(index as u32) - } - - fn index(self) -> usize { - self.0 as usize - } -} +newtype_index!(ScopeId); /////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a @@ -410,6 +395,11 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, TerminatorKind::Goto { target: return_block }); builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); + // Attribute any unreachable codepaths to the function's closing brace + if let Some(unreachable_block) = builder.cached_unreachable_block { + builder.cfg.terminate(unreachable_block, source_info, + TerminatorKind::Unreachable); + } return_block.unit() })); assert_eq!(block, builder.return_block()); @@ -425,10 +415,10 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, freevars.iter().map(|fv| { let var_id = fv.var_id(); let var_hir_id = tcx.hir.node_to_hir_id(var_id); - let closure_expr_id = tcx.hir.local_def_id(fn_id).index; + let closure_expr_id = tcx.hir.local_def_id(fn_id); let capture = hir.tables().upvar_capture(ty::UpvarId { var_id: var_hir_id, - closure_expr_id, + closure_expr_id: LocalDefId::from_def_id(closure_expr_id), }); let by_ref = match capture { ty::UpvarCapture::ByValue => false, @@ -437,17 +427,27 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, let mut decl = UpvarDecl { debug_name: keywords::Invalid.name(), by_ref, + mutability: Mutability::Not, }; if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) { if let hir::PatKind::Binding(_, _, ref ident, _) = pat.node { decl.debug_name = ident.node; + + let bm = *hir.tables.pat_binding_modes() + .get(pat.hir_id) + .expect("missing binding mode"); + if bm == ty::BindByValue(hir::MutMutable) { + decl.mutability = Mutability::Mut; + } else { + decl.mutability = Mutability::Not; + } } } decl }).collect() }); - let mut mir = builder.finish(upvar_decls, return_ty, yield_ty); + let mut mir = builder.finish(upvar_decls, yield_ty); mir.spread_arg = spread_arg; mir } @@ -464,7 +464,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let mut block = START_BLOCK; let expr = builder.hir.mirror(ast_expr); - unpack!(block = builder.into_expr(&Lvalue::Local(RETURN_POINTER), block, expr)); + unpack!(block = builder.into_expr(&Place::Local(RETURN_PLACE), block, expr)); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); @@ -472,7 +472,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, // Constants can't `return` so a return block should not be created. assert_eq!(builder.cached_return_block, None); - builder.finish(vec![], ty, None) + builder.finish(vec![], None) } fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, @@ -484,7 +484,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty); let source_info = builder.source_info(span); builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); - builder.finish(vec![], ty, None) + builder.finish(vec![], None) } impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -507,12 +507,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { push_unsafe_count: 0, unpushed_unsafe: safety, breakable_scopes: vec![], - local_decls: IndexVec::from_elem_n(LocalDecl::new_return_pointer(return_ty, + local_decls: IndexVec::from_elem_n(LocalDecl::new_return_place(return_ty, span), 1), var_indices: NodeMap(), unit_temp: None, cached_resume_block: None, - cached_return_block: None + cached_return_block: None, + cached_unreachable_block: None, }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -526,7 +527,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn finish(self, upvar_decls: Vec, - return_ty: Ty<'tcx>, yield_ty: Option>) -> Mir<'tcx> { for (index, block) in self.cfg.basic_blocks.iter().enumerate() { @@ -537,9 +537,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Mir::new(self.cfg.basic_blocks, self.visibility_scopes, - ClearOnDecode::Set(self.visibility_scope_info), + ClearCrossCrate::Set(self.visibility_scope_info), IndexVec::new(), - return_ty, yield_ty, self.local_decls, self.arg_count, @@ -566,7 +565,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } self.local_decls.push(LocalDecl { - mutability: Mutability::Not, + mutability: Mutability::Mut, ty, source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, @@ -582,19 +581,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let mut scope = None; // Bind the argument patterns for (index, &(ty, pattern)) in arguments.iter().enumerate() { - // Function arguments always get the first Local indices after the return pointer - let lvalue = Lvalue::Local(Local::new(index + 1)); + // Function arguments always get the first Local indices after the return place + let local = Local::new(index + 1); + let place = Place::Local(local); if let Some(pattern) = pattern { let pattern = self.hir.pattern_from_hir(pattern); - scope = self.declare_bindings(scope, ast_body.span, - LintLevel::Inherited, &pattern); - unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue)); + + match *pattern.kind { + // Don't introduce extra copies for simple bindings + PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => { + self.local_decls[local].mutability = mutability; + self.var_indices.insert(var, local); + } + _ => { + scope = self.declare_bindings(scope, ast_body.span, + LintLevel::Inherited, &pattern); + unpack!(block = self.place_into_pattern(block, pattern, &place)); + } + } } // Make sure we drop (parts of) the argument even when not matched on. self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span), - argument_scope, &lvalue, ty); + argument_scope, &place, ty); } @@ -604,10 +614,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } let body = self.hir.mirror(ast_body); - self.into(&Lvalue::Local(RETURN_POINTER), block, body) + self.into(&Place::Local(RETURN_PLACE), block, body) } - fn get_unit_temp(&mut self) -> Lvalue<'tcx> { + fn get_unit_temp(&mut self) -> Place<'tcx> { match self.unit_temp { Some(ref tmp) => tmp.clone(), None => { @@ -630,6 +640,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } + + fn unreachable_block(&mut self) -> BasicBlock { + match self.cached_unreachable_block { + Some(ub) => ub, + None => { + let ub = self.cfg.start_new_block(); + self.cached_unreachable_block = Some(ub); + ub + } + } + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 032734194329c..630d0bf179294 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -39,10 +39,10 @@ mapping is from one scope to a vector of SEME regions. ### Drops The primary purpose for scopes is to insert drops: while translating -the contents, we also accumulate lvalues that need to be dropped upon +the contents, we also accumulate places that need to be dropped upon exit from each scope. This is done by calling `schedule_drop`. Once a drop is scheduled, whenever we branch out we will insert drops of all -those lvalues onto the outgoing edge. Note that we don't know the full +those places onto the outgoing edge. Note that we don't know the full set of scheduled drops up front, and so whenever we exit from the scope we only drop the values scheduled thus far. For example, consider the scope S corresponding to this loop: @@ -91,9 +91,9 @@ use build::{BlockAnd, BlockAndExtension, Builder, CFG}; use hair::LintLevel; use rustc::middle::region; use rustc::ty::{Ty, TyCtxt}; +use rustc::hir; use rustc::hir::def_id::LOCAL_CRATE; use rustc::mir::*; -use rustc::mir::transform::MirSource; use syntax_pos::{Span}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::fx::FxHashMap; @@ -120,7 +120,7 @@ pub struct Scope<'tcx> { /// * freeing up stack space has no effect during unwinding needs_cleanup: bool, - /// set of lvalues to drop when exiting this scope. This starts + /// set of places to drop when exiting this scope. This starts /// out empty but grows as variables are declared during the /// building process. This is a stack, so we always drop from the /// end of the vector (top of the stack) first. @@ -131,15 +131,18 @@ pub struct Scope<'tcx> { /// The cache for drop chain on "generator drop" exit. cached_generator_drop: Option, + + /// The cache for drop chain on "unwind" exit. + cached_unwind: CachedBlock, } #[derive(Debug)] struct DropData<'tcx> { - /// span where drop obligation was incurred (typically where lvalue was declared) + /// span where drop obligation was incurred (typically where place was declared) span: Span, - /// lvalue to drop - location: Lvalue<'tcx>, + /// place to drop + location: Place<'tcx>, /// Whether this is a full value Drop, or just a StorageDead. kind: DropKind @@ -154,6 +157,11 @@ struct CachedBlock { unwind: Option, /// The cached block for unwinds during cleanups-on-generator-drop path + /// + /// This is split from the standard unwind path here to prevent drop + /// elaboration from creating drop flags that would have to be captured + /// by the generator. I'm not sure how important this optimization is, + /// but it is here. generator_drop: Option, } @@ -176,7 +184,7 @@ pub struct BreakableScope<'tcx> { pub break_block: BasicBlock, /// The destination of the loop/block expression itself (i.e. where to put the result of a /// `break` expression) - pub break_destination: Lvalue<'tcx>, + pub break_destination: Place<'tcx>, } impl CachedBlock { @@ -217,34 +225,29 @@ impl<'tcx> Scope<'tcx> { /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a /// larger extent of code. /// - /// `unwind` controls whether caches for the unwind branch are also invalidated. - fn invalidate_cache(&mut self, unwind: bool) { + /// `storage_only` controls whether to invalidate only drop paths run `StorageDead`. + /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current + /// top-of-scope (as opposed to dependent scopes). + fn invalidate_cache(&mut self, storage_only: bool, this_scope_only: bool) { + // FIXME: maybe do shared caching of `cached_exits` etc. to handle functions + // with lots of `try!`? + + // cached exits drop storage and refer to the top-of-scope self.cached_exits.clear(); - if !unwind { return; } - for dropdata in &mut self.drops { - if let DropKind::Value { ref mut cached_block } = dropdata.kind { - cached_block.invalidate(); - } + + if !storage_only { + // the current generator drop and unwind ignore + // storage but refer to top-of-scope + self.cached_generator_drop = None; + self.cached_unwind.invalidate(); } - } - /// Returns the cached entrypoint for diverging exit from this scope. - /// - /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for - /// this method to work correctly. - fn cached_block(&self, generator_drop: bool) -> Option { - let mut drops = self.drops.iter().rev().filter_map(|data| { - match data.kind { - DropKind::Value { cached_block } => { - Some(cached_block.get(generator_drop)) + if !storage_only && !this_scope_only { + for dropdata in &mut self.drops { + if let DropKind::Value { ref mut cached_block } = dropdata.kind { + cached_block.invalidate(); } - DropKind::Storage => None } - }); - if let Some(cached_block) = drops.next() { - Some(cached_block.expect("drop cache is not filled")) - } else { - None } } @@ -267,7 +270,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn in_breakable_scope(&mut self, loop_block: Option, break_block: BasicBlock, - break_destination: Lvalue<'tcx>, + break_destination: Place<'tcx>, f: F) -> R where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> R { @@ -356,7 +359,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { needs_cleanup: false, drops: vec![], cached_generator_drop: None, - cached_exits: FxHashMap() + cached_exits: FxHashMap(), + cached_unwind: CachedBlock::default(), }); } @@ -379,7 +383,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert_eq!(scope.region_scope, region_scope.0); self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope); + let resume_block = self.resume_block(); unpack!(block = build_scope_drops(&mut self.cfg, + resume_block, &scope, &self.scopes, block, @@ -418,6 +424,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } { + let resume_block = self.resume_block(); let mut rest = &mut self.scopes[(len - scope_count)..]; while let Some((scope, rest_)) = {rest}.split_last_mut() { rest = rest_; @@ -437,6 +444,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope); unpack!(block = build_scope_drops(&mut self.cfg, + resume_block, scope, rest, block, @@ -464,6 +472,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let src_info = self.scopes[0].source_info(self.fn_span); let mut block = self.cfg.start_new_block(); let result = block; + let resume_block = self.resume_block(); let mut rest = &mut self.scopes[..]; while let Some((scope, rest_)) = {rest}.split_last_mut() { @@ -482,15 +491,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::Goto { target: b }); b }; + + // End all regions for scopes out of which we are breaking. + self.cfg.push_end_region(self.hir.tcx(), block, src_info, scope.region_scope); + unpack!(block = build_scope_drops(&mut self.cfg, + resume_block, scope, rest, block, self.arg_count, true)); - - // End all regions for scopes out of which we are breaking. - self.cfg.push_end_region(self.hir.tcx(), block, src_info, scope.region_scope); } self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop); @@ -591,35 +602,32 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// When building statics/constants, returns `None` since /// intermediate values do not have to be dropped in that case. pub fn local_scope(&self) -> Option { - match self.hir.src { - MirSource::Const(_) | - MirSource::Static(..) => + match self.hir.body_owner_kind { + hir::BodyOwnerKind::Const | + hir::BodyOwnerKind::Static(_) => // No need to free storage in this context. None, - MirSource::Fn(_) => + hir::BodyOwnerKind::Fn => Some(self.topmost_scope()), - MirSource::Promoted(..) | - MirSource::GeneratorDrop(..) => - bug!(), } } // Scheduling drops // ================ - /// Indicates that `lvalue` should be dropped on exit from + /// Indicates that `place` should be dropped on exit from /// `region_scope`. pub fn schedule_drop(&mut self, span: Span, region_scope: region::Scope, - lvalue: &Lvalue<'tcx>, - lvalue_ty: Ty<'tcx>) { - let needs_drop = self.hir.needs_drop(lvalue_ty); + place: &Place<'tcx>, + place_ty: Ty<'tcx>) { + let needs_drop = self.hir.needs_drop(place_ty); let drop_kind = if needs_drop { DropKind::Value { cached_block: CachedBlock::default() } } else { // Only temps and vars need their storage dead. - match *lvalue { - Lvalue::Local(index) if index.index() > self.arg_count => DropKind::Storage, + match *place { + Place::Local(index) if index.index() > self.arg_count => DropKind::Storage, _ => return } }; @@ -672,8 +680,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // invalidating caches of each scope visited. This way bare minimum of the // caches gets invalidated. i.e. if a new drop is added into the middle scope, the // cache of outer scpoe stays intact. - let invalidate_unwind = needs_drop && !this_scope; - scope.invalidate_cache(invalidate_unwind); + scope.invalidate_cache(!needs_drop, this_scope); if this_scope { if let DropKind::Value { .. } = drop_kind { scope.needs_cleanup = true; @@ -684,13 +691,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let scope_end = region_scope_span.with_lo(region_scope_span.hi()); scope.drops.push(DropData { span: scope_end, - location: lvalue.clone(), + location: place.clone(), kind: drop_kind }); return; } } - span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, lvalue); + span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, place); } // Other @@ -700,18 +707,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// This path terminates in Resume. Returns the start of the path. /// See module comment for more details. None indicates there’s no /// cleanup to do at this point. - pub fn diverge_cleanup(&mut self) -> Option { + pub fn diverge_cleanup(&mut self) -> BasicBlock { self.diverge_cleanup_gen(false) } - fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> Option { - if !self.scopes.iter().any(|scope| scope.needs_cleanup) { - return None; + fn resume_block(&mut self) -> BasicBlock { + if let Some(target) = self.cached_resume_block { + target + } else { + let resumeblk = self.cfg.start_new_cleanup_block(); + self.cfg.terminate(resumeblk, + SourceInfo { + scope: ARGUMENT_VISIBILITY_SCOPE, + span: self.fn_span + }, + TerminatorKind::Resume); + self.cached_resume_block = Some(resumeblk); + resumeblk } - assert!(!self.scopes.is_empty()); // or `any` above would be false + } + + fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { + // To start, create the resume terminator. + let mut target = self.resume_block(); - let Builder { ref mut cfg, ref mut scopes, - ref mut cached_resume_block, .. } = *self; + let Builder { ref mut cfg, ref mut scopes, .. } = *self; // Build up the drops in **reverse** order. The end result will // look like: @@ -724,30 +744,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // store caches. If everything is cached, we'll just walk right // to left reading the cached results but never created anything. - // To start, create the resume terminator. - let mut target = if let Some(target) = *cached_resume_block { - target - } else { - let resumeblk = cfg.start_new_cleanup_block(); - cfg.terminate(resumeblk, - scopes[0].source_info(self.fn_span), - TerminatorKind::Resume); - *cached_resume_block = Some(resumeblk); - resumeblk - }; - - for scope in scopes.iter_mut() { - target = build_diverge_scope(self.hir.tcx(), cfg, scope.region_scope_span, - scope, target, generator_drop); + if scopes.iter().any(|scope| scope.needs_cleanup) { + for scope in scopes.iter_mut() { + target = build_diverge_scope(self.hir.tcx(), cfg, scope.region_scope_span, + scope, target, generator_drop); + } } - Some(target) + + target } /// Utility function for *non*-scope code to build their own drops pub fn build_drop(&mut self, block: BasicBlock, span: Span, - location: Lvalue<'tcx>, + location: Place<'tcx>, ty: Ty<'tcx>) -> BlockAnd<()> { if !self.hir.needs_drop(ty) { return block.unit(); @@ -759,7 +770,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::Drop { location, target: next_target, - unwind: diverge_target, + unwind: Some(diverge_target), }); next_target.unit() } @@ -768,7 +779,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn build_drop_and_replace(&mut self, block: BasicBlock, span: Span, - location: Lvalue<'tcx>, + location: Place<'tcx>, value: Operand<'tcx>) -> BlockAnd<()> { let source_info = self.source_info(span); let next_target = self.cfg.start_new_block(); @@ -778,7 +789,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { location, value, target: next_target, - unwind: diverge_target, + unwind: Some(diverge_target), }); next_target.unit() } @@ -803,7 +814,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { expected, msg, target: success_block, - cleanup, + cleanup: Some(cleanup), }); success_block @@ -812,6 +823,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Builds drops for pop_scope and exit_scope. fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, + resume_block: BasicBlock, scope: &Scope<'tcx>, earlier_scopes: &[Scope<'tcx>], mut block: BasicBlock, @@ -819,35 +831,55 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, generator_drop: bool) -> BlockAnd<()> { debug!("build_scope_drops({:?} -> {:?})", block, scope); - let mut iter = scope.drops.iter().rev().peekable(); + let mut iter = scope.drops.iter().rev(); while let Some(drop_data) = iter.next() { let source_info = scope.source_info(drop_data.span); match drop_data.kind { DropKind::Value { .. } => { - // Try to find the next block with its cached block - // for us to diverge into in case the drop panics. - let on_diverge = iter.peek().iter().filter_map(|dd| { + // Try to find the next block with its cached block for us to + // diverge into, either a previous block in this current scope or + // the top of the previous scope. + // + // If it wasn't for EndRegion, we could just chain all the DropData + // together and pick the first DropKind::Value. Please do that + // when we replace EndRegion with NLL. + let on_diverge = iter.clone().filter_map(|dd| { match dd.kind { - DropKind::Value { cached_block } => { - let result = cached_block.get(generator_drop); - if result.is_none() { - span_bug!(drop_data.span, "cached block not present?") - } - result - }, + DropKind::Value { cached_block } => Some(cached_block), DropKind::Storage => None } - }).next(); - // If there’s no `cached_block`s within current scope, - // we must look for one in the enclosing scope. - let on_diverge = on_diverge.or_else(|| { - earlier_scopes.iter().rev().flat_map(|s| s.cached_block(generator_drop)).next() + }).next().or_else(|| { + if earlier_scopes.iter().any(|scope| scope.needs_cleanup) { + // If *any* scope requires cleanup code to be run, + // we must use the cached unwind from the *topmost* + // scope, to ensure all EndRegions from surrounding + // scopes are executed before the drop code runs. + Some(earlier_scopes.last().unwrap().cached_unwind) + } else { + // We don't need any further cleanup, so return None + // to avoid creating a landing pad. We can skip + // EndRegions because all local regions end anyway + // when the function unwinds. + // + // This is an important optimization because LLVM is + // terrible at optimizing landing pads. FIXME: I think + // it would be cleaner and better to do this optimization + // in SimplifyCfg instead of here. + None + } + }); + + let on_diverge = on_diverge.map(|cached_block| { + cached_block.get(generator_drop).unwrap_or_else(|| { + span_bug!(drop_data.span, "cached block not present?") + }) }); + let next = cfg.start_new_block(); cfg.terminate(block, source_info, TerminatorKind::Drop { location: drop_data.location.clone(), target: next, - unwind: on_diverge + unwind: Some(on_diverge.unwrap_or(resume_block)) }); block = next; } @@ -862,7 +894,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, // Drop the storage for both value and storage drops. // Only temps and vars need their storage dead. match drop_data.location { - Lvalue::Local(index) if index.index() > arg_count => { + Place::Local(index) if index.index() > arg_count => { cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(index) @@ -933,14 +965,23 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, }; } - // Finally, push the EndRegion block, used by mir-borrowck. (Block - // becomes trivial goto after pass that removes all EndRegions.) - { - let block = cfg.start_new_cleanup_block(); - cfg.push_end_region(tcx, block, source_info(span), scope.region_scope); - cfg.terminate(block, source_info(span), TerminatorKind::Goto { target: target }); - target = block - } + // Finally, push the EndRegion block, used by mir-borrowck, and set + // `cached_unwind` to point to it (Block becomes trivial goto after + // pass that removes all EndRegions). + target = { + let cached_block = scope.cached_unwind.ref_mut(generator_drop); + if let Some(cached_block) = *cached_block { + cached_block + } else { + let block = cfg.start_new_cleanup_block(); + cfg.push_end_region(tcx, block, source_info(span), scope.region_scope); + cfg.terminate(block, source_info(span), TerminatorKind::Goto { target: target }); + *cached_block = Some(block); + block + } + }; + + debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); target } diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index bd41bce67da8e..1cbe0dcc017f9 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -8,26 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use syntax_pos::DUMMY_SP; - use rustc::mir::{self, Mir, Location}; use rustc::ty::{self, TyCtxt}; use util::elaborate_drops::DropFlagState; use super::{MoveDataParamEnv}; use super::indexes::MovePathIndex; -use super::move_paths::{MoveData, LookupResult}; +use super::move_paths::{MoveData, LookupResult, InitKind}; pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, path: MovePathIndex, mut cond: F) -> Option - where F: FnMut(&mir::LvalueProjection<'tcx>) -> bool + where F: FnMut(&mir::PlaceProjection<'tcx>) -> bool { let mut next_child = move_data.move_paths[path].first_child; while let Some(child_index) = next_child { - match move_data.move_paths[child_index].lvalue { - mir::Lvalue::Projection(ref proj) => { + match move_data.move_paths[child_index].place { + mir::Place::Projection(ref proj) => { if cond(proj) { return Some(child_index) } @@ -44,12 +42,12 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, /// paths (1.) past arrays, slices, and pointers, nor (2.) into a type /// that implements `Drop`. /// -/// Lvalues behind references or arrays are not tracked by elaboration +/// Places behind references or arrays are not tracked by elaboration /// and are always assumed to be initialized when accessible. As /// references and indexes can be reseated, trying to track them can /// only lead to trouble. /// -/// Lvalues behind ADT's with a Drop impl are not tracked by +/// Places behind ADT's with a Drop impl are not tracked by /// elaboration since they can never have a drop-flag state that /// differs from that of the parent with the Drop impl. /// @@ -58,19 +56,24 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>, /// is no need to maintain separate drop flags to track such state. /// /// FIXME: we have to do something for moving slice patterns. -fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &Mir<'tcx>, - lv: &mir::Lvalue<'tcx>) -> bool { - let ty = lv.ty(mir, tcx).to_ty(tcx); +fn place_contents_drop_state_cannot_differ<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + place: &mir::Place<'tcx>) -> bool { + let ty = place.ty(mir, tcx).to_ty(tcx); match ty.sty { - ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { - debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => true", - lv, ty); + ty::TyArray(..) => { + debug!("place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false", + place, ty); + false + } + ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { + debug!("place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true", + place, ty); true } ty::TyAdt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => { - debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true", - lv, ty); + debug!("place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true", + place, ty); true } _ => { @@ -79,8 +82,8 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx } } -pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_lookup_result_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, lookup_result: LookupResult, @@ -97,26 +100,26 @@ pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>( } } -pub(crate) fn on_all_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_all_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, move_path_index: MovePathIndex, mut each_child: F) where F: FnMut(MovePathIndex) { - fn is_terminal_path<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn is_terminal_path<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, path: MovePathIndex) -> bool { - lvalue_contents_drop_state_cannot_differ( - tcx, mir, &move_data.move_paths[path].lvalue) + place_contents_drop_state_cannot_differ( + tcx, mir, &move_data.move_paths[path].place) } - fn on_all_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, + fn on_all_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, move_data: &MoveData<'tcx>, move_path_index: MovePathIndex, @@ -138,20 +141,22 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>( on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child); } -pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, path: MovePathIndex, mut each_child: F) where F: FnMut(MovePathIndex) { on_all_children_bits(tcx, mir, &ctxt.move_data, path, |child| { - let lvalue = &ctxt.move_data.move_paths[path].lvalue; - let ty = lvalue.ty(mir, tcx).to_ty(tcx); - debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty); + let place = &ctxt.move_data.move_paths[path].place; + let ty = place.ty(mir, tcx).to_ty(tcx); + debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty); - if ty.needs_drop(tcx, ctxt.param_env) { + let gcx = tcx.global_tcx(); + let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap(); + if erased_ty.needs_drop(gcx, ctxt.param_env) { each_child(child); } else { debug!("on_all_drop_children_bits - skipping") @@ -159,33 +164,32 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>( }) } -pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) { let move_data = &ctxt.move_data; for arg in mir.args_iter() { - let lvalue = mir::Lvalue::Local(arg); - let lookup_result = move_data.rev_lookup.find(&lvalue); + let place = mir::Place::Local(arg); + let lookup_result = move_data.rev_lookup.find(&place); on_lookup_result_bits(tcx, mir, move_data, lookup_result, |mpi| callback(mpi, DropFlagState::Present)); } } -pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, + ctxt: &MoveDataParamEnv<'gcx, 'tcx>, loc: Location, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) { let move_data = &ctxt.move_data; - let param_env = ctxt.param_env; debug!("drop_flag_effects_for_location({:?})", loc); // first, move out of the RHS @@ -193,59 +197,45 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>( let path = mi.move_path_index(move_data); debug!("moving out of path {:?}", move_data.move_paths[path]); - // don't move out of non-Copy things - let lvalue = &move_data.move_paths[path].lvalue; - let ty = lvalue.ty(mir, tcx).to_ty(tcx); - if !ty.moves_by_default(tcx, param_env, DUMMY_SP) { - continue; - } - on_all_children_bits(tcx, mir, move_data, path, |mpi| callback(mpi, DropFlagState::Absent)) } - let block = &mir[loc.block]; - match block.statements.get(loc.statement_index) { - Some(stmt) => match stmt.kind { - mir::StatementKind::SetDiscriminant{ .. } => { - span_bug!(stmt.source_info.span, "SetDiscrimant should not exist during borrowck"); - } - mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - match rvalue.initialization_state() { - mir::tcx::RvalueInitializationState::Shallow => { - debug!("drop_flag_effects: box assignment {:?}", stmt); - if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(lvalue) { - callback(mpi, DropFlagState::Present); - } - } - mir::tcx::RvalueInitializationState::Deep => { - debug!("drop_flag_effects: assignment {:?}", stmt); - on_lookup_result_bits(tcx, mir, move_data, - move_data.rev_lookup.find(lvalue), - |mpi| callback(mpi, DropFlagState::Present)) - } - } - } - mir::StatementKind::StorageLive(_) | - mir::StatementKind::StorageDead(_) | - mir::StatementKind::InlineAsm { .. } | - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Validate(..) | - mir::StatementKind::Nop => {} - }, - None => { - debug!("drop_flag_effects: replace {:?}", block.terminator()); - match block.terminator().kind { - mir::TerminatorKind::DropAndReplace { ref location, .. } => { - on_lookup_result_bits(tcx, mir, move_data, - move_data.rev_lookup.find(location), - |mpi| callback(mpi, DropFlagState::Present)) - } - _ => { - // other terminators do not contain move-ins - } + debug!("drop_flag_effects: assignment for location({:?})", loc); + + for_location_inits( + tcx, + mir, + move_data, + loc, + |mpi| callback(mpi, DropFlagState::Present) + ); +} + +pub(crate) fn for_location_inits<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + loc: Location, + mut callback: F) + where F: FnMut(MovePathIndex) +{ + for ii in &move_data.init_loc_map[loc] { + let init = move_data.inits[*ii]; + match init.kind { + InitKind::Deep => { + let path = init.path; + + on_all_children_bits(tcx, mir, move_data, + path, + &mut callback) + }, + InitKind::Shallow => { + let mpi = init.path; + callback(mpi); } + InitKind::NonPanicPathOnly => (), } } } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 3f815ec83e3a5..2bba3e263f3c6 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -10,7 +10,8 @@ use rustc::mir::{self, Location, Mir}; use rustc::mir::visit::Visitor; -use rustc::ty::{Region, TyCtxt}; +use rustc::ty::{self, Region, TyCtxt}; +use rustc::ty::RegionKind; use rustc::ty::RegionKind::ReScope; use rustc::util::nodemap::{FxHashMap, FxHashSet}; @@ -20,22 +21,28 @@ use rustc_data_structures::indexed_vec::{IndexVec}; use dataflow::{BitDenotation, BlockSets, DataflowOperator}; pub use dataflow::indexes::BorrowIndex; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::ToRegionVid; + +use syntax_pos::Span; use std::fmt; // `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be // uniquely identified in the MIR by the `Location` of the assigment // statement in which it appears on the right hand side. -pub struct Borrows<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, borrows: IndexVec>, location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, + region_span_map: FxHashMap, + nonlexical_regioncx: Option>, } // temporarily allow some dead fields: `kind` and `region` will be -// needed by borrowck; `lvalue` will probably be a MovePathIndex when +// needed by borrowck; `place` will probably be a MovePathIndex when // that is extended to include borrowed data paths. #[allow(dead_code)] #[derive(Debug)] @@ -43,7 +50,7 @@ pub struct BorrowData<'tcx> { pub(crate) location: Location, pub(crate) kind: mir::BorrowKind, pub(crate) region: Region<'tcx>, - pub(crate) lvalue: mir::Lvalue<'tcx>, + pub(crate) place: mir::Place<'tcx>, } impl<'tcx> fmt::Display for BorrowData<'tcx> { @@ -55,34 +62,50 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { }; let region = format!("{}", self.region); let region = if region.len() > 0 { format!("{} ", region) } else { region }; - write!(w, "&{}{}{:?}", region, kind, self.lvalue) + write!(w, "&{}{}{:?}", region, kind, self.place) } } -impl<'a, 'tcx> Borrows<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { - let mut visitor = GatherBorrows { idx_vec: IndexVec::new(), - location_map: FxHashMap(), - region_map: FxHashMap(), }; +impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + nonlexical_regioncx: Option>) + -> Self { + let mut visitor = GatherBorrows { + tcx, + mir, + idx_vec: IndexVec::new(), + location_map: FxHashMap(), + region_map: FxHashMap(), + region_span_map: FxHashMap() + }; visitor.visit_mir(mir); return Borrows { tcx: tcx, mir: mir, borrows: visitor.idx_vec, location_map: visitor.location_map, - region_map: visitor.region_map, }; + region_map: visitor.region_map, + region_span_map: visitor.region_span_map, + nonlexical_regioncx }; - struct GatherBorrows<'tcx> { + struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, idx_vec: IndexVec>, location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, + region_span_map: FxHashMap, } - impl<'tcx> Visitor<'tcx> for GatherBorrows<'tcx> { + + impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) { - if let mir::Rvalue::Ref(region, kind, ref lvalue) = *rvalue { + if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue { + if is_unsafe_place(self.tcx, self.mir, place) { return; } + let borrow = BorrowData { - location: location, kind: kind, region: region, lvalue: lvalue.clone(), + location: location, kind: kind, region: region, place: place.clone(), }; let idx = self.idx_vec.push(borrow); self.location_map.insert(location, idx); @@ -90,6 +113,16 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { borrows.insert(idx); } } + + fn visit_statement(&mut self, + block: mir::BasicBlock, + statement: &mir::Statement<'tcx>, + location: Location) { + if let mir::StatementKind::EndRegion(region_scope) = statement.kind { + self.region_span_map.insert(ReScope(region_scope), statement.source_info.span); + } + self.super_statement(block, statement, location); + } } } @@ -98,9 +131,51 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { pub fn location(&self, idx: BorrowIndex) -> &Location { &self.borrows[idx].location } + + /// Returns the span for the "end point" given region. This will + /// return `None` if NLL is enabled, since that concept has no + /// meaning there. Otherwise, return region span if it exists and + /// span for end of the function if it doesn't exist. + pub fn opt_region_end_span(&self, region: &Region) -> Option { + match self.nonlexical_regioncx { + Some(_) => None, + None => { + match self.region_span_map.get(region) { + Some(span) => Some(span.end_point()), + None => Some(self.mir.span.end_point()) + } + } + } + } + + /// Add all borrows to the kill set, if those borrows are out of scope at `location`. + fn kill_loans_out_of_scope_at_location(&self, + sets: &mut BlockSets, + location: Location) { + if let Some(ref regioncx) = self.nonlexical_regioncx { + for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { + let borrow_region = borrow_data.region.to_region_vid(); + if !regioncx.region_contains_point(borrow_region, location) { + // The region checker really considers the borrow + // to start at the point **after** the location of + // the borrow, but the borrow checker puts the gen + // directly **on** the location of the + // borrow. This results in a gen/kill both being + // generated for same point if we are not + // careful. Probably we should change the point of + // the gen, but for now we hackily account for the + // mismatch here by not generating a kill for the + // location on the borrow itself. + if location != borrow_data.location { + sets.kill(&borrow_index); + } + } + } + } + } } -impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { type Idx = BorrowIndex; fn name() -> &'static str { "borrows" } fn bits_per_block(&self) -> usize { @@ -122,15 +197,23 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> { }); match stmt.kind { mir::StatementKind::EndRegion(region_scope) => { - let borrow_indexes = self.region_map.get(&ReScope(region_scope)).unwrap_or_else(|| { - panic!("could not find BorrowIndexs for region scope {:?}", region_scope); - }); - - for idx in borrow_indexes { sets.kill(&idx); } + if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) { + assert!(self.nonlexical_regioncx.is_none()); + for idx in borrow_indexes { sets.kill(&idx); } + } else { + // (if there is no entry, then there are no borrows to be tracked) + } } mir::StatementKind::Assign(_, ref rhs) => { - if let mir::Rvalue::Ref(region, _, _) = *rhs { + if let mir::Rvalue::Ref(region, _, ref place) = *rhs { + if is_unsafe_place(self.tcx, self.mir, place) { return; } + if let RegionKind::ReEmpty = region { + // If the borrowed value is dead, the region for it + // can be empty. Don't track the borrow in that case. + return + } + let index = self.location_map.get(&location).unwrap_or_else(|| { panic!("could not find BorrowIndex for location {:?}", location); }); @@ -149,32 +232,96 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> { mir::StatementKind::Nop => {} } + + self.kill_loans_out_of_scope_at_location(sets, location); } + fn terminator_effect(&self, - _sets: &mut BlockSets, - _location: Location) { - // no terminators start nor end region scopes. + sets: &mut BlockSets, + location: Location) { + let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| { + panic!("could not find block at location {:?}", location); + }); + match block.terminator().kind { + mir::TerminatorKind::Resume | + mir::TerminatorKind::Return | + mir::TerminatorKind::GeneratorDrop => { + // When we return from the function, then all `ReScope`-style regions + // are guaranteed to have ended. + // Normally, there would be `EndRegion` statements that come before, + // and hence most of these loans will already be dead -- but, in some cases + // like unwind paths, we do not always emit `EndRegion` statements, so we + // add some kills here as a "backup" and to avoid spurious error messages. + for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { + if let ReScope(..) = borrow_data.region { + sets.kill(&borrow_index); + } + } + } + mir::TerminatorKind::SwitchInt {..} | + mir::TerminatorKind::Drop {..} | + mir::TerminatorKind::DropAndReplace {..} | + mir::TerminatorKind::Call {..} | + mir::TerminatorKind::Assert {..} | + mir::TerminatorKind::Yield {..} | + mir::TerminatorKind::Goto {..} | + mir::TerminatorKind::FalseEdges {..} | + mir::TerminatorKind::Unreachable => {} + } + self.kill_loans_out_of_scope_at_location(sets, location); } fn propagate_call_return(&self, _in_out: &mut IdxSet, _call_bb: mir::BasicBlock, _dest_bb: mir::BasicBlock, - _dest_lval: &mir::Lvalue) { + _dest_place: &mir::Place) { // there are no effects on the region scopes from method calls. } } -impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // union effects of preds when computing borrows } } -impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for Borrows<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = no Rvalue::Refs are active by default } } + +fn is_unsafe_place<'a, 'gcx: 'tcx, 'tcx: 'a>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + place: &mir::Place<'tcx> +) -> bool { + use self::mir::Place::*; + use self::mir::ProjectionElem; + + match *place { + Local(_) => false, + Static(ref static_) => tcx.is_static_mut(static_.def_id), + Projection(ref proj) => { + match proj.elem { + ProjectionElem::Field(..) | + ProjectionElem::Downcast(..) | + ProjectionElem::Subslice { .. } | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Index(_) => { + is_unsafe_place(tcx, mir, &proj.base) + } + ProjectionElem::Deref => { + let ty = proj.base.ty(mir, tcx).to_ty(tcx); + match ty.sty { + ty::TyRawPtr(..) => true, + _ => is_unsafe_place(tcx, mir, &proj.base), + } + } + } + } + } +} diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 19a595622b92e..50c8df3c2e3d0 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -14,18 +14,21 @@ use rustc::ty::TyCtxt; use rustc::mir::{self, Mir, Location}; +use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. use rustc_data_structures::bitslice::{BitwiseOperator}; use rustc_data_structures::indexed_set::{IdxSet}; +use rustc_data_structures::indexed_vec::Idx; use super::MoveDataParamEnv; use util::elaborate_drops::DropFlagState; -use super::move_paths::{HasMoveData, MoveData, MovePathIndex}; +use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex, InitIndex}; +use super::move_paths::{LookupResult, InitKind}; use super::{BitDenotation, BlockSets, DataflowOperator}; use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_location; -use super::on_lookup_result_bits; +use super::{on_lookup_result_bits, for_location_inits}; mod storage_liveness; @@ -69,23 +72,23 @@ pub(super) mod borrows; /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } @@ -124,23 +127,23 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> { /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } @@ -185,27 +188,111 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> { /// Similarly, at a given `drop` statement, the set-difference between /// this data and `MaybeInitializedLvals` yields the set of l-values /// that would require a dynamic drop-flag at that statement. -pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, } -impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, +impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>) + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) -> Self { DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } } } -impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } -impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { +/// `MovingOutStatements` tracks the statements that perform moves out +/// of particular l-values. More precisely, it tracks whether the +/// *effect* of such moves (namely, the uninitialization of the +/// l-value in question) can reach some point in the control-flow of +/// the function, or if that effect is "killed" by some intervening +/// operation reinitializing that l-value. +/// +/// The resulting dataflow is a more enriched version of +/// `MaybeUninitializedLvals`. Both structures on their own only tell +/// you if an l-value *might* be uninitialized at a given point in the +/// control flow. But `MovingOutStatements` also includes the added +/// data of *which* particular statement causing the deinitialization +/// that the borrow checker's error message may need to report. +#[allow(dead_code)] +pub struct MovingOutStatements<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> MovingOutStatements<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) + -> Self + { + MovingOutStatements { tcx: tcx, mir: mir, mdpe: mdpe } + } +} + +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } +} + +/// `EverInitializedLvals` tracks all l-values that might have ever been +/// initialized upon reaching a particular point in the control flow +/// for a function, without an intervening `Storage Dead`. +/// +/// This dataflow is used to determine if an immutable local variable may +/// be assigned to. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // ever-init: +/// // { } +/// let a = S; let b = S; let c; let d; // {a, b } +/// +/// if pred { +/// drop(a); // {a, b, } +/// b = S; // {a, b, } +/// +/// } else { +/// drop(b); // {a, b, } +/// d = S; // {a, b, d } +/// +/// } // {a, b, d } +/// +/// c = S; // {a, b, c, d } +/// } +/// ``` +pub struct EverInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedLvals<'a, 'gcx, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) + -> Self + { + EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } + } +} + +impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedLvals<'a, 'gcx, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } +} + + +impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -216,7 +303,7 @@ impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -227,7 +314,7 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: DropFlagState) { @@ -238,7 +325,7 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { } } -impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "maybe_init" } fn bits_per_block(&self) -> usize { @@ -281,23 +368,23 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { in_out: &mut IdxSet, _call_bb: mir::BasicBlock, _dest_bb: mir::BasicBlock, - dest_lval: &mir::Lvalue) { + dest_place: &mir::Place) { // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). + // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_lval), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.add(&mpi); }); } } -impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "maybe_uninit" } fn bits_per_block(&self) -> usize { self.move_data().move_paths.len() } - // sets on_entry bits for Arg lvalues + // sets on_entry bits for Arg places fn start_block_effect(&self, sets: &mut BlockSets) { // set all bits to 1 (uninit) before gathering counterevidence for e in sets.on_entry.words_mut() { *e = !0; } @@ -336,23 +423,23 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { in_out: &mut IdxSet, _call_bb: mir::BasicBlock, _dest_bb: mir::BasicBlock, - dest_lval: &mir::Lvalue) { + dest_place: &mir::Place) { // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 0 (initialized). + // the bits for that dest_place to 0 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_lval), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.remove(&mpi); }); } } -impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { type Idx = MovePathIndex; fn name() -> &'static str { "definite_init" } fn bits_per_block(&self) -> usize { self.move_data().move_paths.len() } - // sets on_entry bits for Arg lvalues + // sets on_entry bits for Arg places fn start_block_effect(&self, sets: &mut BlockSets) { for e in sets.on_entry.words_mut() { *e = 0; } @@ -390,36 +477,232 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { in_out: &mut IdxSet, _call_bb: mir::BasicBlock, _dest_bb: mir::BasicBlock, - dest_lval: &mir::Lvalue) { + dest_place: &mir::Place) { // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). + // the bits for that dest_place to 1 (initialized). on_lookup_result_bits(self.tcx, self.mir, self.move_data(), - self.move_data().rev_lookup.find(dest_lval), + self.move_data().rev_lookup.find(dest_place), |mpi| { in_out.add(&mpi); }); } } -impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> { + type Idx = MoveOutIndex; + fn name() -> &'static str { "moving_out" } + fn bits_per_block(&self) -> usize { + self.move_data().moves.len() + } + + fn start_block_effect(&self, _sets: &mut BlockSets) { + // no move-statements have been executed prior to function + // execution, so this method has no effect on `_sets`. + } + fn statement_effect(&self, + sets: &mut BlockSets, + location: Location) { + let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data()); + let stmt = &mir[location.block].statements[location.statement_index]; + let loc_map = &move_data.loc_map; + let path_map = &move_data.path_map; + let bits_per_block = self.bits_per_block(); + + match stmt.kind { + // this analysis only tries to find moves explicitly + // written by the user, so we ignore the move-outs + // created by `StorageDead` and at the beginning + // of a function. + mir::StatementKind::StorageDead(_) => {} + _ => { + debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", + stmt, location, &loc_map[location]); + for move_index in &loc_map[location] { + // Every path deinitialized by a *particular move* + // has corresponding bit, "gen'ed" (i.e. set) + // here, in dataflow vector + zero_to_one(sets.gen_set.words_mut(), *move_index); + } + } + } + + for_location_inits(tcx, mir, move_data, location, + |mpi| for moi in &path_map[mpi] { + assert!(moi.index() < bits_per_block); + sets.kill_set.add(&moi); + } + ); + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + location: Location) + { + let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data()); + let term = mir[location.block].terminator(); + let loc_map = &move_data.loc_map; + let path_map = &move_data.path_map; + + debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}", + term, location, &loc_map[location]); + let bits_per_block = self.bits_per_block(); + for move_index in &loc_map[location] { + assert!(move_index.index() < bits_per_block); + zero_to_one(sets.gen_set.words_mut(), *move_index); + } + + for_location_inits(tcx, mir, move_data, location, + |mpi| for moi in &path_map[mpi] { + assert!(moi.index() < bits_per_block); + sets.kill_set.add(&moi); + } + ); + } + + fn propagate_call_return(&self, + in_out: &mut IdxSet, + _call_bb: mir::BasicBlock, + _dest_bb: mir::BasicBlock, + dest_place: &mir::Place) { + let move_data = self.move_data(); + let bits_per_block = self.bits_per_block(); + + let path_map = &move_data.path_map; + on_lookup_result_bits(self.tcx, + self.mir, + move_data, + move_data.rev_lookup.find(dest_place), + |mpi| for moi in &path_map[mpi] { + assert!(moi.index() < bits_per_block); + in_out.remove(&moi); + }); + } +} + +impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> { + type Idx = InitIndex; + fn name() -> &'static str { "ever_init" } + fn bits_per_block(&self) -> usize { + self.move_data().inits.len() + } + + fn start_block_effect(&self, sets: &mut BlockSets) { + let bits_per_block = self.bits_per_block(); + for init_index in (0..self.mir.arg_count).map(InitIndex::new) { + assert!(init_index.index() < bits_per_block); + sets.gen_set.add(&init_index); + } + } + fn statement_effect(&self, + sets: &mut BlockSets, + location: Location) { + let (_, mir, move_data) = (self.tcx, self.mir, self.move_data()); + let stmt = &mir[location.block].statements[location.statement_index]; + let init_path_map = &move_data.init_path_map; + let init_loc_map = &move_data.init_loc_map; + let rev_lookup = &move_data.rev_lookup; + let bits_per_block = self.bits_per_block(); + + debug!("statement {:?} at loc {:?} initializes move_indexes {:?}", + stmt, location, &init_loc_map[location]); + for init_index in &init_loc_map[location] { + assert!(init_index.index() < bits_per_block); + sets.gen_set.add(init_index); + } + + match stmt.kind { + mir::StatementKind::StorageDead(local) => { + // End inits for StorageDead, so that an immutable variable can + // be reinitialized on the next iteration of the loop. + if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) { + debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}", + stmt, location, &init_path_map[mpi]); + for ii in &init_path_map[mpi] { + assert!(ii.index() < bits_per_block); + sets.kill_set.add(&ii); + } + } + } + _ => {} + } + } + + fn terminator_effect(&self, + sets: &mut BlockSets, + location: Location) + { + let (mir, move_data) = (self.mir, self.move_data()); + let term = mir[location.block].terminator(); + let init_loc_map = &move_data.init_loc_map; + debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}", + term, location, &init_loc_map[location]); + let bits_per_block = self.bits_per_block(); + for init_index in &init_loc_map[location] { + if move_data.inits[*init_index].kind != InitKind::NonPanicPathOnly { + assert!(init_index.index() < bits_per_block); + sets.gen_set.add(init_index); + } + } + } + + fn propagate_call_return(&self, + in_out: &mut IdxSet, + call_bb: mir::BasicBlock, + _dest_bb: mir::BasicBlock, + _dest_place: &mir::Place) { + let move_data = self.move_data(); + let bits_per_block = self.bits_per_block(); + let init_loc_map = &move_data.init_loc_map; + + let call_loc = Location { + block: call_bb, + statement_index: self.mir[call_bb].statements.len(), + }; + for init_index in &init_loc_map[call_loc] { + assert!(init_index.index() < bits_per_block); + in_out.add(init_index); + } + } +} + +fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) { + let retval = bitvec.set_bit(move_index.index()); + assert!(retval); +} + +impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // "maybe" means we union effects of both preds } } -impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 | pred2 // "maybe" means we union effects of both preds } } -impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn join(&self, pred1: usize, pred2: usize) -> usize { pred1 & pred2 // "definitely" means we intersect effects of both preds } } +impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // moves from both preds are in scope + } +} + +impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // inits from both preds are in scope + } +} + // The way that dataflow fixed point iteration works, you want to // start at bottom and work your way to a fixed point. Control-flow // merges will apply the `join` operator to each block entry's current @@ -430,23 +713,37 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { // propagating, or you start at all-ones and then use Intersect as // your merge when propagating. -impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = uninitialized } } -impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { false // bottom = initialized (start_block_effect counters this at outset) } } -impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { #[inline] fn bottom_value() -> bool { true // bottom = initialized (start_block_effect counters this at outset) } } + +impl<'a, 'gcx, 'tcx> DataflowOperator for MovingOutStatements<'a, 'gcx, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = no loans in scope by default + } +} + +impl<'a, 'gcx, 'tcx> DataflowOperator for EverInitializedLvals<'a, 'gcx, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = no initialized variables by default + } +} diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs index 98615c6b26826..fe6cd660b1e83 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> BitDenotation for MaybeStorageLive<'a, 'tcx> { _in_out: &mut IdxSet, _call_bb: mir::BasicBlock, _dest_bb: mir::BasicBlock, - _dest_lval: &mir::Lvalue) { + _dest_place: &mir::Place) { // Nothing to do when a call returns successfully } } diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 9fa5691d647b7..6be006b1ea972 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -26,7 +26,8 @@ use std::usize; pub use self::impls::{MaybeStorageLive}; pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; -pub use self::impls::{DefinitelyInitializedLvals}; +pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements}; +pub use self::impls::EverInitializedLvals; pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex}; pub(crate) use self::drop_flag_effects::*; @@ -91,19 +92,19 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option return None; } -pub struct MoveDataParamEnv<'tcx> { +pub struct MoveDataParamEnv<'gcx, 'tcx> { pub(crate) move_data: MoveData<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, + pub(crate) param_env: ty::ParamEnv<'gcx>, } -pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &Mir<'tcx>, - node_id: ast::NodeId, - attributes: &[ast::Attribute], - dead_unwinds: &IdxSet, - bd: BD, - p: P) - -> DataflowResults +pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + node_id: ast::NodeId, + attributes: &[ast::Attribute], + dead_unwinds: &IdxSet, + bd: BD, + p: P) + -> DataflowResults where BD: BitDenotation, P: Fn(&BD, BD::Idx) -> &fmt::Debug { @@ -609,12 +610,12 @@ pub trait BitDenotation: DataflowOperator { in_out: &mut IdxSet, call_bb: mir::BasicBlock, dest_bb: mir::BasicBlock, - dest_lval: &mir::Lvalue); + dest_place: &mir::Place); } -impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation +impl<'a, 'gcx, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation { - pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub fn new(_tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, dead_unwinds: &'a IdxSet, denotation: D) -> Self { @@ -713,14 +714,20 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } } - if let Some((ref dest_lval, ref dest_bb)) = *destination { + if let Some((ref dest_place, ref dest_bb)) = *destination { // N.B.: This must be done *last*, after all other // propagation, as documented in comment above. self.flow_state.operator.propagate_call_return( - in_out, bb, *dest_bb, dest_lval); + in_out, bb, *dest_bb, dest_place); self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb); } } + mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => { + self.propagate_bits_into_entry_set_for(in_out, changed, real_target); + for target in imaginary_targets { + self.propagate_bits_into_entry_set_for(in_out, changed, target); + } + } } } diff --git a/src/librustc_mir/dataflow/move_paths/abs_domain.rs b/src/librustc_mir/dataflow/move_paths/abs_domain.rs index 00825c7a880e9..4d20857bc2ec8 100644 --- a/src/librustc_mir/dataflow/move_paths/abs_domain.rs +++ b/src/librustc_mir/dataflow/move_paths/abs_domain.rs @@ -9,7 +9,7 @@ // except according to those terms. //! The move-analysis portion of borrowck needs to work in an abstract -//! domain of lifted Lvalues. Most of the Lvalue variants fall into a +//! domain of lifted Places. Most of the Place variants fall into a //! one-to-one mapping between the concrete and abstract (e.g. a //! field-deref on a local-variable, `x.field`, has the same meaning //! in both domains). Indexed-Projections are the exception: `a[x]` @@ -21,7 +21,7 @@ //! `a[x]` would still overlap them both. But that is not this //! representation does today.) -use rustc::mir::{Local, LvalueElem, Operand, ProjectionElem}; +use rustc::mir::{Local, PlaceElem, Operand, ProjectionElem}; use rustc::ty::Ty; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -47,7 +47,7 @@ impl<'tcx> Lift for Ty<'tcx> { type Abstract = AbstractType; fn lift(&self) -> Self::Abstract { AbstractType } } -impl<'tcx> Lift for LvalueElem<'tcx> { +impl<'tcx> Lift for PlaceElem<'tcx> { type Abstract = AbstractElem<'tcx>; fn lift(&self) -> Self::Abstract { match *self { diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 86298c3b83e29..c20beb7d8c2a7 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -14,65 +14,68 @@ use rustc::mir::tcx::RvalueInitializationState; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::{IndexVec}; -use syntax::codemap::DUMMY_SP; - use std::collections::hash_map::Entry; use std::mem; use super::abs_domain::Lift; use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex}; +use super::{MoveError, InitIndex, Init, LookupResult, InitKind}; +use super::IllegalMoveOriginKind::*; -pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> { +struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> { mir: &'a Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, data: MoveData<'tcx>, + errors: Vec>, } -pub enum MovePathError { - IllegalMove, - UnionMove { path: MovePathIndex }, -} - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn new(mir: &'a Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Self { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { + fn new(mir: &'a Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); + let mut init_path_map = IndexVec::new(); MoveDataBuilder { mir, tcx, - param_env, + errors: Vec::new(), data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(mir), rev_lookup: MovePathLookup { - locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| { - Self::new_move_path(&mut move_paths, &mut path_map, None, v) + locals: mir.local_decls.indices().map(Place::Local).map(|v| { + Self::new_move_path( + &mut move_paths, + &mut path_map, + &mut init_path_map, + None, + v, + ) }).collect(), projections: FxHashMap(), }, move_paths, path_map, + inits: IndexVec::new(), + init_loc_map: LocationMap::new(mir), + init_path_map, } } } fn new_move_path(move_paths: &mut IndexVec>, path_map: &mut IndexVec>, + init_path_map: &mut IndexVec>, parent: Option, - lvalue: Lvalue<'tcx>) + place: Place<'tcx>) -> MovePathIndex { let move_path = move_paths.push(MovePath { next_sibling: None, first_child: None, parent, - lvalue, + place, }); if let Some(parent) = parent { @@ -83,80 +86,106 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { let path_map_ent = path_map.push(vec![]); assert_eq!(path_map_ent, move_path); + + let init_path_map_ent = init_path_map.push(vec![]); + assert_eq!(init_path_map_ent, move_path); + move_path } +} - /// This creates a MovePath for a given lvalue, returning an `MovePathError` - /// if that lvalue can't be moved from. +impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { + /// This creates a MovePath for a given place, returning an `MovePathError` + /// if that place can't be moved from. /// - /// NOTE: lvalues behind references *do not* get a move path, which is + /// NOTE: places behind references *do not* get a move path, which is /// problematic for borrowck. /// /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, lval: &Lvalue<'tcx>) - -> Result + fn move_path_for(&mut self, place: &Place<'tcx>) + -> Result> { - debug!("lookup({:?})", lval); - match *lval { - Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]), - // error: can't move out of a static - Lvalue::Static(..) => Err(MovePathError::IllegalMove), - Lvalue::Projection(ref proj) => { - self.move_path_for_projection(lval, proj) + debug!("lookup({:?})", place); + match *place { + Place::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), + Place::Static(..) => { + let span = self.builder.mir.source_info(self.loc).span; + Err(MoveError::cannot_move_out_of(span, Static)) + } + Place::Projection(ref proj) => { + self.move_path_for_projection(place, proj) } } } - fn create_move_path(&mut self, lval: &Lvalue<'tcx>) { + fn create_move_path(&mut self, place: &Place<'tcx>) { // This is an assignment, not a move, so this not being a valid // move path is OK. - let _ = self.move_path_for(lval); + let _ = self.move_path_for(place); } fn move_path_for_projection(&mut self, - lval: &Lvalue<'tcx>, - proj: &LvalueProjection<'tcx>) - -> Result + place: &Place<'tcx>, + proj: &PlaceProjection<'tcx>) + -> Result> { let base = try!(self.move_path_for(&proj.base)); - let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - match lv_ty.sty { - // error: can't move out of borrowed content - ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove), - // error: can't move out of struct with destructor - ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => - return Err(MovePathError::IllegalMove), + let mir = self.builder.mir; + let tcx = self.builder.tcx; + let place_ty = proj.base.ty(mir, tcx).to_ty(tcx); + match place_ty.sty { + ty::TyRef(..) | ty::TyRawPtr(..) => + return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span, + BorrowedContent)), + ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => + return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span, + InteriorOfTypeWithDestructor { + container_ty: place_ty + })), // move out of union - always move the entire union ty::TyAdt(adt, _) if adt.is_union() => - return Err(MovePathError::UnionMove { path: base }), - // error: can't move out of a slice - ty::TySlice(..) => - return Err(MovePathError::IllegalMove), + return Err(MoveError::UnionMove { path: base }), + ty::TySlice(_) => + return Err(MoveError::cannot_move_out_of( + mir.source_info(self.loc).span, + InteriorOfSliceOrArray { + ty: place_ty, is_index: match proj.elem { + ProjectionElem::Index(..) => true, + _ => false + }, + })), ty::TyArray(..) => match proj.elem { - // error: can't move out of an array - ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove), + ProjectionElem::Index(..) => + return Err(MoveError::cannot_move_out_of( + mir.source_info(self.loc).span, + InteriorOfSliceOrArray { + ty: place_ty, is_index: true + })), _ => { // FIXME: still badly broken } }, _ => {} }; - match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) { + match self.builder.data.rev_lookup.projections.entry((base, proj.elem.lift())) { Entry::Occupied(ent) => Ok(*ent.get()), Entry::Vacant(ent) => { - let path = Self::new_move_path( - &mut self.data.move_paths, - &mut self.data.path_map, + let path = MoveDataBuilder::new_move_path( + &mut self.builder.data.move_paths, + &mut self.builder.data.path_map, + &mut self.builder.data.init_path_map, Some(base), - lval.clone() + place.clone() ); ent.insert(path); Ok(path) } } } +} - fn finalize(self) -> MoveData<'tcx> { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { + fn finalize(self) -> Result, (MoveData<'tcx>, Vec>)> { debug!("{}", { debug!("moves for {:?}:", self.mir.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -168,15 +197,21 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } "done dumping moves" }); - self.data + + if self.errors.len() > 0 { + Err((self.data, self.errors)) + } else { + Ok(self.data) + } } } -pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> MoveData<'tcx> { - let mut builder = MoveDataBuilder::new(mir, tcx, param_env); +pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> Result, + (MoveData<'tcx>, Vec>)> { + let mut builder = MoveDataBuilder::new(mir, tcx); + + builder.gather_args(); for (bb, block) in mir.basic_blocks().iter_enumerated() { for (i, stmt) in block.statements.iter().enumerate() { @@ -194,22 +229,59 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, builder.finalize() } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { + fn gather_args(&mut self) { + for arg in self.mir.args_iter() { + let path = self.data.rev_lookup.locals[arg]; + let span = self.mir.local_decls[arg].source_info.span; + + let init = self.data.inits.push(Init { + path, span, kind: InitKind::Deep + }); + + debug!("gather_args: adding init {:?} of {:?} for argument {:?}", + init, path, arg); + + self.data.init_path_map[path].push(init); + } + } + fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { debug!("gather_statement({:?}, {:?})", loc, stmt); + (Gatherer { builder: self, loc }).gather_statement(stmt); + } + + fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { + debug!("gather_terminator({:?}, {:?})", loc, term); + (Gatherer { builder: self, loc }).gather_terminator(term); + } +} + +struct Gatherer<'b, 'a: 'b, 'gcx: 'tcx, 'tcx: 'a> { + builder: &'b mut MoveDataBuilder<'a, 'gcx, 'tcx>, + loc: Location, +} + +impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { + fn gather_statement(&mut self, stmt: &Statement<'tcx>) { match stmt.kind { - StatementKind::Assign(ref lval, ref rval) => { - self.create_move_path(lval); + StatementKind::Assign(ref place, ref rval) => { + self.create_move_path(place); if let RvalueInitializationState::Shallow = rval.initialization_state() { // Box starts out uninitialized - need to create a separate // move-path for the interior so it will be separate from // the exterior. - self.create_move_path(&lval.clone().deref()); + self.create_move_path(&place.clone().deref()); + self.gather_init(place, InitKind::Shallow); + } else { + self.gather_init(place, InitKind::Deep); } - self.gather_rvalue(loc, rval); + self.gather_rvalue(rval); + } + StatementKind::StorageLive(_) => {} + StatementKind::StorageDead(local) => { + self.gather_move(&Place::Local(local)); } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) => {} StatementKind::SetDiscriminant{ .. } => { span_bug!(stmt.source_info.span, "SetDiscriminant should not exist during borrowck"); @@ -221,22 +293,22 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } - fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) { + fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { match *rvalue { Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) | Rvalue::UnaryOp(_, ref operand) => { - self.gather_operand(loc, operand) + self.gather_operand(operand) } Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) | Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { - self.gather_operand(loc, lhs); - self.gather_operand(loc, rhs); + self.gather_operand(lhs); + self.gather_operand(rhs); } Rvalue::Aggregate(ref _kind, ref operands) => { for operand in operands { - self.gather_operand(loc, operand); + self.gather_operand(operand); } } Rvalue::Ref(..) | @@ -246,7 +318,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { Rvalue::NullaryOp(NullOp::Box, _) => { // This returns an rvalue with uninitialized contents. We can't // move out of it here because it is an rvalue - assignments always - // completely initialize their lvalue. + // completely initialize their place. // // However, this does not matter - MIR building is careful to // only emit a shallow free for the partially-initialized @@ -258,16 +330,16 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } - fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { - debug!("gather_terminator({:?}, {:?})", loc, term); + fn gather_terminator(&mut self, term: &Terminator<'tcx>) { match term.kind { TerminatorKind::Goto { target: _ } | TerminatorKind::Resume | TerminatorKind::GeneratorDrop | + TerminatorKind::FalseEdges { .. } | TerminatorKind::Unreachable => { } TerminatorKind::Return => { - self.gather_move(loc, &Lvalue::Local(RETURN_POINTER)); + self.gather_move(&Place::Local(RETURN_PLACE)); } TerminatorKind::Assert { .. } | @@ -276,62 +348,74 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } TerminatorKind::Yield { ref value, .. } => { - self.gather_operand(loc, value); + self.gather_operand(value); } TerminatorKind::Drop { ref location, target: _, unwind: _ } => { - self.gather_move(loc, location); + self.gather_move(location); } TerminatorKind::DropAndReplace { ref location, ref value, .. } => { self.create_move_path(location); - self.gather_operand(loc, value); + self.gather_operand(value); + self.gather_init(location, InitKind::Deep); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.gather_operand(loc, func); + self.gather_operand(func); for arg in args { - self.gather_operand(loc, arg); + self.gather_operand(arg); } if let Some((ref destination, _bb)) = *destination { self.create_move_path(destination); + self.gather_init(destination, InitKind::NonPanicPathOnly); } } } } - fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) { + fn gather_operand(&mut self, operand: &Operand<'tcx>) { match *operand { - Operand::Constant(..) => {} // not-a-move - Operand::Consume(ref lval) => { // a move - self.gather_move(loc, lval); + Operand::Constant(..) | + Operand::Copy(..) => {} // not-a-move + Operand::Move(ref place) => { // a move + self.gather_move(place); } } } - fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) { - debug!("gather_move({:?}, {:?})", loc, lval); - - let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx); - if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) { - debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty); - return - } + fn gather_move(&mut self, place: &Place<'tcx>) { + debug!("gather_move({:?}, {:?})", self.loc, place); - let path = match self.move_path_for(lval) { - Ok(path) | Err(MovePathError::UnionMove { path }) => path, - Err(MovePathError::IllegalMove) => { - // Moving out of a bad path. Eventually, this should be a MIR - // borrowck error instead of a bug. - span_bug!(self.mir.span, - "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}", - lval, lv_ty, loc); + let path = match self.move_path_for(place) { + Ok(path) | Err(MoveError::UnionMove { path }) => path, + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push(error); + return; } }; - let move_out = self.data.moves.push(MoveOut { path: path, source: loc }); + let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc }); debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}", - loc, lval, move_out, path); + self.loc, place, move_out, path); + + self.builder.data.path_map[path].push(move_out); + self.builder.data.loc_map[self.loc].push(move_out); + } - self.data.path_map[path].push(move_out); - self.data.loc_map[loc].push(move_out); + fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) { + debug!("gather_init({:?}, {:?})", self.loc, place); + + if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { + let init = self.builder.data.inits.push(Init { + span: self.builder.mir.source_info(self.loc).span, + path, + kind, + }); + + debug!("gather_init({:?}, {:?}): adding init {:?} of {:?}", + self.loc, place, init, path); + + self.builder.data.init_path_map[path].push(init); + self.builder.data.init_loc_map[self.loc].push(init); + } } } diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index d2d8064984682..9d91e1344dc37 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -13,6 +13,7 @@ use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::{IndexVec}; +use syntax_pos::{Span}; use std::fmt; use std::ops::{Index, IndexMut}; @@ -59,12 +60,16 @@ pub(crate) mod indexes { /// Index into MoveData.moves. new_index!(MoveOutIndex, "mo"); + /// Index into MoveData.inits. + new_index!(InitIndex, "in"); + /// Index into Borrows.locations new_index!(BorrowIndex, "bw"); } pub use self::indexes::MovePathIndex; pub use self::indexes::MoveOutIndex; +pub use self::indexes::InitIndex; impl MoveOutIndex { pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex { @@ -89,7 +94,7 @@ pub struct MovePath<'tcx> { pub next_sibling: Option, pub first_child: Option, pub parent: Option, - pub lvalue: Lvalue<'tcx>, + pub place: Place<'tcx>, } impl<'tcx> fmt::Debug for MovePath<'tcx> { @@ -104,13 +109,13 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> { if let Some(next_sibling) = self.next_sibling { write!(w, " next_sibling: {:?}", next_sibling)?; } - write!(w, " lvalue: {:?} }}", self.lvalue) + write!(w, " place: {:?} }}", self.place) } } impl<'tcx> fmt::Display for MovePath<'tcx> { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - write!(w, "{:?}", self.lvalue) + write!(w, "{:?}", self.place) } } @@ -125,6 +130,11 @@ pub struct MoveData<'tcx> { pub loc_map: LocationMap>, pub path_map: IndexVec>, pub rev_lookup: MovePathLookup<'tcx>, + pub inits: IndexVec, + /// Each Location `l` is mapped to the Inits that are effects + /// of executing the code at `l`. + pub init_loc_map: LocationMap>, + pub init_path_map: IndexVec>, } pub trait HasMoveData<'tcx> { @@ -181,16 +191,44 @@ impl fmt::Debug for MoveOut { } } +/// `Init` represents a point in a program that initializes some L-value; +#[derive(Copy, Clone)] +pub struct Init { + /// path being initialized + pub path: MovePathIndex, + /// span of initialization + pub span: Span, + /// Extra information about this initialization + pub kind: InitKind, +} + +/// Additional information about the initialization. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum InitKind { + /// Deep init, even on panic + Deep, + /// Only does a shallow init + Shallow, + /// This doesn't initialize the variabe on panic (and a panic is possible). + NonPanicPathOnly, +} + +impl fmt::Debug for Init { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{:?}@{:?} ({:?})", self.path, self.span, self.kind) + } +} + /// Tables mapping from an l-value to its MovePathIndex. #[derive(Debug)] pub struct MovePathLookup<'tcx> { locals: IndexVec, - /// projections are made from a base-lvalue and a projection - /// elem. The base-lvalue will have a unique MovePathIndex; we use + /// projections are made from a base-place and a projection + /// elem. The base-place will have a unique MovePathIndex; we use /// the latter as the index into the outer vector (narrowing /// subsequent search so that it is solely relative to that - /// base-lvalue). For the remaining lookup, we map the projection + /// base-place). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. projections: FxHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex> } @@ -208,11 +246,11 @@ impl<'tcx> MovePathLookup<'tcx> { // alternative will *not* create a MovePath on the fly for an // unknown l-value, but will rather return the nearest available // parent. - pub fn find(&self, lval: &Lvalue<'tcx>) -> LookupResult { - match *lval { - Lvalue::Local(local) => LookupResult::Exact(self.locals[local]), - Lvalue::Static(..) => LookupResult::Parent(None), - Lvalue::Projection(ref proj) => { + pub fn find(&self, place: &Place<'tcx>) -> LookupResult { + match *place { + Place::Local(local) => LookupResult::Exact(self.locals[local]), + Place::Static(..) => LookupResult::Parent(None), + Place::Projection(ref proj) => { match self.find(&proj.base) { LookupResult::Exact(base_path) => { match self.projections.get(&(base_path, proj.elem.lift())) { @@ -225,13 +263,42 @@ impl<'tcx> MovePathLookup<'tcx> { } } } + + pub fn find_local(&self, local: Local) -> MovePathIndex { + self.locals[local] + } +} + +#[derive(Debug)] +pub struct IllegalMoveOrigin<'tcx> { + pub(crate) span: Span, + pub(crate) kind: IllegalMoveOriginKind<'tcx>, +} + +#[derive(Debug)] +pub(crate) enum IllegalMoveOriginKind<'tcx> { + Static, + BorrowedContent, + InteriorOfTypeWithDestructor { container_ty: ty::Ty<'tcx> }, + InteriorOfSliceOrArray { ty: ty::Ty<'tcx>, is_index: bool, }, +} + +#[derive(Debug)] +pub enum MoveError<'tcx> { + IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> }, + UnionMove { path: MovePathIndex }, +} + +impl<'tcx> MoveError<'tcx> { + fn cannot_move_out_of(span: Span, kind: IllegalMoveOriginKind<'tcx>) -> Self { + let origin = IllegalMoveOrigin { span, kind }; + MoveError::IllegalMove { cannot_move_out_of: origin } + } } -impl<'a, 'tcx> MoveData<'tcx> { - pub fn gather_moves(mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Self { - builder::gather_moves(mir, tcx, param_env) +impl<'a, 'gcx, 'tcx> MoveData<'tcx> { + pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> Result>)> { + builder::gather_moves(mir, tcx) } } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 2c4afb0aa0e04..619c0dc847ebc 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -229,6 +229,57 @@ fn main() { See also https://doc.rust-lang.org/book/first-edition/unsafe.html "##, +E0373: r##" +This error occurs when an attempt is made to use data captured by a closure, +when that data may no longer exist. It's most commonly seen when attempting to +return a closure: + +```compile_fail,E0373 +fn foo() -> Box u32> { + let x = 0u32; + Box::new(|y| x + y) +} +``` + +Notice that `x` is stack-allocated by `foo()`. By default, Rust captures +closed-over data by reference. This means that once `foo()` returns, `x` no +longer exists. An attempt to access `x` within the closure would thus be +unsafe. + +Another situation where this might be encountered is when spawning threads: + +```compile_fail,E0373 +fn foo() { + let x = 0u32; + let y = 1u32; + + let thr = std::thread::spawn(|| { + x + y + }); +} +``` + +Since our new thread runs in parallel, the stack frame containing `x` and `y` +may well have disappeared by the time we try to use them. Even if we call +`thr.join()` within foo (which blocks until `thr` has completed, ensuring the +stack frame won't disappear), we will not succeed: the compiler cannot prove +that this behaviour is safe, and so won't let us do it. + +The solution to this problem is usually to switch to using a `move` closure. +This approach moves (or copies, where possible) data into the closure, rather +than taking references to it. For example: + +``` +fn foo() -> Box u32> { + let x = 0u32; + Box::new(move |y| x + y) +} +``` + +Now that the closure has its own copy of the data, there's no need to worry +about safety. +"##, + E0381: r##" It is not allowed to use or capture an uninitialized variable. For example: @@ -250,6 +301,152 @@ fn main() { ``` "##, +E0382: r##" +This error occurs when an attempt is made to use a variable after its contents +have been moved elsewhere. For example: + +```compile_fail,E0382 +struct MyStruct { s: u32 } + +fn main() { + let mut x = MyStruct{ s: 5u32 }; + let y = x; + x.s = 6; + println!("{}", x.s); +} +``` + +Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out +of `x` when we set `y`. This is fundamental to Rust's ownership system: outside +of workarounds like `Rc`, a value cannot be owned by more than one variable. + +Sometimes we don't need to move the value. Using a reference, we can let another +function borrow the value without changing its ownership. In the example below, +we don't actually have to move our string to `calculate_length`, we can give it +a reference to it with `&` instead. + +``` +fn main() { + let s1 = String::from("hello"); + + let len = calculate_length(&s1); + + println!("The length of '{}' is {}.", s1, len); +} + +fn calculate_length(s: &String) -> usize { + s.len() +} +``` + +A mutable reference can be created with `&mut`. + +Sometimes we don't want a reference, but a duplicate. All types marked `Clone` +can be duplicated by calling `.clone()`. Subsequent changes to a clone do not +affect the original variable. + +Most types in the standard library are marked `Clone`. The example below +demonstrates using `clone()` on a string. `s1` is first set to "many", and then +copied to `s2`. Then the first character of `s1` is removed, without affecting +`s2`. "any many" is printed to the console. + +``` +fn main() { + let mut s1 = String::from("many"); + let s2 = s1.clone(); + s1.remove(0); + println!("{} {}", s1, s2); +} +``` + +If we control the definition of a type, we can implement `Clone` on it ourselves +with `#[derive(Clone)]`. + +Some types have no ownership semantics at all and are trivial to duplicate. An +example is `i32` and the other number types. We don't have to call `.clone()` to +clone them, because they are marked `Copy` in addition to `Clone`. Implicit +cloning is more convienient in this case. We can mark our own types `Copy` if +all their members also are marked `Copy`. + +In the example below, we implement a `Point` type. Because it only stores two +integers, we opt-out of ownership semantics with `Copy`. Then we can +`let p2 = p1` without `p1` being moved. + +``` +#[derive(Copy, Clone)] +struct Point { x: i32, y: i32 } + +fn main() { + let mut p1 = Point{ x: -1, y: 2 }; + let p2 = p1; + p1.x = 1; + println!("p1: {}, {}", p1.x, p1.y); + println!("p2: {}, {}", p2.x, p2.y); +} +``` + +Alternatively, if we don't control the struct's definition, or mutable shared +ownership is truly required, we can use `Rc` and `RefCell`: + +``` +use std::cell::RefCell; +use std::rc::Rc; + +struct MyStruct { s: u32 } + +fn main() { + let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 })); + let y = x.clone(); + x.borrow_mut().s = 6; + println!("{}", x.borrow().s); +} +``` + +With this approach, x and y share ownership of the data via the `Rc` (reference +count type). `RefCell` essentially performs runtime borrow checking: ensuring +that at most one writer or multiple readers can access the data at any one time. + +If you wish to learn more about ownership in Rust, start with the chapter in the +Book: + +https://doc.rust-lang.org/book/first-edition/ownership.html +"##, + +E0383: r##" +This error occurs when an attempt is made to partially reinitialize a +structure that is currently uninitialized. + +For example, this can happen when a drop has taken place: + +```compile_fail,E0383 +struct Foo { + a: u32, +} +impl Drop for Foo { + fn drop(&mut self) { /* ... */ } +} + +let mut x = Foo { a: 1 }; +drop(x); // `x` is now uninitialized +x.a = 2; // error, partial reinitialization of uninitialized structure `t` +``` + +This error can be fixed by fully reinitializing the structure in question: + +``` +struct Foo { + a: u32, +} +impl Drop for Foo { + fn drop(&mut self) { /* ... */ } +} + +let mut x = Foo { a: 1 }; +drop(x); +x = Foo { a: 2 }; +``` +"##, + E0384: r##" This error occurs when an attempt is made to reassign an immutable variable. For example: @@ -272,6 +469,161 @@ fn main() { ``` "##, +/*E0386: r##" +This error occurs when an attempt is made to mutate the target of a mutable +reference stored inside an immutable container. + +For example, this can happen when storing a `&mut` inside an immutable `Box`: + +```compile_fail,E0386 +let mut x: i64 = 1; +let y: Box<_> = Box::new(&mut x); +**y = 2; // error, cannot assign to data in an immutable container +``` + +This error can be fixed by making the container mutable: + +``` +let mut x: i64 = 1; +let mut y: Box<_> = Box::new(&mut x); +**y = 2; +``` + +It can also be fixed by using a type with interior mutability, such as `Cell` +or `RefCell`: + +``` +use std::cell::Cell; + +let x: i64 = 1; +let y: Box> = Box::new(Cell::new(x)); +y.set(2); +``` +"##,*/ + +E0387: r##" +This error occurs when an attempt is made to mutate or mutably reference data +that a closure has captured immutably. Examples of this error are shown below: + +```compile_fail,E0387 +// Accepts a function or a closure that captures its environment immutably. +// Closures passed to foo will not be able to mutate their closed-over state. +fn foo(f: F) { } + +// Attempts to mutate closed-over data. Error message reads: +// `cannot assign to data in a captured outer variable...` +fn mutable() { + let mut x = 0u32; + foo(|| x = 2); +} + +// Attempts to take a mutable reference to closed-over data. Error message +// reads: `cannot borrow data mutably in a captured outer variable...` +fn mut_addr() { + let mut x = 0u32; + foo(|| { let y = &mut x; }); +} +``` + +The problem here is that foo is defined as accepting a parameter of type `Fn`. +Closures passed into foo will thus be inferred to be of type `Fn`, meaning that +they capture their context immutably. + +If the definition of `foo` is under your control, the simplest solution is to +capture the data mutably. This can be done by defining `foo` to take FnMut +rather than Fn: + +``` +fn foo(f: F) { } +``` + +Alternatively, we can consider using the `Cell` and `RefCell` types to achieve +interior mutability through a shared reference. Our example's `mutable` +function could be redefined as below: + +``` +use std::cell::Cell; + +fn foo(f: F) { } + +fn mutable() { + let x = Cell::new(0u32); + foo(|| x.set(2)); +} +``` + +You can read more about cell types in the API documentation: + +https://doc.rust-lang.org/std/cell/ +"##, + +E0388: r##" +E0388 was removed and is no longer issued. +"##, + +E0389: r##" +An attempt was made to mutate data using a non-mutable reference. This +commonly occurs when attempting to assign to a non-mutable reference of a +mutable reference (`&(&mut T)`). + +Example of erroneous code: + +```compile_fail,E0389 +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + let fancy_ref = &(&mut fancy); + fancy_ref.num = 6; // error: cannot assign to data in a `&` reference + println!("{}", fancy_ref.num); +} +``` + +Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an +immutable reference to a value borrows it immutably. There can be multiple +references of type `&(&mut T)` that point to the same value, so they must be +immutable to prevent multiple mutable references to the same value. + +To fix this, either remove the outer reference: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + + let fancy_ref = &mut fancy; + // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum) + + fancy_ref.num = 6; // No error! + + println!("{}", fancy_ref.num); +} +``` + +Or make the outer reference mutable: + +``` +struct FancyNum { + num: u8 +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + + let fancy_ref = &mut (&mut fancy); + // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum) + + fancy_ref.num = 6; // No error! + + println!("{}", fancy_ref.num); +} +``` +"##, E0394: r##" A static was referred to by value by another static. @@ -418,8 +770,6 @@ static B: &'static AtomicUsize = &A; // ok! You can also have this error while using a cell type: ```compile_fail,E0492 -#![feature(const_cell_new)] - use std::cell::Cell; const A: Cell = Cell::new(1); @@ -446,8 +796,6 @@ However, if you still wish to use these types, you can achieve this by an unsafe wrapper: ``` -#![feature(const_cell_new)] - use std::cell::Cell; use std::marker::Sync; @@ -999,11 +1347,435 @@ fn print_fancy_ref(fancy_ref: &FancyNum){ ``` "##, +E0507: r##" +You tried to move out of a value which was borrowed. Erroneous code example: + +```compile_fail,E0507 +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // error: cannot move out of borrowed content +} +``` + +Here, the `nothing_is_true` method takes the ownership of `self`. However, +`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`, +which is a borrow of the content owned by the `RefCell`. To fix this error, +you have three choices: + +* Try to avoid moving the variable. +* Somehow reclaim the ownership. +* Implement the `Copy` trait on the type. + +Examples: + +``` +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(&self) {} // First case, we don't take ownership +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // ok! +} +``` + +Or: + +``` +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + let x = x.into_inner(); // we get back ownership + + x.nothing_is_true(); // ok! +} +``` + +Or: + +``` +use std::cell::RefCell; + +#[derive(Clone, Copy)] // we implement the Copy trait +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // ok! +} +``` + +Moving a member out of a mutably borrowed struct will also cause E0507 error: + +```compile_fail,E0507 +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +struct Batcave { + knight: TheDarkKnight +} + +fn main() { + let mut cave = Batcave { + knight: TheDarkKnight + }; + let borrowed = &mut cave; + + borrowed.knight.nothing_is_true(); // E0507 +} +``` + +It is fine only if you put something back. `mem::replace` can be used for that: + +``` +# struct TheDarkKnight; +# impl TheDarkKnight { fn nothing_is_true(self) {} } +# struct Batcave { knight: TheDarkKnight } +use std::mem; + +let mut cave = Batcave { + knight: TheDarkKnight +}; +let borrowed = &mut cave; + +mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! +``` + +You can find more information about borrowing in the rust-book: +http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html +"##, + +E0508: r##" +A value was moved out of a non-copy fixed-size array. + +Example of erroneous code: + +```compile_fail,E0508 +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`, + // a non-copy fixed-size array +} +``` + +The first element was moved out of the array, but this is not +possible because `NonCopy` does not implement the `Copy` trait. + +Consider borrowing the element instead of moving it: + +``` +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = &array[0]; // Borrowing is allowed, unlike moving. +} +``` + +Alternatively, if your type implements `Clone` and you need to own the value, +consider borrowing and then cloning: + +``` +#[derive(Clone)] +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + // Now you can clone the array element. + let _value = array[0].clone(); +} +``` +"##, + +E0509: r##" +This error occurs when an attempt is made to move out of a value whose type +implements the `Drop` trait. + +Example of erroneous code: + +```compile_fail,E0509 +struct FancyNum { + num: usize +} + +struct DropStruct { + fancy: FancyNum +} + +impl Drop for DropStruct { + fn drop(&mut self) { + // Destruct DropStruct, possibly using FancyNum + } +} + +fn main() { + let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; + let fancy_field = drop_struct.fancy; // Error E0509 + println!("Fancy: {}", fancy_field.num); + // implicit call to `drop_struct.drop()` as drop_struct goes out of scope +} +``` + +Here, we tried to move a field out of a struct of type `DropStruct` which +implements the `Drop` trait. However, a struct cannot be dropped if one or +more of its fields have been moved. + +Structs implementing the `Drop` trait have an implicit destructor that gets +called when they go out of scope. This destructor may use the fields of the +struct, so moving out of the struct could make it impossible to run the +destructor. Therefore, we must think of all values whose type implements the +`Drop` trait as single units whose fields cannot be moved. + +This error can be fixed by creating a reference to the fields of a struct, +enum, or tuple using the `ref` keyword: + +``` +struct FancyNum { + num: usize +} + +struct DropStruct { + fancy: FancyNum +} + +impl Drop for DropStruct { + fn drop(&mut self) { + // Destruct DropStruct, possibly using FancyNum + } +} + +fn main() { + let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; + let ref fancy_field = drop_struct.fancy; // No more errors! + println!("Fancy: {}", fancy_field.num); + // implicit call to `drop_struct.drop()` as drop_struct goes out of scope +} +``` + +Note that this technique can also be used in the arms of a match expression: + +``` +struct FancyNum { + num: usize +} + +enum DropEnum { + Fancy(FancyNum) +} + +impl Drop for DropEnum { + fn drop(&mut self) { + // Destruct DropEnum, possibly using FancyNum + } +} + +fn main() { + // Creates and enum of type `DropEnum`, which implements `Drop` + let drop_enum = DropEnum::Fancy(FancyNum{num: 10}); + match drop_enum { + // Creates a reference to the inside of `DropEnum::Fancy` + DropEnum::Fancy(ref fancy_field) => // No error! + println!("It was fancy-- {}!", fancy_field.num), + } + // implicit call to `drop_enum.drop()` as drop_enum goes out of scope +} +``` +"##, + +E0595: r##" +Closures cannot mutate immutable captured variables. + +Erroneous code example: + +```compile_fail,E0595 +let x = 3; // error: closure cannot assign to immutable local variable `x` +let mut c = || { x += 1 }; +``` + +Make the variable binding mutable: + +``` +let mut x = 3; // ok! +let mut c = || { x += 1 }; +``` +"##, + +E0596: r##" +This error occurs because you tried to mutably borrow a non-mutable variable. + +Example of erroneous code: + +```compile_fail,E0596 +let x = 1; +let y = &mut x; // error: cannot borrow mutably +``` + +In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it +fails. To fix this error, you need to make `x` mutable: + +``` +let mut x = 1; +let y = &mut x; // ok! +``` +"##, + +E0597: r##" +This error occurs because a borrow was made inside a variable which has a +greater lifetime than the borrowed one. + +Example of erroneous code: + +```compile_fail,E0597 +struct Foo<'a> { + x: Option<&'a u32>, +} + +let mut x = Foo { x: None }; +let y = 0; +x.x = Some(&y); // error: `y` does not live long enough +``` + +In here, `x` is created before `y` and therefore has a greater lifetime. Always +keep in mind that values in a scope are dropped in the opposite order they are +created. So to fix the previous example, just make the `y` lifetime greater than +the `x`'s one: + +``` +struct Foo<'a> { + x: Option<&'a u32>, +} + +let y = 0; +let mut x = Foo { x: None }; +x.x = Some(&y); +``` +"##, + +E0626: r##" +This error occurs because a borrow in a generator persists across a +yield point. + +```compile_fail,E0626 +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let a = &String::new(); // <-- This borrow... + yield (); // ...is still in scope here, when the yield occurs. + println!("{}", a); +}; +b.resume(); +``` + +At present, it is not permitted to have a yield that occurs while a +borrow is still in scope. To resolve this error, the borrow must +either be "contained" to a smaller scope that does not overlap the +yield or else eliminated in another way. So, for example, we might +resolve the previous example by removing the borrow and just storing +the integer by value: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let a = 3; + yield (); + println!("{}", a); +}; +b.resume(); +``` + +This is a very simple case, of course. In more complex cases, we may +wish to have more than one reference to the value that was borrowed -- +in those cases, something like the `Rc` or `Arc` types may be useful. + +This error also frequently arises with iteration: + +```compile_fail,E0626 +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + for &x in &v { // <-- borrow of `v` is still in scope... + yield x; // ...when this yield occurs. + } +}; +b.resume(); +``` + +Such cases can sometimes be resolved by iterating "by value" (or using +`into_iter()`) to avoid borrowing: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + for x in v { // <-- Take ownership of the values instead! + yield x; // <-- Now yield is OK. + } +}; +b.resume(); +``` + +If taking ownership is not an option, using indices can work too: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + let len = v.len(); // (*) + for i in 0..len { + let x = v[i]; // (*) + yield x; // <-- Now yield is OK. + } +}; +b.resume(); + +// (*) -- Unfortunately, these temporaries are currently required. +// See . +``` +"##, + } register_diagnostics! { +// E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0524, // two closures require unique access to `..` at the same time E0526, // shuffle indices are not constant + E0594, // cannot assign to {} + E0598, // lifetime of {} is too short to guarantee its contents can be... E0625, // thread-local statics cannot be accessed at compile-time } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index f5a53e2aa8eed..848c2d3c811e9 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -20,6 +20,7 @@ use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::cast::CastKind as TyCastKind; use rustc::hir; +use rustc::hir::def_id::LocalDefId; impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { type Output = Expr<'tcx>; @@ -115,7 +116,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }, }; - overloaded_lvalue(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) + overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) } Adjust::Borrow(AutoBorrow::Ref(r, m)) => { ExprKind::Borrow { @@ -334,7 +335,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprIndex(ref lhs, ref index) => { if cx.tables().is_method_call(expr) { - overloaded_lvalue(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) + overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) } else { ExprKind::Index { lhs: lhs.to_ref(), @@ -345,7 +346,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { if cx.tables().is_method_call(expr) { - overloaded_lvalue(cx, expr, expr_ty, None, vec![arg.to_ref()]) + overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()]) } else { ExprKind::Deref { arg: arg.to_ref() } } @@ -712,8 +713,8 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }); let region = cx.tcx.mk_region(region); - let self_expr = if let ty::TyClosure(..) = closure_ty.sty { - match cx.tcx.closure_kind(closure_def_id) { + let self_expr = if let ty::TyClosure(_, closure_substs) = closure_ty.sty { + match cx.infcx.closure_kind(closure_def_id, closure_substs).unwrap() { ty::ClosureKind::Fn => { let ref_closure_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { @@ -783,7 +784,7 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, // point we need an implicit deref let upvar_id = ty::UpvarId { var_id: var_hir_id, - closure_expr_id: closure_def_id.index, + closure_expr_id: LocalDefId::from_def_id(closure_def_id), }; match cx.tables().upvar_capture(upvar_id) { ty::UpvarCapture::ByValue => field_kind, @@ -843,15 +844,15 @@ fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } -fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, +fn overloaded_place<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, - lvalue_ty: Ty<'tcx>, + place_ty: Ty<'tcx>, custom_callee: Option<(DefId, &'tcx Substs<'tcx>)>, args: Vec>) -> ExprKind<'tcx> { // For an overloaded *x or x[y] expression of type T, the method // call returns an &T and we must add the deref so that the types - // line up (this is because `*x` and `x[y]` represent lvalues): + // line up (this is because `*x` and `x[y]` represent places): let recv_ty = match args[0] { ExprRef::Hair(e) => cx.tables().expr_ty_adjusted(e), @@ -863,10 +864,10 @@ fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. let (region, mt) = match recv_ty.sty { ty::TyRef(region, mt) => (region, mt), - _ => span_bug!(expr.span, "overloaded_lvalue: receiver is not a reference"), + _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"), }; let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { - ty: lvalue_ty, + ty: place_ty, mutbl: mt.mutbl, }); @@ -897,7 +898,7 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let var_hir_id = cx.tcx.hir.node_to_hir_id(freevar.var_id()); let upvar_id = ty::UpvarId { var_id: var_hir_id, - closure_expr_id: cx.tcx.hir.local_def_id(closure_expr.id).index, + closure_expr_id: cx.tcx.hir.local_def_id(closure_expr.id).to_local(), }; let upvar_capture = cx.tables().upvar_capture(upvar_id); let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 4434df0ac3e9b..306b41714a553 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -15,7 +15,6 @@ //! use hair::*; -use rustc::mir::transform::MirSource; use rustc::middle::const_val::{ConstEvalErr, ConstVal}; use rustc_const_eval::ConstContext; @@ -51,8 +50,8 @@ pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { /// `const`, or the body of a `const fn`. constness: hir::Constness, - /// What are we compiling? - pub src: MirSource, + /// What kind of body is being compiled. + pub body_owner_kind: hir::BodyOwnerKind, /// True if this constant/function needs overflow checks. check_overflow: bool, @@ -60,22 +59,20 @@ pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - src: MirSource) -> Cx<'a, 'gcx, 'tcx> { - let constness = match src { - MirSource::Const(_) | - MirSource::Static(..) => hir::Constness::Const, - MirSource::GeneratorDrop(..) => hir::Constness::NotConst, - MirSource::Fn(id) => { - let fn_like = FnLikeNode::from_node(infcx.tcx.hir.get(id)); + src_id: ast::NodeId) -> Cx<'a, 'gcx, 'tcx> { + let tcx = infcx.tcx; + let src_def_id = tcx.hir.local_def_id(src_id); + let body_owner_kind = tcx.hir.body_owner_kind(src_id); + + let constness = match body_owner_kind { + hir::BodyOwnerKind::Const | + hir::BodyOwnerKind::Static(_) => hir::Constness::Const, + hir::BodyOwnerKind::Fn => { + let fn_like = FnLikeNode::from_node(infcx.tcx.hir.get(src_id)); fn_like.map_or(hir::Constness::NotConst, |f| f.constness()) } - MirSource::Promoted(..) => bug!(), }; - let tcx = infcx.tcx; - let src_id = src.item_id(); - let src_def_id = tcx.hir.local_def_id(src_id); - let attrs = tcx.hir.attrs(src_id); // Some functions always have overflow checks enabled, @@ -100,7 +97,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { region_scope_tree: tcx.region_scope_tree(src_def_id), tables: tcx.typeck_tables_of(src_def_id), constness, - src, + body_owner_kind, check_overflow, } } @@ -216,10 +213,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { bug!("found no method `{}` in `{:?}`", method_name, trait_def_id); } - pub fn num_variants(&mut self, adt_def: &ty::AdtDef) -> usize { - adt_def.variants.len() - } - pub fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: usize) -> Vec { (0..adt_def.variants[variant_index].fields.len()) .map(Field::new) @@ -259,6 +252,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn check_overflow(&self) -> bool { self.check_overflow } + + pub fn type_moves_by_default(&self, ty: Ty<'tcx>, span: Span) -> bool { + self.infcx.type_moves_by_default(self.param_env, ty, span) + } } fn lint_level_for_hir_id(tcx: TyCtxt, mut id: ast::NodeId) -> ast::NodeId { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d0b9849986b82..53f9b885ac6c6 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -18,11 +18,20 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(catch_expr)] +#![feature(conservative_impl_trait)] +#![feature(const_fn)] +#![feature(core_intrinsics)] +#![feature(decl_macro)] #![feature(i128_type)] +#![feature(inclusive_range_syntax)] +#![feature(match_default_bindings)] +#![feature(range_contains)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] #![feature(collection_placement)] #![feature(nonzero)] +#![feature(underscore_lifetimes)] #[macro_use] extern crate bitflags; @@ -30,7 +39,8 @@ extern crate bitflags; extern crate graphviz as dot; #[macro_use] extern crate rustc; -extern crate rustc_data_structures; +#[macro_use] extern crate rustc_data_structures; +extern crate serialize as rustc_serialize; extern crate rustc_errors; #[macro_use] extern crate syntax; diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index a3a986918a4fd..46193dedf8968 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -13,7 +13,6 @@ use rustc::hir::def_id::DefId; use rustc::infer; use rustc::middle::const_val::ConstVal; use rustc::mir::*; -use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; @@ -28,7 +27,8 @@ use syntax_pos::Span; use std::fmt; use std::iter; -use transform::{add_call_guards, no_landing_pads, simplify}; +use transform::{add_moves_for_packed_drops, add_call_guards}; +use transform::{no_landing_pads, simplify}; use util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; use util::patch::MirPatch; @@ -115,6 +115,8 @@ fn make_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); + add_moves_for_packed_drops::add_moves_for_packed_drops( + tcx, &mut result, instance.def_id()); no_landing_pads::no_landing_pads(tcx, &mut result); simplify::simplify_cfg(&mut result); add_call_guards::CriticalCallEdges.add_call_guards(&mut result); @@ -196,9 +198,8 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, IndexVec::from_elem_n( VisibilityScopeData { span: span, parent_scope: None }, 1 ), - ClearOnDecode::Clear, + ClearCrossCrate::Clear, IndexVec::new(), - sig.output(), None, local_decls_for_sig(&sig, span), sig.inputs().len(), @@ -215,7 +216,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx, param_env }; - let dropee = Lvalue::Local(Local::new(1+0)).deref(); + let dropee = Place::Local(Local::new(1+0)).deref(); let resume_block = elaborator.patch.resume_block(); elaborate_drops::elaborate_drop( &mut elaborator, @@ -279,6 +280,9 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { fn downcast_subpath(&self, _path: Self::Path, _variant: usize) -> Option { Some(()) } + fn array_subpath(&self, _path: Self::Path, _index: u32, _size: u32) -> Option { + None + } } /// Build a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`. @@ -344,9 +348,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { IndexVec::from_elem_n( VisibilityScopeData { span: self.span, parent_scope: None }, 1 ), - ClearOnDecode::Clear, + ClearCrossCrate::Clear, IndexVec::new(), - self.sig.output(), None, self.local_decls, self.sig.inputs().len(), @@ -381,19 +384,19 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { } fn copy_shim(&mut self) { - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let rcvr = Place::Local(Local::new(1+0)).deref(); let ret_statement = self.make_statement( StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(rcvr)) + Place::Local(RETURN_PLACE), + Rvalue::Use(Operand::Copy(rcvr)) ) ); self.block(vec![ret_statement], TerminatorKind::Return, false); } - fn make_lvalue(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Lvalue<'tcx> { + fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> { let span = self.span; - Lvalue::Local( + Place::Local( self.local_decls.push(temp_decl(mutability, ty, span)) ) } @@ -401,10 +404,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { fn make_clone_call( &mut self, ty: Ty<'tcx>, - rcvr_field: Lvalue<'tcx>, + rcvr_field: Place<'tcx>, next: BasicBlock, cleanup: BasicBlock - ) -> Lvalue<'tcx> { + ) -> Place<'tcx> { let tcx = self.tcx; let substs = Substs::for_item( @@ -427,7 +430,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { }, }); - let ref_loc = self.make_lvalue( + let ref_loc = self.make_place( Mutability::Not, tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty, @@ -435,7 +438,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { }) ); - let loc = self.make_lvalue(Mutability::Not, ty); + let loc = self.make_place(Mutability::Not, ty); // `let ref_loc: &ty = &rcvr_field;` let statement = self.make_statement( @@ -448,7 +451,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // `let loc = Clone::clone(ref_loc);` self.block(vec![statement], TerminatorKind::Call { func, - args: vec![Operand::Consume(ref_loc)], + args: vec![Operand::Move(ref_loc)], destination: Some((loc.clone(), next)), cleanup: Some(cleanup), }, false); @@ -458,26 +461,26 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { fn loop_header( &mut self, - beg: Lvalue<'tcx>, - end: Lvalue<'tcx>, + beg: Place<'tcx>, + end: Place<'tcx>, loop_body: BasicBlock, loop_end: BasicBlock, is_cleanup: bool ) { let tcx = self.tcx; - let cond = self.make_lvalue(Mutability::Mut, tcx.types.bool); + let cond = self.make_place(Mutability::Mut, tcx.types.bool); let compute_cond = self.make_statement( StatementKind::Assign( cond.clone(), - Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) + Rvalue::BinaryOp(BinOp::Ne, Operand::Copy(end), Operand::Copy(beg)) ) ); // `if end != beg { goto loop_body; } else { goto loop_end; }` self.block( vec![compute_cond], - TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), + TerminatorKind::if_(tcx, Operand::Move(cond), loop_body, loop_end), is_cleanup ); } @@ -499,11 +502,11 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { fn array_shim(&mut self, ty: Ty<'tcx>, len: u64) { let tcx = self.tcx; let span = self.span; - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let rcvr = Place::Local(Local::new(1+0)).deref(); let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span)); - let end = self.make_lvalue(Mutability::Not, tcx.types.usize); - let ret = self.make_lvalue(Mutability::Mut, tcx.mk_array(ty, len)); + let end = self.make_place(Mutability::Not, tcx.types.usize); + let ret = self.make_place(Mutability::Mut, tcx.mk_array(ty, len)); // BB #0 // `let mut beg = 0;` @@ -512,7 +515,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let inits = vec![ self.make_statement( StatementKind::Assign( - Lvalue::Local(beg), + Place::Local(beg), Rvalue::Use(Operand::Constant(self.make_usize(0))) ) ), @@ -530,7 +533,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // BB #3; // } // BB #4; - self.loop_header(Lvalue::Local(beg), end, BasicBlock::new(2), BasicBlock::new(4), false); + self.loop_header(Place::Local(beg), end, BasicBlock::new(2), BasicBlock::new(4), false); // BB #2 // `let cloned = Clone::clone(rcvr[beg])`; @@ -547,15 +550,15 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { self.make_statement( StatementKind::Assign( ret_field, - Rvalue::Use(Operand::Consume(cloned)) + Rvalue::Use(Operand::Move(cloned)) ) ), self.make_statement( StatementKind::Assign( - Lvalue::Local(beg), + Place::Local(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Consume(Lvalue::Local(beg)), + Operand::Copy(Place::Local(beg)), Operand::Constant(self.make_usize(1)) ) ) @@ -567,8 +570,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // `return ret;` let ret_statement = self.make_statement( StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(ret.clone())), + Place::Local(RETURN_PLACE), + Rvalue::Use(Operand::Move(ret.clone())), ) ); self.block(vec![ret_statement], TerminatorKind::Return, false); @@ -581,7 +584,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span)); let init = self.make_statement( StatementKind::Assign( - Lvalue::Local(beg), + Place::Local(beg), Rvalue::Use(Operand::Constant(self.make_usize(0))) ) ); @@ -592,7 +595,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // BB #8; // } // BB #9; - self.loop_header(Lvalue::Local(beg), Lvalue::Local(end), + self.loop_header(Place::Local(beg), Place::Local(end), BasicBlock::new(7), BasicBlock::new(9), true); // BB #7 (cleanup) @@ -608,10 +611,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // `goto #6;` let statement = self.make_statement( StatementKind::Assign( - Lvalue::Local(beg), + Place::Local(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Consume(Lvalue::Local(beg)), + Operand::Copy(Place::Local(beg)), Operand::Constant(self.make_usize(1)) ) ) @@ -628,7 +631,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { _ => bug!("only tuples and closures are accepted"), }; - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let rcvr = Place::Local(Local::new(1+0)).deref(); let mut returns = Vec::new(); for (i, ity) in tys.iter().enumerate() { @@ -663,10 +666,10 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { // `return kind(returns[0], returns[1], ..., returns[tys.len() - 1]);` let ret_statement = self.make_statement( StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), + Place::Local(RETURN_PLACE), Rvalue::Aggregate( box kind, - returns.into_iter().map(Operand::Consume).collect() + returns.into_iter().map(Operand::Move).collect() ) ) ); @@ -701,12 +704,12 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; let rcvr_arg = Local::new(1+0); - let rcvr_l = Lvalue::Local(rcvr_arg); + let rcvr_l = Place::Local(rcvr_arg); let mut statements = vec![]; let rcvr = match rcvr_adjustment { - Adjustment::Identity => Operand::Consume(rcvr_l), - Adjustment::Deref => Operand::Consume(rcvr_l.deref()), + Adjustment::Identity => Operand::Move(rcvr_l), + Adjustment::Deref => Operand::Copy(rcvr_l.deref()), Adjustment::RefMut => { // let rcvr = &mut rcvr; let ref_rcvr = local_decls.push(temp_decl( @@ -720,11 +723,11 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, statements.push(Statement { source_info, kind: StatementKind::Assign( - Lvalue::Local(ref_rcvr), + Place::Local(ref_rcvr), Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut, rcvr_l) ) }); - Operand::Consume(Lvalue::Local(ref_rcvr)) + Operand::Move(Place::Local(ref_rcvr)) } }; @@ -749,12 +752,12 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Some(untuple_args) = untuple_args { args.extend(untuple_args.iter().enumerate().map(|(i, ity)| { - let arg_lv = Lvalue::Local(Local::new(1+1)); - Operand::Consume(arg_lv.field(Field::new(i), *ity)) + let arg_place = Place::Local(Local::new(1+1)); + Operand::Move(arg_place.field(Field::new(i), *ity)) })); } else { args.extend((1..sig.inputs().len()).map(|i| { - Operand::Consume(Lvalue::Local(Local::new(1+i))) + Operand::Move(Place::Local(Local::new(1+i))) })); } @@ -771,7 +774,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, block(&mut blocks, statements, TerminatorKind::Call { func: callee, args, - destination: Some((Lvalue::Local(RETURN_POINTER), + destination: Some((Place::Local(RETURN_PLACE), BasicBlock::new(1))), cleanup: if let Adjustment::RefMut = rcvr_adjustment { Some(BasicBlock::new(3)) @@ -783,7 +786,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Adjustment::RefMut = rcvr_adjustment { // BB #1 - drop for Self block(&mut blocks, vec![], TerminatorKind::Drop { - location: Lvalue::Local(rcvr_arg), + location: Place::Local(rcvr_arg), target: BasicBlock::new(2), unwind: None }, false); @@ -793,7 +796,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Adjustment::RefMut = rcvr_adjustment { // BB #3 - drop if closure panics block(&mut blocks, vec![], TerminatorKind::Drop { - location: Lvalue::Local(rcvr_arg), + location: Place::Local(rcvr_arg), target: BasicBlock::new(4), unwind: None }, true); @@ -807,9 +810,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, IndexVec::from_elem_n( VisibilityScopeData { span: span, parent_scope: None }, 1 ), - ClearOnDecode::Clear, + ClearCrossCrate::Clear, IndexVec::new(), - sig.output(), None, local_decls, sig.inputs().len(), @@ -826,13 +828,19 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, ctor_id: ast::NodeId, fields: &[hir::StructField], span: Span) - -> (Mir<'tcx>, MirSource) + -> Mir<'tcx> { let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); let def_id = tcx.hir.local_def_id(ctor_id); - let sig = tcx.no_late_bound_regions(&tcx.fn_sig(def_id)) + let sig = gcx.fn_sig(def_id).no_late_bound_regions() .expect("LBR in ADT constructor signature"); - let sig = tcx.erase_regions(&sig); + let sig = gcx.erase_regions(&sig); + let param_env = gcx.param_env(def_id); + + // Normalize the sig now that we have liberated the late-bound + // regions. + let sig = gcx.normalize_associated_type_in_env(&sig, param_env); let (adt_def, substs) = match sig.output().sty { ty::TyAdt(adt_def, substs) => (adt_def, substs), @@ -859,11 +867,11 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, statements: vec![Statement { source_info, kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), + Place::Local(RETURN_PLACE), Rvalue::Aggregate( box AggregateKind::Adt(adt_def, variant_no, substs, None), (1..sig.inputs().len()+1).map(|i| { - Operand::Consume(Lvalue::Local(Local::new(i))) + Operand::Move(Place::Local(Local::new(i))) }).collect() ) ) @@ -875,19 +883,17 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, is_cleanup: false }; - let mir = Mir::new( + Mir::new( IndexVec::from_elem_n(start_block, 1), IndexVec::from_elem_n( VisibilityScopeData { span: span, parent_scope: None }, 1 ), - ClearOnDecode::Clear, + ClearCrossCrate::Clear, IndexVec::new(), - sig.output(), None, local_decls, sig.inputs().len(), vec![], span - ); - (mir, MirSource::Fn(ctor_id)) + ) } diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index 3f14a6be8b25e..5be369f85bc21 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -10,8 +10,8 @@ use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use transform::{MirPass, MirSource}; #[derive(PartialEq)] pub enum AddCallGuards { diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs new file mode 100644 index 0000000000000..203669c61badd --- /dev/null +++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs @@ -0,0 +1,141 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::def_id::DefId; +use rustc::mir::*; +use rustc::ty::TyCtxt; + +use transform::{MirPass, MirSource}; +use util::patch::MirPatch; +use util; + +// This pass moves values being dropped that are within a packed +// struct to a separate local before dropping them, to ensure that +// they are dropped from an aligned address. +// +// For example, if we have something like +// ```Rust +// #[repr(packed)] +// struct Foo { +// dealign: u8, +// data: Vec +// } +// +// let foo = ...; +// ``` +// +// We want to call `drop_in_place::>` on `data` from an aligned +// address. This means we can't simply drop `foo.data` directly, because +// its address is not aligned. +// +// Instead, we move `foo.data` to a local and drop that: +// ``` +// storage.live(drop_temp) +// drop_temp = foo.data; +// drop(drop_temp) -> next +// next: +// storage.dead(drop_temp) +// ``` +// +// The storage instructions are required to avoid stack space +// blowup. + +pub struct AddMovesForPackedDrops; + +impl MirPass for AddMovesForPackedDrops { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, + mir: &mut Mir<'tcx>) + { + debug!("add_moves_for_packed_drops({:?} @ {:?})", src, mir.span); + add_moves_for_packed_drops(tcx, mir, src.def_id); + } +} + +pub fn add_moves_for_packed_drops<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &mut Mir<'tcx>, + def_id: DefId) +{ + let patch = add_moves_for_packed_drops_patch(tcx, mir, def_id); + patch.apply(mir); +} + +fn add_moves_for_packed_drops_patch<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + def_id: DefId) + -> MirPatch<'tcx> +{ + let mut patch = MirPatch::new(mir); + let param_env = tcx.param_env(def_id); + + for (bb, data) in mir.basic_blocks().iter_enumerated() { + let loc = Location { block: bb, statement_index: data.statements.len() }; + let terminator = data.terminator(); + + match terminator.kind { + TerminatorKind::Drop { ref location, .. } + if util::is_disaligned(tcx, mir, param_env, location) => + { + add_move_for_packed_drop(tcx, mir, &mut patch, terminator, + loc, data.is_cleanup); + } + TerminatorKind::DropAndReplace { .. } => { + span_bug!(terminator.source_info.span, + "replace in AddMovesForPackedDrops"); + } + _ => {} + } + } + + patch +} + +fn add_move_for_packed_drop<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + patch: &mut MirPatch<'tcx>, + terminator: &Terminator<'tcx>, + loc: Location, + is_cleanup: bool) +{ + debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); + let (location, target, unwind) = match terminator.kind { + TerminatorKind::Drop { ref location, target, unwind } => + (location, target, unwind), + _ => unreachable!() + }; + + let source_info = terminator.source_info; + let ty = location.ty(mir, tcx).to_ty(tcx); + let temp = patch.new_temp(ty, terminator.source_info.span); + + let storage_dead_block = patch.new_block(BasicBlockData { + statements: vec![Statement { + source_info, kind: StatementKind::StorageDead(temp) + }], + terminator: Some(Terminator { + source_info, kind: TerminatorKind::Goto { target } + }), + is_cleanup + }); + + patch.add_statement( + loc, StatementKind::StorageLive(temp)); + patch.add_assign(loc, Place::Local(temp), + Rvalue::Use(Operand::Move(location.clone()))); + patch.patch_terminator(loc.block, TerminatorKind::Drop { + location: Place::Local(temp), + target: storage_dead_block, + unwind + }); +} diff --git a/src/librustc_mir/transform/add_validation.rs b/src/librustc_mir/transform/add_validation.rs index 8fad538af97ba..f0f6add3f6f81 100644 --- a/src/librustc_mir/transform/add_validation.rs +++ b/src/librustc_mir/transform/add_validation.rs @@ -17,22 +17,22 @@ use rustc::ty::{self, TyCtxt, RegionKind}; use rustc::hir; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; use rustc::middle::region; +use transform::{MirPass, MirSource}; pub struct AddValidation; -/// Determine the "context" of the lval: Mutability and region. -fn lval_context<'a, 'tcx, D>( - lval: &Lvalue<'tcx>, +/// Determine the "context" of the place: Mutability and region. +fn place_context<'a, 'tcx, D>( + place: &Place<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx> ) -> (Option, hir::Mutability) where D: HasLocalDecls<'tcx> { - use rustc::mir::Lvalue::*; + use rustc::mir::Place::*; - match *lval { + match *place { Local { .. } => (None, hir::MutMutable), Static(_) => (None, hir::MutImmutable), Projection(ref proj) => { @@ -66,7 +66,7 @@ fn lval_context<'a, 'tcx, D>( // This is already as restricted as it gets, no need to even recurse context } else { - let base_context = lval_context(&proj.base, local_decls, tcx); + let base_context = place_context(&proj.base, local_decls, tcx); // The region of the outermost Deref is always most restrictive. let re = context.0.or(base_context.0); let mutbl = context.1.and(base_context.1); @@ -74,7 +74,7 @@ fn lval_context<'a, 'tcx, D>( } } - _ => lval_context(&proj.base, local_decls, tcx), + _ => place_context(&proj.base, local_decls, tcx), } } } @@ -106,8 +106,9 @@ fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> } } - let fn_like = match src { - MirSource::Fn(node_id) => { + let node_id = tcx.hir.as_local_node_id(src.def_id).unwrap(); + let fn_like = match tcx.hir.body_owner_kind(node_id) { + hir::BodyOwnerKind::Fn => { match FnLikeNode::from_node(tcx.hir.get(node_id)) { Some(fn_like) => fn_like, None => return false, // e.g. struct ctor shims -- such auto-generated code cannot @@ -197,11 +198,11 @@ impl MirPass for AddValidation { let restricted_validation = emit_validate == 1 && fn_contains_unsafe(tcx, src); let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone. - // Convert an lvalue to a validation operand. - let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx, Lvalue<'tcx>> { - let (re, mutbl) = lval_context(&lval, &local_decls, tcx); - let ty = lval.ty(&local_decls, tcx).to_ty(tcx); - ValidationOperand { lval, ty, re, mutbl } + // Convert a place to a validation operand. + let place_to_operand = |place: Place<'tcx>| -> ValidationOperand<'tcx, Place<'tcx>> { + let (re, mutbl) = place_context(&place, &local_decls, tcx); + let ty = place.ty(&local_decls, tcx).to_ty(tcx); + ValidationOperand { place, ty, re, mutbl } }; // Emit an Acquire at the beginning of the given block. If we are in restricted emission @@ -236,14 +237,14 @@ impl MirPass for AddValidation { }; // Gather all arguments, skip return value. let operands = mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count) - .map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect(); + .map(|(local, _)| place_to_operand(Place::Local(local))).collect(); emit_acquire(&mut mir.basic_blocks_mut()[START_BLOCK], source_info, operands); } // PART 2 // Add ReleaseValid/AcquireValid around function call terminators. We don't use a visitor // because we need to access the block that a Call jumps to. - let mut returns : Vec<(SourceInfo, Lvalue<'tcx>, BasicBlock)> = Vec::new(); + let mut returns : Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new(); for block_data in mir.basic_blocks_mut() { match block_data.terminator { Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. }, @@ -255,12 +256,13 @@ impl MirPass for AddValidation { let release_stmt = Statement { source_info, kind: StatementKind::Validate(ValidationOp::Release, - destination.iter().map(|dest| lval_to_operand(dest.0.clone())) + destination.iter().map(|dest| place_to_operand(dest.0.clone())) .chain( args.iter().filter_map(|op| { match op { - &Operand::Consume(ref lval) => - Some(lval_to_operand(lval.clone())), + &Operand::Copy(ref place) | + &Operand::Move(ref place) => + Some(place_to_operand(place.clone())), &Operand::Constant(..) => { None }, } }) @@ -273,16 +275,16 @@ impl MirPass for AddValidation { returns.push((source_info, destination.0.clone(), destination.1)); } } - Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. }, + Some(Terminator { kind: TerminatorKind::Drop { location: ref place, .. }, source_info }) | - Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. }, + Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref place, .. }, source_info }) => { // Before the call: Release all arguments if !restricted_validation { let release_stmt = Statement { source_info, kind: StatementKind::Validate(ValidationOp::Release, - vec![lval_to_operand(lval.clone())]), + vec![place_to_operand(place.clone())]), }; block_data.statements.push(release_stmt); } @@ -294,11 +296,11 @@ impl MirPass for AddValidation { } } // Now we go over the returns we collected to acquire the return values. - for (source_info, dest_lval, dest_block) in returns { + for (source_info, dest_place, dest_block) in returns { emit_acquire( &mut mir.basic_blocks_mut()[dest_block], source_info, - vec![lval_to_operand(dest_lval)] + vec![place_to_operand(dest_place)] ); } @@ -319,22 +321,20 @@ impl MirPass for AddValidation { StatementKind::Assign(_, Rvalue::Ref(_, _, _)) => { // Due to a lack of NLL; we can't capture anything directly here. // Instead, we have to re-match and clone there. - let (dest_lval, re, src_lval) = match block_data.statements[i].kind { - StatementKind::Assign(ref dest_lval, - Rvalue::Ref(re, _, ref src_lval)) => { - (dest_lval.clone(), re, src_lval.clone()) + let (dest_place, re, src_place) = match block_data.statements[i].kind { + StatementKind::Assign(ref dest_place, + Rvalue::Ref(re, _, ref src_place)) => { + (dest_place.clone(), re, src_place.clone()) }, _ => bug!("We already matched this."), }; // So this is a ref, and we got all the data we wanted. // Do an acquire of the result -- but only what it points to, so add a Deref // projection. - let dest_lval = Projection { base: dest_lval, elem: ProjectionElem::Deref }; - let dest_lval = Lvalue::Projection(Box::new(dest_lval)); let acquire_stmt = Statement { source_info: block_data.statements[i].source_info, kind: StatementKind::Validate(ValidationOp::Acquire, - vec![lval_to_operand(dest_lval)]), + vec![place_to_operand(dest_place.deref())]), }; block_data.statements.insert(i+1, acquire_stmt); @@ -347,21 +347,24 @@ impl MirPass for AddValidation { }; let release_stmt = Statement { source_info: block_data.statements[i].source_info, - kind: StatementKind::Validate(op, vec![lval_to_operand(src_lval)]), + kind: StatementKind::Validate(op, vec![place_to_operand(src_place)]), }; block_data.statements.insert(i, release_stmt); } // Casts can change what validation does (e.g. unsizing) - StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Consume(_), _)) + StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Copy(_), _)) | + StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Move(_), _)) if kind != CastKind::Misc => { // Due to a lack of NLL; we can't capture anything directly here. // Instead, we have to re-match and clone there. - let (dest_lval, src_lval) = match block_data.statements[i].kind { - StatementKind::Assign(ref dest_lval, - Rvalue::Cast(_, Operand::Consume(ref src_lval), _)) => + let (dest_place, src_place) = match block_data.statements[i].kind { + StatementKind::Assign(ref dest_place, + Rvalue::Cast(_, Operand::Copy(ref src_place), _)) | + StatementKind::Assign(ref dest_place, + Rvalue::Cast(_, Operand::Move(ref src_place), _)) => { - (dest_lval.clone(), src_lval.clone()) + (dest_place.clone(), src_place.clone()) }, _ => bug!("We already matched this."), }; @@ -370,7 +373,7 @@ impl MirPass for AddValidation { let acquire_stmt = Statement { source_info: block_data.statements[i].source_info, kind: StatementKind::Validate(ValidationOp::Acquire, - vec![lval_to_operand(dest_lval)]), + vec![place_to_operand(dest_place)]), }; block_data.statements.insert(i+1, acquire_stmt); @@ -378,7 +381,7 @@ impl MirPass for AddValidation { let release_stmt = Statement { source_info: block_data.statements[i].source_info, kind: StatementKind::Validate(ValidationOp::Release, - vec![lval_to_operand(src_lval)]), + vec![place_to_operand(src_place)]), }; block_data.statements.insert(i, release_stmt); } diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 49ce36223994b..7833f4bbac7aa 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -14,17 +14,16 @@ use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty::maps::Providers; use rustc::ty::{self, TyCtxt}; use rustc::hir; -use rustc::hir::def::Def; use rustc::hir::def_id::DefId; -use rustc::hir::map::{DefPathData, Node}; -use rustc::lint::builtin::{SAFE_EXTERN_STATICS, UNUSED_UNSAFE}; +use rustc::lint::builtin::{SAFE_EXTERN_STATICS, SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; use rustc::mir::*; -use rustc::mir::visit::{LvalueContext, Visitor}; +use rustc::mir::visit::{PlaceContext, Visitor}; use syntax::ast; +use syntax::symbol::Symbol; use std::rc::Rc; - +use util; pub struct UnsafetyChecker<'a, 'tcx: 'a> { mir: &'a Mir<'tcx>, @@ -34,6 +33,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, used_unsafe: FxHashSet, + inherited_blocks: Vec<(ast::NodeId, bool)>, } impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> { @@ -52,6 +52,7 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> { tcx, param_env, used_unsafe: FxHashSet(), + inherited_blocks: vec![], } } } @@ -73,8 +74,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { TerminatorKind::GeneratorDrop | TerminatorKind::Resume | TerminatorKind::Return | - TerminatorKind::Unreachable => { - // safe (at least as emitted during MIR construction) + TerminatorKind::Unreachable | + TerminatorKind::FalseEdges { .. } => { + // safe (at least as emitted during MIR construction) } TerminatorKind::Call { ref func, .. } => { @@ -116,26 +118,46 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { rvalue: &Rvalue<'tcx>, location: Location) { - if let &Rvalue::Aggregate( - box AggregateKind::Closure(def_id, _), - _ - ) = rvalue { - let unsafety_violations = self.tcx.unsafety_violations(def_id); - self.register_violations(&unsafety_violations); + if let &Rvalue::Aggregate(box ref aggregate, _) = rvalue { + match aggregate { + &AggregateKind::Array(..) | + &AggregateKind::Tuple | + &AggregateKind::Adt(..) => {} + &AggregateKind::Closure(def_id, _) | + &AggregateKind::Generator(def_id, _, _) => { + let UnsafetyCheckResult { + violations, unsafe_blocks + } = self.tcx.unsafety_check_result(def_id); + self.register_violations(&violations, &unsafe_blocks); + } + } } self.super_rvalue(rvalue, location); } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - match lvalue { - &Lvalue::Projection(box Projection { + if let PlaceContext::Borrow { .. } = context { + if util::is_disaligned(self.tcx, self.mir, self.param_env, place) { + let source_info = self.source_info; + let lint_root = + self.visibility_scope_info[source_info.scope].lint_root; + self.register_violations(&[UnsafetyViolation { + source_info, + description: Symbol::intern("borrow of packed field").as_str(), + kind: UnsafetyViolationKind::BorrowPacked(lint_root) + }], &[]); + } + } + + match place { + &Place::Projection(box Projection { ref base, ref elem }) => { let old_source_info = self.source_info; - if let &Lvalue::Local(local) = base { + if let &Place::Local(local) = base { if self.mir.local_decls[local].internal { // Internal locals are used in the `move_val_init` desugaring. // We want to check unsafety against the source info of the @@ -148,37 +170,39 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { ty::TyRawPtr(..) => { self.require_unsafe("dereference of raw pointer") } - ty::TyAdt(adt, _) if adt.is_union() => { - if context == LvalueContext::Store || - context == LvalueContext::Drop - { - let elem_ty = match elem { - &ProjectionElem::Field(_, ty) => ty, - _ => span_bug!( - self.source_info.span, - "non-field projection {:?} from union?", - lvalue) - }; - if elem_ty.moves_by_default(self.tcx, self.param_env, - self.source_info.span) { - self.require_unsafe( - "assignment to non-`Copy` union field") + ty::TyAdt(adt, _) => { + if adt.is_union() { + if context == PlaceContext::Store || + context == PlaceContext::Drop + { + let elem_ty = match elem { + &ProjectionElem::Field(_, ty) => ty, + _ => span_bug!( + self.source_info.span, + "non-field projection {:?} from union?", + place) + }; + if elem_ty.moves_by_default(self.tcx, self.param_env, + self.source_info.span) { + self.require_unsafe( + "assignment to non-`Copy` union field") + } else { + // write to non-move union, safe + } } else { - // write to non-move union, safe + self.require_unsafe("access to union field") } - } else { - self.require_unsafe("access to union field") } } _ => {} } self.source_info = old_source_info; } - &Lvalue::Local(..) => { + &Place::Local(..) => { // locals are safe } - &Lvalue::Static(box Static { def_id, ty: _ }) => { - if self.is_static_mut(def_id) { + &Place::Static(box Static { def_id, ty: _ }) => { + if self.tcx.is_static_mut(def_id) { self.require_unsafe("use of mutable static"); } else if self.tcx.is_foreign_item(def_id) { let source_info = self.source_info; @@ -186,76 +210,69 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.visibility_scope_info[source_info.scope].lint_root; self.register_violations(&[UnsafetyViolation { source_info, - description: "use of extern static", - lint_node_id: Some(lint_root) - }]); + description: Symbol::intern("use of extern static").as_str(), + kind: UnsafetyViolationKind::ExternStatic(lint_root) + }], &[]); } } - } - self.super_lvalue(lvalue, context, location); + }; + self.super_place(place, context, location); } } impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { - fn is_static_mut(&self, def_id: DefId) -> bool { - if let Some(node) = self.tcx.hir.get_if_local(def_id) { - match node { - Node::NodeItem(&hir::Item { - node: hir::ItemStatic(_, hir::MutMutable, _), .. - }) => true, - Node::NodeForeignItem(&hir::ForeignItem { - node: hir::ForeignItemStatic(_, mutbl), .. - }) => mutbl, - _ => false - } - } else { - match self.tcx.describe_def(def_id) { - Some(Def::Static(_, mutbl)) => mutbl, - _ => false - } - } - } fn require_unsafe(&mut self, description: &'static str) { let source_info = self.source_info; self.register_violations(&[UnsafetyViolation { - source_info, description, lint_node_id: None - }]); + source_info, + description: Symbol::intern(description).as_str(), + kind: UnsafetyViolationKind::General, + }], &[]); } - fn register_violations(&mut self, violations: &[UnsafetyViolation]) { - match self.visibility_scope_info[self.source_info.scope].safety { + fn register_violations(&mut self, + violations: &[UnsafetyViolation], + unsafe_blocks: &[(ast::NodeId, bool)]) { + let within_unsafe = match self.visibility_scope_info[self.source_info.scope].safety { Safety::Safe => { for violation in violations { if !self.violations.contains(violation) { self.violations.push(violation.clone()) } } + + false } - Safety::BuiltinUnsafe | Safety::FnUnsafe => {} + Safety::BuiltinUnsafe | Safety::FnUnsafe => true, Safety::ExplicitUnsafe(node_id) => { if !violations.is_empty() { self.used_unsafe.insert(node_id); } + true } - } + }; + self.inherited_blocks.extend(unsafe_blocks.iter().map(|&(node_id, is_used)| { + (node_id, is_used && !within_unsafe) + })); } } pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { - unsafety_violations, + unsafety_check_result, + unsafe_derive_on_repr_packed, ..*providers }; } -struct UnusedUnsafeVisitor<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - used_unsafe: FxHashSet +struct UnusedUnsafeVisitor<'a> { + used_unsafe: &'a FxHashSet, + unsafe_blocks: &'a mut Vec<(ast::NodeId, bool)>, } -impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx> { +impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> { fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> { @@ -266,50 +283,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx> hir::intravisit::walk_block(self, block); if let hir::UnsafeBlock(hir::UserProvided) = block.rules { - if !self.used_unsafe.contains(&block.id) { - self.report_unused_unsafe(block); - } + self.unsafe_blocks.push((block.id, self.used_unsafe.contains(&block.id))); } } } -impl<'a, 'tcx> UnusedUnsafeVisitor<'a, 'tcx> { - /// Return the NodeId for an enclosing scope that is also `unsafe` - fn is_enclosed(&self, id: ast::NodeId) -> Option<(String, ast::NodeId)> { - let parent_id = self.tcx.hir.get_parent_node(id); - if parent_id != id { - if self.used_unsafe.contains(&parent_id) { - Some(("block".to_string(), parent_id)) - } else if let Some(hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(_, hir::Unsafety::Unsafe, _, _, _, _), - .. - })) = self.tcx.hir.find(parent_id) { - Some(("fn".to_string(), parent_id)) - } else { - self.is_enclosed(parent_id) - } - } else { - None - } - } - - fn report_unused_unsafe(&self, block: &'tcx hir::Block) { - let mut db = self.tcx.struct_span_lint_node(UNUSED_UNSAFE, - block.id, - block.span, - "unnecessary `unsafe` block"); - db.span_label(block.span, "unnecessary `unsafe` block"); - if let Some((kind, id)) = self.is_enclosed(block.id) { - db.span_note(self.tcx.hir.span(id), - &format!("because it's nested under this `unsafe` {}", kind)); - } - db.emit(); - } -} - fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - used_unsafe: FxHashSet) + used_unsafe: &FxHashSet, + unsafe_blocks: &'a mut Vec<(ast::NodeId, bool)>) { let body_id = tcx.hir.as_local_node_id(def_id).and_then(|node_id| { @@ -327,25 +309,27 @@ fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe); - hir::intravisit::Visitor::visit_body( - &mut UnusedUnsafeVisitor { tcx, used_unsafe }, - body); + let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks }; + hir::intravisit::Visitor::visit_body(&mut visitor, body); } -fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> - Rc<[UnsafetyViolation]> +fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) + -> UnsafetyCheckResult { debug!("unsafety_violations({:?})", def_id); // NB: this borrow is valid because all the consumers of - // `mir_const` force this. - let mir = &tcx.mir_const(def_id).borrow(); + // `mir_built` force this. + let mir = &tcx.mir_built(def_id).borrow(); let visibility_scope_info = match mir.visibility_scope_info { - ClearOnDecode::Set(ref data) => data, - ClearOnDecode::Clear => { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => { debug!("unsafety_violations: {:?} - remote, skipping", def_id); - return Rc::new([]) + return UnsafetyCheckResult { + violations: Rc::new([]), + unsafe_blocks: Rc::new([]) + } } }; @@ -354,34 +338,136 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> mir, visibility_scope_info, tcx, param_env); checker.visit_mir(mir); - check_unused_unsafe(tcx, def_id, checker.used_unsafe); - checker.violations.into() + check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks); + UnsafetyCheckResult { + violations: checker.violations.into(), + unsafe_blocks: checker.inherited_blocks.into() + } +} + +fn unsafe_derive_on_repr_packed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { + let lint_node_id = match tcx.hir.as_local_node_id(def_id) { + Some(node_id) => node_id, + None => bug!("checking unsafety for non-local def id {:?}", def_id) + }; + + // FIXME: when we make this a hard error, this should have its + // own error code. + let message = if !tcx.generics_of(def_id).types.is_empty() { + format!("#[derive] can't be used on a #[repr(packed)] struct with \ + type parameters (error E0133)") + } else { + format!("#[derive] can't be used on a non-Copy #[repr(packed)] struct \ + (error E0133)") + }; + tcx.lint_node(SAFE_PACKED_BORROWS, + lint_node_id, + tcx.def_span(def_id), + &message); +} + +/// Return the NodeId for an enclosing scope that is also `unsafe` +fn is_enclosed(tcx: TyCtxt, + used_unsafe: &FxHashSet, + id: ast::NodeId) -> Option<(String, ast::NodeId)> { + let parent_id = tcx.hir.get_parent_node(id); + if parent_id != id { + if used_unsafe.contains(&parent_id) { + Some(("block".to_string(), parent_id)) + } else if let Some(hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(_, hir::Unsafety::Unsafe, _, _, _, _), + .. + })) = tcx.hir.find(parent_id) { + Some(("fn".to_string(), parent_id)) + } else { + is_enclosed(tcx, used_unsafe, parent_id) + } + } else { + None + } +} + +fn report_unused_unsafe(tcx: TyCtxt, used_unsafe: &FxHashSet, id: ast::NodeId) { + let span = tcx.hir.span(id); + let mut db = tcx.struct_span_lint_node(UNUSED_UNSAFE, id, span, "unnecessary `unsafe` block"); + db.span_label(span, "unnecessary `unsafe` block"); + if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { + db.span_note(tcx.hir.span(id), + &format!("because it's nested under this `unsafe` {}", kind)); + } + db.emit(); +} + +fn builtin_derive_def_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Option { + debug!("builtin_derive_def_id({:?})", def_id); + if let Some(impl_def_id) = tcx.impl_of_method(def_id) { + if tcx.has_attr(impl_def_id, "automatically_derived") { + debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id); + Some(impl_def_id) + } else { + debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id); + None + } + } else { + debug!("builtin_derive_def_id({:?}) - not a method", def_id); + None + } } pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { debug!("check_unsafety({:?})", def_id); - match tcx.def_key(def_id).disambiguated_data.data { - // closures are handled by their parent fn. - DefPathData::ClosureExpr => return, - _ => {} - }; + + // closures are handled by their parent fn. + if tcx.is_closure(def_id) { + return; + } + + let UnsafetyCheckResult { + violations, + unsafe_blocks + } = tcx.unsafety_check_result(def_id); for &UnsafetyViolation { - source_info, description, lint_node_id - } in &*tcx.unsafety_violations(def_id) { + source_info, description, kind + } in violations.iter() { // Report an error. - if let Some(lint_node_id) = lint_node_id { - tcx.lint_node(SAFE_EXTERN_STATICS, - lint_node_id, - source_info.span, - &format!("{} requires unsafe function or \ - block (error E0133)", description)); - } else { - struct_span_err!( - tcx.sess, source_info.span, E0133, - "{} requires unsafe function or block", description) - .span_label(source_info.span, description) - .emit(); + match kind { + UnsafetyViolationKind::General => { + struct_span_err!( + tcx.sess, source_info.span, E0133, + "{} requires unsafe function or block", description) + .span_label(source_info.span, &description[..]) + .emit(); + } + UnsafetyViolationKind::ExternStatic(lint_node_id) => { + tcx.lint_node(SAFE_EXTERN_STATICS, + lint_node_id, + source_info.span, + &format!("{} requires unsafe function or \ + block (error E0133)", &description[..])); + } + UnsafetyViolationKind::BorrowPacked(lint_node_id) => { + if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id) { + tcx.unsafe_derive_on_repr_packed(impl_def_id); + } else { + tcx.lint_node(SAFE_PACKED_BORROWS, + lint_node_id, + source_info.span, + &format!("{} requires unsafe function or \ + block (error E0133)", &description[..])); + } + } + } + } + + let mut unsafe_blocks: Vec<_> = unsafe_blocks.into_iter().collect(); + unsafe_blocks.sort(); + let used_unsafe: FxHashSet<_> = unsafe_blocks.iter() + .flat_map(|&&(id, used)| if used { Some(id) } else { None }) + .collect(); + for &(block_id, is_used) in unsafe_blocks { + if !is_used { + report_unused_unsafe(tcx, &used_unsafe, block_id); } } } diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/clean_end_regions.rs index a6750f400ba93..7986313aa8134 100644 --- a/src/librustc_mir/transform/clean_end_regions.rs +++ b/src/librustc_mir/transform/clean_end_regions.rs @@ -22,10 +22,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc::middle::region; -use rustc::mir::transform::{MirPass, MirSource}; use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind}; -use rustc::mir::visit::{MutVisitor, Visitor, Lookup}; +use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; use rustc::ty::{Ty, RegionKind, TyCtxt}; +use transform::{MirPass, MirSource}; pub struct CleanEndRegions; @@ -67,7 +67,7 @@ impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions { self.super_rvalue(rvalue, location); } - fn visit_ty(&mut self, ty: &Ty<'tcx>, _: Lookup) { + fn visit_ty(&mut self, ty: &Ty<'tcx>, _: TyContext) { // Gather regions that occur in types for re in ty.walk().flat_map(|t| t.regions()) { match *re { diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index ac8ebd306d321..95fe99a1bec9f 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -29,10 +29,11 @@ //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the //! future. -use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind}; -use rustc::mir::transform::{MirPass, MirSource}; +use rustc::hir; +use rustc::mir::{Constant, Local, LocalKind, Location, Place, Mir, Operand, Rvalue, StatementKind}; use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; +use transform::{MirPass, MirSource}; use util::def_use::DefUseAnalysis; pub struct CopyPropagation; @@ -42,25 +43,22 @@ impl MirPass for CopyPropagation { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx>) { - match source { - MirSource::Const(_) => { - // Don't run on constants, because constant qualification might reject the - // optimized IR. - return - } - MirSource::Static(..) | MirSource::Promoted(..) => { - // Don't run on statics and promoted statics, because trans might not be able to - // evaluate the optimized IR. - return - } - MirSource::Fn(function_node_id) => { - if tcx.is_const_fn(tcx.hir.local_def_id(function_node_id)) { + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (_, Some(_)) | + (hir::BodyOwnerKind::Const, _) | + (hir::BodyOwnerKind::Static(_), _) => return, + + (hir::BodyOwnerKind::Fn, _) => { + if tcx.is_const_fn(source.def_id) { // Don't run on const functions, as, again, trans might not be able to evaluate // the optimized IR. return } } - MirSource::GeneratorDrop(_) => (), } // We only run when the MIR optimization level is > 1. @@ -69,10 +67,14 @@ impl MirPass for CopyPropagation { return; } + let mut def_use_analysis = DefUseAnalysis::new(mir); loop { - let mut def_use_analysis = DefUseAnalysis::new(mir); def_use_analysis.analyze(mir); + if eliminate_self_assignments(mir, &def_use_analysis) { + def_use_analysis.analyze(mir); + } + let mut changed = false; for dest_local in mir.local_decls.indices() { debug!("Considering destination local: {:?}", dest_local); @@ -99,10 +101,15 @@ impl MirPass for CopyPropagation { dest_local); continue } - let dest_lvalue_def = dest_use_info.defs_and_uses.iter().filter(|lvalue_def| { - lvalue_def.context.is_mutating_use() && !lvalue_def.context.is_drop() - }).next().unwrap(); - location = dest_lvalue_def.location; + // Conservatively gives up if the dest is an argument, + // because there may be uses of the original argument value. + if mir.local_kind(dest_local) == LocalKind::Arg { + debug!(" Can't copy-propagate local: dest {:?} (argument)", + dest_local); + continue; + } + let dest_place_def = dest_use_info.defs_not_including_drop().next().unwrap(); + location = dest_place_def.location; let basic_block = &mir[location.block]; let statement_index = location.statement_index; @@ -116,11 +123,12 @@ impl MirPass for CopyPropagation { // That use of the source must be an assignment. match statement.kind { - StatementKind::Assign(Lvalue::Local(local), Rvalue::Use(ref operand)) if + StatementKind::Assign(Place::Local(local), Rvalue::Use(ref operand)) if local == dest_local => { let maybe_action = match *operand { - Operand::Consume(ref src_lvalue) => { - Action::local_copy(&mir, &def_use_analysis, src_lvalue) + Operand::Copy(ref src_place) | + Operand::Move(ref src_place) => { + Action::local_copy(&mir, &def_use_analysis, src_place) } Operand::Constant(ref src_constant) => { Action::constant(src_constant) @@ -151,16 +159,53 @@ impl MirPass for CopyPropagation { } } +fn eliminate_self_assignments<'tcx>( + mir: &mut Mir<'tcx>, + def_use_analysis: &DefUseAnalysis<'tcx>, +) -> bool { + let mut changed = false; + + for dest_local in mir.local_decls.indices() { + let dest_use_info = def_use_analysis.local_info(dest_local); + + for def in dest_use_info.defs_not_including_drop() { + let location = def.location; + if let Some(stmt) = mir[location.block].statements.get(location.statement_index) { + match stmt.kind { + StatementKind::Assign( + Place::Local(local), + Rvalue::Use(Operand::Copy(Place::Local(src_local))), + ) | + StatementKind::Assign( + Place::Local(local), + Rvalue::Use(Operand::Move(Place::Local(src_local))), + ) if local == dest_local && dest_local == src_local => {} + _ => { + continue; + } + } + } else { + continue; + } + debug!("Deleting a self-assignment for {:?}", dest_local); + mir.make_statement_nop(location); + changed = true; + } + } + + changed +} + enum Action<'tcx> { PropagateLocalCopy(Local), PropagateConstant(Constant<'tcx>), } impl<'tcx> Action<'tcx> { - fn local_copy(mir: &Mir<'tcx>, def_use_analysis: &DefUseAnalysis, src_lvalue: &Lvalue<'tcx>) + fn local_copy(mir: &Mir<'tcx>, def_use_analysis: &DefUseAnalysis, src_place: &Place<'tcx>) -> Option> { // The source must be a local. - let src_local = if let Lvalue::Local(local) = *src_lvalue { + let src_local = if let Place::Local(local) = *src_place { local } else { debug!(" Can't copy-propagate local: source is not a local"); @@ -194,10 +239,13 @@ impl<'tcx> Action<'tcx> { // USE(SRC); let src_def_count = src_use_info.def_count_not_including_drop(); // allow function arguments to be propagated - if src_def_count > 1 || - (src_def_count == 0 && mir.local_kind(src_local) != LocalKind::Arg) { - debug!(" Can't copy-propagate local: {} defs of src", - src_use_info.def_count_not_including_drop()); + let is_arg = mir.local_kind(src_local) == LocalKind::Arg; + if (is_arg && src_def_count != 0) || (!is_arg && src_def_count != 1) { + debug!( + " Can't copy-propagate local: {} defs of src{}", + src_def_count, + if is_arg { " (argument)" } else { "" }, + ); return None } @@ -224,14 +272,14 @@ impl<'tcx> Action<'tcx> { debug!(" Replacing all uses of {:?} with {:?} (local)", dest_local, src_local); - for lvalue_use in &def_use_analysis.local_info(dest_local).defs_and_uses { - if lvalue_use.context.is_storage_marker() { - mir.make_statement_nop(lvalue_use.location) + for place_use in &def_use_analysis.local_info(dest_local).defs_and_uses { + if place_use.context.is_storage_marker() { + mir.make_statement_nop(place_use.location) } } - for lvalue_use in &def_use_analysis.local_info(src_local).defs_and_uses { - if lvalue_use.context.is_storage_marker() { - mir.make_statement_nop(lvalue_use.location) + for place_use in &def_use_analysis.local_info(src_local).defs_and_uses { + if place_use.context.is_storage_marker() { + mir.make_statement_nop(place_use.location) } } @@ -252,22 +300,22 @@ impl<'tcx> Action<'tcx> { dest_local, src_constant); let dest_local_info = def_use_analysis.local_info(dest_local); - for lvalue_use in &dest_local_info.defs_and_uses { - if lvalue_use.context.is_storage_marker() { - mir.make_statement_nop(lvalue_use.location) + for place_use in &dest_local_info.defs_and_uses { + if place_use.context.is_storage_marker() { + mir.make_statement_nop(place_use.location) } } // Replace all uses of the destination local with the constant. let mut visitor = ConstantPropagationVisitor::new(dest_local, src_constant); - for dest_lvalue_use in &dest_local_info.defs_and_uses { - visitor.visit_location(mir, dest_lvalue_use.location) + for dest_place_use in &dest_local_info.defs_and_uses { + visitor.visit_location(mir, dest_place_use.location) } // Zap the assignment instruction if we eliminated all the uses. We won't have been // able to do that if the destination was used in a projection, because projections - // must have lvalues on their LHS. + // must have places on their LHS. let use_count = dest_local_info.use_count(); if visitor.uses_replaced == use_count { debug!(" {} of {} use(s) replaced; deleting assignment", @@ -311,7 +359,8 @@ impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> { self.super_operand(operand, location); match *operand { - Operand::Consume(Lvalue::Local(local)) if local == self.dest_local => {} + Operand::Copy(Place::Local(local)) | + Operand::Move(Place::Local(local)) if local == self.dest_local => {} _ => return, } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index d21dbeafb5d0a..eccb0d231b89d 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::hir; use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; use rustc_data_structures::indexed_vec::Idx; +use transform::{MirPass, MirSource}; pub struct Deaggregator; @@ -20,16 +21,21 @@ impl MirPass for Deaggregator { tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx>) { - let node_id = source.item_id(); - let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id)); + let node_path = tcx.item_path_str(source.def_id); debug!("running on: {:?}", node_path); // we only run when mir_opt_level > 2 if tcx.sess.opts.debugging_opts.mir_opt_level <= 2 { return; } - // Do not trigger on constants. Could be revised in future - if let MirSource::Fn(_) = source {} else { return; } + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (hir::BodyOwnerKind::Fn, None) => {}, + _ => return + } // In fact, we might not want to trigger in other cases. // Ex: when we could use SROA. See issue #35259 @@ -61,8 +67,8 @@ impl MirPass for Deaggregator { let ty = variant_def.fields[i].ty(tcx, substs); let rhs = Rvalue::Use(op.clone()); - let lhs_cast = if adt_def.variants.len() > 1 { - Lvalue::Projection(Box::new(LvalueProjection { + let lhs_cast = if adt_def.is_enum() { + Place::Projection(Box::new(PlaceProjection { base: lhs.clone(), elem: ProjectionElem::Downcast(adt_def, variant), })) @@ -70,7 +76,7 @@ impl MirPass for Deaggregator { lhs.clone() }; - let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection { + let lhs_proj = Place::Projection(Box::new(PlaceProjection { base: lhs_cast, elem: ProjectionElem::Field(Field::new(i), ty), })); @@ -83,10 +89,10 @@ impl MirPass for Deaggregator { } // if the aggregate was an enum, we need to set the discriminant - if adt_def.variants.len() > 1 { + if adt_def.is_enum() { let set_discriminant = Statement { kind: StatementKind::SetDiscriminant { - lvalue: lhs.clone(), + place: lhs.clone(), variant_index: variant, }, source_info: src_info, diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 67a3281dba48b..98753eaa5a354 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -16,9 +16,9 @@ use std::fs::File; use std::io; use rustc::mir::Mir; -use rustc::mir::transform::{MirPass, MirPassIndex, MirSource, MirSuite, PassHook}; use rustc::session::config::{OutputFilenames, OutputType}; use rustc::ty::TyCtxt; +use transform::{MirPass, MirSource}; use util as mir_util; pub struct Marker(pub &'static str); @@ -47,26 +47,21 @@ impl fmt::Display for Disambiguator { } } -pub struct DumpMir; -impl PassHook for DumpMir { - fn on_mir_pass<'a, 'tcx: 'a>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - suite: MirSuite, - pass_num: MirPassIndex, - pass_name: &str, - source: MirSource, - mir: &Mir<'tcx>, - is_after: bool) - { - if mir_util::dump_enabled(tcx, pass_name, source) { - mir_util::dump_mir(tcx, - Some((suite, pass_num)), - pass_name, - &Disambiguator { is_after }, - source, - mir); - } +pub fn on_mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + pass_num: &fmt::Display, + pass_name: &str, + source: MirSource, + mir: &Mir<'tcx>, + is_after: bool) { + if mir_util::dump_enabled(tcx, pass_name, source) { + mir_util::dump_mir(tcx, + Some(pass_num), + pass_name, + &Disambiguator { is_after }, + source, + mir, + |_, _| Ok(()) ); } } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index c833904adbaea..b075d2637da9b 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -15,13 +15,14 @@ use dataflow::{on_all_children_bits, on_all_drop_children_bits}; use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits}; use dataflow::MoveDataParamEnv; use dataflow; +use rustc::hir; use rustc::ty::{self, TyCtxt}; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; use rustc::middle::const_val::ConstVal; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; +use transform::{MirPass, MirSource}; use util::patch::MirPatch; use util::elaborate_drops::{DropFlagState, Unwind, elaborate_drop}; use util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode}; @@ -39,13 +40,17 @@ impl MirPass for ElaborateDrops { mir: &mut Mir<'tcx>) { debug!("elaborate_drops({:?} @ {:?})", src, mir.span); - match src { - MirSource::Fn(..) => {}, + + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(src.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), src.promoted) { + (hir::BodyOwnerKind::Fn, None) => {}, _ => return } - let id = src.item_id(); - let param_env = tcx.param_env(tcx.hir.local_def_id(id)); - let move_data = MoveData::gather_moves(mir, tcx, param_env); + let param_env = tcx.param_env(src.def_id); + let move_data = MoveData::gather_moves(mir, tcx).unwrap(); let elaborate_patch = { let mir = &*mir; let env = MoveDataParamEnv { @@ -83,7 +88,7 @@ fn find_dead_unwinds<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, id: ast::NodeId, - env: &MoveDataParamEnv<'tcx>) + env: &MoveDataParamEnv<'tcx, 'tcx>) -> IdxSetBuf { debug!("find_dead_unwinds({:?})", mir.span); @@ -146,7 +151,7 @@ impl InitializationData { fn apply_location<'a,'tcx>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - env: &MoveDataParamEnv<'tcx>, + env: &MoveDataParamEnv<'tcx, 'tcx>, loc: Location) { drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| { @@ -252,6 +257,20 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { }) } + fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { + match p { + &Projection { + elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false}, .. + } => offset == index, + &Projection { + elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true}, .. + } => size - offset == index, + _ => false + } + }) + } + fn deref_subpath(&self, path: Self::Path) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { match p { @@ -273,16 +292,16 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { } fn get_drop_flag(&mut self, path: Self::Path) -> Option> { - self.ctxt.drop_flag(path).map(Operand::Consume) + self.ctxt.drop_flag(path).map(Operand::Copy) } } struct ElaborateDropsCtxt<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, - env: &'a MoveDataParamEnv<'tcx>, - flow_inits: DataflowResults>, - flow_uninits: DataflowResults>, + env: &'a MoveDataParamEnv<'tcx, 'tcx>, + flow_inits: DataflowResults>, + flow_uninits: DataflowResults>, drop_flags: FxHashMap, patch: MirPatch<'tcx>, } @@ -317,8 +336,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }); } - fn drop_flag(&mut self, index: MovePathIndex) -> Option> { - self.drop_flags.get(&index).map(|t| Lvalue::Local(*t)) + fn drop_flag(&mut self, index: MovePathIndex) -> Option> { + self.drop_flags.get(&index).map(|t| Place::Local(*t)) } /// create a patch that elaborates all drops in the input @@ -353,7 +372,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }); let path = self.move_data().rev_lookup.find(location); - debug!("collect_drop_flags: {:?}, lv {:?} ({:?})", + debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, location, path); let path = match path { @@ -363,7 +382,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let (_maybe_live, maybe_dead) = init_data.state(parent); if maybe_dead { span_bug!(terminator.source_info.span, - "drop of untracked, uninitialized value {:?}, lv {:?} ({:?})", + "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", bb, location, path); } continue @@ -438,7 +457,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { /// The desugaring drops the location if needed, and then writes /// the value (including setting the drop flag) over it in *both* arms. /// - /// The `replace` terminator can also be called on lvalues that + /// The `replace` terminator can also be called on places that /// are not tracked by elaboration (for example, /// `replace x[i] <- tmp0`). The borrow checker requires that /// these locations are initialized before the assignment, @@ -446,7 +465,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn elaborate_replace( &mut self, loc: Location, - location: &Lvalue<'tcx>, + location: &Place<'tcx>, value: &Operand<'tcx>, target: BasicBlock, unwind: Option) @@ -533,7 +552,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { if let Some(&flag) = self.drop_flags.get(&path) { let span = self.patch.source_info_for_location(self.mir, loc).span; let val = self.constant_bool(span, val.value()); - self.patch.add_assign(loc, Lvalue::Local(flag), val); + self.patch.add_assign(loc, Place::Local(flag), val); } } @@ -542,19 +561,19 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let span = self.patch.source_info_for_location(self.mir, loc).span; let false_ = self.constant_bool(span, false); for flag in self.drop_flags.values() { - self.patch.add_assign(loc, Lvalue::Local(*flag), false_.clone()); + self.patch.add_assign(loc, Place::Local(*flag), false_.clone()); } } fn drop_flags_for_fn_rets(&mut self) { for (bb, data) in self.mir.basic_blocks().iter_enumerated() { if let TerminatorKind::Call { - destination: Some((ref lv, tgt)), cleanup: Some(_), .. + destination: Some((ref place, tgt)), cleanup: Some(_), .. } = data.terminator().kind { assert!(!self.patch.is_patched(bb)); let loc = Location { block: tgt, statement_index: 0 }; - let path = self.move_data().rev_lookup.find(lv); + let path = self.move_data().rev_lookup.find(place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) @@ -623,12 +642,12 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { // so mark the return as initialized *before* the // call. if let TerminatorKind::Call { - destination: Some((ref lv, _)), cleanup: None, .. + destination: Some((ref place, _)), cleanup: None, .. } = data.terminator().kind { assert!(!self.patch.is_patched(bb)); let loc = Location { block: bb, statement_index: data.statements.len() }; - let path = self.move_data().rev_lookup.find(lv); + let path = self.move_data().rev_lookup.find(place); on_lookup_result_bits( self.tcx, self.mir, self.move_data(), path, |child| self.set_drop_flag(loc, child, DropFlagState::Present) diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index dc18cdd8f0dd6..dfa048e2e4bcf 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -17,8 +17,8 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use rustc::mir::*; -use rustc::mir::visit::{MutVisitor, Lookup}; -use rustc::mir::transform::{MirPass, MirSource}; +use rustc::mir::visit::{MutVisitor, TyContext}; +use transform::{MirPass, MirSource}; struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -35,7 +35,7 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { } impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) { + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { if !self.in_validation_statement { *ty = self.tcx.erase_regions(ty); } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 729fe46ef37ec..aaa28634eb82c 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -63,12 +63,11 @@ use rustc::hir; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; -use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor}; +use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior}; use rustc::ty::subst::{Kind, Substs}; use util::dump_mir; -use util::liveness; +use util::liveness::{self, LivenessMode}; use rustc_const_math::ConstInt; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; @@ -76,6 +75,7 @@ use std::collections::HashMap; use std::borrow::Cow; use std::iter::once; use std::mem; +use transform::{MirPass, MirSource}; use transform::simplify; use transform::no_landing_pads::no_landing_pads; use dataflow::{self, MaybeStorageLive, state_for_location}; @@ -90,7 +90,7 @@ struct RenameLocalVisitor { impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor { fn visit_local(&mut self, local: &mut Local, - _: LvalueContext<'tcx>, + _: PlaceContext<'tcx>, _: Location) { if *local == self.from { *local = self.to; @@ -103,22 +103,22 @@ struct DerefArgVisitor; impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor { fn visit_local(&mut self, local: &mut Local, - _: LvalueContext<'tcx>, + _: PlaceContext<'tcx>, _: Location) { assert_ne!(*local, self_arg()); } - fn visit_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &mut Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - if *lvalue == Lvalue::Local(self_arg()) { - *lvalue = Lvalue::Projection(Box::new(Projection { - base: lvalue.clone(), + if *place == Place::Local(self_arg()) { + *place = Place::Projection(Box::new(Projection { + base: place.clone(), elem: ProjectionElem::Deref, })); } else { - self.super_lvalue(lvalue, context, location); + self.super_place(place, context, location); } } } @@ -151,7 +151,7 @@ struct TransformVisitor<'a, 'tcx: 'a> { // A list of suspension points, generated during the transform suspension_points: Vec, - // The original RETURN_POINTER local + // The original RETURN_PLACE local new_ret_local: Local, } @@ -162,14 +162,14 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { Rvalue::Aggregate(box adt, vec![val]) } - // Create a Lvalue referencing a generator struct field - fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Lvalue<'tcx> { - let base = Lvalue::Local(self_arg()); + // Create a Place referencing a generator struct field + fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> { + let base = Place::Local(self_arg()); let field = Projection { base: base, elem: ProjectionElem::Field(Field::new(idx), ty), }; - Lvalue::Projection(Box::new(field)) + Place::Projection(Box::new(field)) } // Create a statement which changes the generator state @@ -195,22 +195,22 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &mut Local, - _: LvalueContext<'tcx>, + _: PlaceContext<'tcx>, _: Location) { assert_eq!(self.remap.get(local), None); } - fn visit_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &mut Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - if let Lvalue::Local(l) = *lvalue { + if let Place::Local(l) = *place { // Replace an Local in the remap with a generator struct access if let Some(&(ty, idx)) = self.remap.get(&l) { - *lvalue = self.make_field(idx, ty); + *place = self.make_field(idx, ty); } } else { - self.super_lvalue(lvalue, context, location); + self.super_place(place, context, location); } } @@ -230,7 +230,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { let ret_val = match data.terminator().kind { TerminatorKind::Return => Some((1, None, - Operand::Consume(Lvalue::Local(self.new_ret_local)), + Operand::Move(Place::Local(self.new_ret_local)), None)), TerminatorKind::Yield { ref value, resume, drop } => Some((0, Some(resume), @@ -244,7 +244,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { // We must assign the value first in case it gets declared dead below data.statements.push(Statement { source_info, - kind: StatementKind::Assign(Lvalue::Local(RETURN_POINTER), + kind: StatementKind::Assign(Place::Local(RETURN_PLACE), self.make_state(state_idx, v)), }); let state = if let Some(resume) = resume { // Yield @@ -310,7 +310,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>, mir.local_decls.swap(0, new_ret_local.index()); RenameLocalVisitor { - from: RETURN_POINTER, + from: RETURN_PLACE, to: new_ret_local, }.visit_mir(mir); @@ -338,7 +338,7 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (liveness::LocalSet, HashMap) { let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let node_id = source.item_id(); + let node_id = tcx.hir.as_local_node_id(source.def_id).unwrap(); let analysis = MaybeStorageLive::new(mir); let storage_live = dataflow::do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis, @@ -348,7 +348,10 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ignored.visit_mir(mir); let mut set = liveness::LocalSet::new_empty(mir.local_decls.len()); - let liveness = liveness::liveness_of_locals(mir); + let liveness = liveness::liveness_of_locals(mir, LivenessMode { + include_regular_use: true, + include_drops: true, + }); liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness); let mut storage_liveness_map = HashMap::new(); @@ -449,7 +452,7 @@ fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let default_block = insert_term_block(mir, default); let switch = TerminatorKind::SwitchInt { - discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)), + discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)), switch_ty: tcx.types.u32, values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::>()), targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(), @@ -491,7 +494,7 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &Terminator { source_info, kind: TerminatorKind::Drop { - location: Lvalue::Local(local), + location: Place::Local(local), target, unwind } @@ -513,7 +516,7 @@ fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, elaborate_drop( &mut elaborator, source_info, - &Lvalue::Local(gen), + &Place::Local(gen), (), target, unwind, @@ -554,8 +557,7 @@ fn create_generator_drop_shim<'a, 'tcx>( } // Replace the return variable - mir.return_ty = tcx.mk_nil(); - mir.local_decls[RETURN_POINTER] = LocalDecl { + mir.local_decls[RETURN_PLACE] = LocalDecl { mutability: Mutability::Mut, ty: tcx.mk_nil(), name: None, @@ -587,7 +589,7 @@ fn create_generator_drop_shim<'a, 'tcx>( // unrelated code from the resume part of the function simplify::remove_dead_blocks(&mut mir); - dump_mir(tcx, None, "generator_drop", &0, source, &mut mir); + dump_mir(tcx, None, "generator_drop", &0, source, &mut mir, |_, _| Ok(()) ); mir } @@ -673,7 +675,7 @@ fn create_generator_resume_function<'a, 'tcx>( // unrelated code from the drop part of the function simplify::remove_dead_blocks(mir); - dump_mir(tcx, None, "generator_resume", &0, source, mir); + dump_mir(tcx, None, "generator_resume", &0, source, mir, |_, _| Ok(()) ); } fn source_info<'a, 'tcx>(mir: &Mir<'tcx>) -> SourceInfo { @@ -689,7 +691,7 @@ fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock { // Create a block to destroy an unresumed generators. This can only destroy upvars. let drop_clean = BasicBlock::new(mir.basic_blocks().len()); let term = TerminatorKind::Drop { - location: Lvalue::Local(self_arg()), + location: Place::Local(self_arg()), target: return_block, unwind: None, }; @@ -760,12 +762,16 @@ impl MirPass for StateTransform { assert!(mir.generator_drop.is_none()); - let node_id = source.item_id(); - let def_id = tcx.hir.local_def_id(source.item_id()); + let def_id = source.def_id; + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); let hir_id = tcx.hir.node_to_hir_id(node_id); // Get the interior types which typeck computed - let interior = *tcx.typeck_tables_of(def_id).generator_interiors().get(hir_id).unwrap(); + let tables = tcx.typeck_tables_of(def_id); + let interior = match tables.node_id_to_type(hir_id).sty { + ty::TyGenerator(_, _, interior) => interior, + ref t => bug!("type of generator not a generator: {:?}", t), + }; // The first argument is the generator type passed by value let gen_ty = mir.local_decls.raw[1].ty; @@ -774,11 +780,11 @@ impl MirPass for StateTransform { let state_did = tcx.lang_items().gen_state().unwrap(); let state_adt_ref = tcx.adt_def(state_did); let state_substs = tcx.mk_substs([Kind::from(yield_ty), - Kind::from(mir.return_ty)].iter()); + Kind::from(mir.return_ty())].iter()); let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - // We rename RETURN_POINTER which has type mir.return_ty to new_ret_local - // RETURN_POINTER then is a fresh unused local with type ret_ty. + // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local + // RETURN_PLACE then is a fresh unused local with type ret_ty. let new_ret_local = replace_result_variable(ret_ty, mir); // Extract locals which are live across suspension point into `layout` @@ -788,7 +794,7 @@ impl MirPass for StateTransform { let state_field = mir.upvar_decls.len(); - // Run the transformation which converts Lvalues from Local to generator struct + // Run the transformation which converts Places from Local to generator struct // accesses for locals in `remap`. // It also rewrites `return x` and `yield y` as writing a new generator state and returning // GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively. @@ -805,7 +811,6 @@ impl MirPass for StateTransform { transform.visit_mir(mir); // Update our MIR struct to reflect the changed we've made - mir.return_ty = ret_ty; mir.yield_ty = None; mir.arg_count = 1; mir.spread_arg = None; @@ -816,14 +821,14 @@ impl MirPass for StateTransform { // This is expanded to a drop ladder in `elaborate_generator_drops`. let drop_clean = insert_clean_drop(mir); - dump_mir(tcx, None, "generator_pre-elab", &0, source, mir); + dump_mir(tcx, None, "generator_pre-elab", &0, source, mir, |_, _| Ok(()) ); // Expand `drop(generator_struct)` to a drop ladder which destroys upvars. // If any upvars are moved out of, drop elaboration will handle upvar destruction. // However we need to also elaborate the code generated by `insert_clean_drop`. elaborate_generator_drops(tcx, def_id, mir); - dump_mir(tcx, None, "generator_post-transform", &0, source, mir); + dump_mir(tcx, None, "generator_post-transform", &0, source, mir, |_, _| Ok(()) ); // Create a copy of our MIR and use it to create the drop shim for the generator let drop_shim = create_generator_drop_shim(tcx, diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 48a21dfdbd467..7216384795278 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -10,18 +10,21 @@ //! Inlining pass for MIR functions +use rustc::hir; use rustc::hir::def_id::DefId; use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; use rustc::mir::visit::*; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::layout::LayoutOf; use rustc::ty::subst::{Subst,Substs}; use std::collections::VecDeque; +use std::iter; +use transform::{MirPass, MirSource}; use super::simplify::{remove_dead_blocks, CfgSimplifier}; use syntax::{attr}; @@ -37,7 +40,7 @@ const UNKNOWN_SIZE_COST: usize = 10; pub struct Inline; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] struct CallSite<'tcx> { callee: DefId, substs: &'tcx Substs<'tcx>, @@ -77,8 +80,13 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let mut callsites = VecDeque::new(); + let param_env = self.tcx.param_env(self.source.def_id); + // Only do inlining into fn bodies. - if let MirSource::Fn(_) = self.source { + let id = self.tcx.hir.as_local_node_id(self.source.def_id).unwrap(); + let body_owner_kind = self.tcx.hir.body_owner_kind(id); + if let (hir::BodyOwnerKind::Fn, None) = (body_owner_kind, self.source.promoted) { + for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated() { // Don't inline calls that are in cleanup blocks. if bb_data.is_cleanup { continue; } @@ -87,18 +95,23 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let terminator = bb_data.terminator(); if let TerminatorKind::Call { func: Operand::Constant(ref f), .. } = terminator.kind { - if let ty::TyFnDef(callee_def_id, substs) = f.ty.sty { - if self.tcx.trait_of_item(callee_def_id).is_none() { - callsites.push_back(CallSite { - callee: callee_def_id, - substs, - bb, - location: terminator.source_info - }); + if let ty::TyFnDef(callee_def_id, substs) = f.ty.sty { + if let Some(instance) = Instance::resolve(self.tcx, + param_env, + callee_def_id, + substs) { + callsites.push_back(CallSite { + callee: instance.def_id(), + substs: instance.substs, + bb, + location: terminator.source_info + }); + } } } - } } + } else { + return; } let mut local_change; @@ -107,7 +120,9 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { loop { local_change = false; while let Some(callsite) = callsites.pop_front() { + debug!("checking whether to inline callsite {:?}", callsite); if !self.tcx.is_mir_available(callsite.callee) { + debug!("checking whether to inline callsite {:?} - MIR unavailable", callsite); continue; } @@ -115,7 +130,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { callsite.location.span, callsite.callee) { Ok(ref callee_mir) if self.should_inline(callsite, callee_mir) => { - callee_mir.subst(self.tcx, callsite.substs) + subst_and_normalize(callee_mir, self.tcx, &callsite.substs, param_env) } Ok(_) => continue, @@ -127,10 +142,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; let start = caller_mir.basic_blocks().len(); - + debug!("attempting to inline callsite {:?} - mir={:?}", callsite, callee_mir); if !self.inline_call(callsite, caller_mir, callee_mir) { + debug!("attempting to inline callsite {:?} - failure", callsite); continue; } + debug!("attempting to inline callsite {:?} - success", callsite); // Add callsites from inlined function for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated().skip(start) { @@ -174,16 +191,19 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { callee_mir: &Mir<'tcx>) -> bool { + debug!("should_inline({:?})", callsite); let tcx = self.tcx; // Don't inline closures that have captures // FIXME: Handle closures better if callee_mir.upvar_decls.len() > 0 { + debug!(" upvar decls present - not inlining"); return false; } // Cannot inline generators which haven't been transformed yet if callee_mir.yield_ty.is_some() { + debug!(" yield ty present - not inlining"); return false; } @@ -195,7 +215,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // there are cases that prevent inlining that we // need to check for first. attr::InlineAttr::Always => true, - attr::InlineAttr::Never => return false, + attr::InlineAttr::Never => { + debug!("#[inline(never)] present - not inlining"); + return false + } attr::InlineAttr::Hint => true, attr::InlineAttr::None => false, }; @@ -205,6 +228,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // reference unexported symbols if callsite.callee.is_local() { if callsite.substs.types().count() == 0 && !hinted { + debug!(" callee is an exported function - not inlining"); return false; } } @@ -226,11 +250,11 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { if callee_mir.basic_blocks().len() <= 3 { threshold += threshold / 4; } + debug!(" final inline threshold = {}", threshold); // FIXME: Give a bonus to functions with only a single caller - let def_id = tcx.hir.local_def_id(self.source.item_id()); - let param_env = tcx.param_env(def_id); + let param_env = tcx.param_env(self.source.def_id); let mut first_block = true; let mut cost = 0; @@ -321,12 +345,17 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } } - debug!("Inline cost for {:?} is {}", callsite.callee, cost); - if let attr::InlineAttr::Always = hint { + debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost); true } else { - cost <= threshold + if cost <= threshold { + debug!("INLINING {:?} [cost={} <= threshold={}]", callsite, cost, threshold); + true + } else { + debug!("NOT inlining {:?} [cost={} > threshold={}]", callsite, cost, threshold); + false + } } } @@ -375,12 +404,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // If the call is something like `a[*i] = f(i)`, where // `i : &mut usize`, then just duplicating the `a[*i]` - // Lvalue could result in two different locations if `f` + // Place could result in two different locations if `f` // writes to `i`. To prevent this we need to create a temporary - // borrow of the lvalue and pass the destination as `*temp` instead. - fn dest_needs_borrow(lval: &Lvalue) -> bool { - match *lval { - Lvalue::Projection(ref p) => { + // borrow of the place and pass the destination as `*temp` instead. + fn dest_needs_borrow(place: &Place) -> bool { + match *place { + Place::Projection(ref p) => { match p.elem { ProjectionElem::Deref | ProjectionElem::Index(_) => true, @@ -389,7 +418,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } // Static variables need a borrow because the callee // might modify the same static. - Lvalue::Static(_) => true, + Place::Static(_) => true, _ => false } } @@ -406,7 +435,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let temp = LocalDecl::new_temp(ty, callsite.location.span); let tmp = caller_mir.local_decls.push(temp); - let tmp = Lvalue::Local(tmp); + let tmp = Place::Local(tmp); let stmt = Statement { source_info: callsite.location, @@ -427,8 +456,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // needs to generate the cast. // FIXME: we should probably just generate correct MIR in the first place... - let arg = if let Operand::Consume(ref lval) = args[0] { - lval.clone() + let arg = if let Operand::Move(ref place) = args[0] { + place.clone() } else { bug!("Constant arg to \"box_free\""); }; @@ -479,8 +508,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } } - fn cast_box_free_arg(&self, arg: Lvalue<'tcx>, ptr_ty: Ty<'tcx>, - callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Operand<'tcx> { + fn cast_box_free_arg(&self, arg: Place<'tcx>, ptr_ty: Ty<'tcx>, + callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Local { let arg = Rvalue::Ref( self.tcx.types.re_erased, BorrowKind::Mut, @@ -489,7 +518,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let ty = arg.ty(caller_mir, self.tcx); let ref_tmp = LocalDecl::new_temp(ty, callsite.location.span); let ref_tmp = caller_mir.local_decls.push(ref_tmp); - let ref_tmp = Lvalue::Local(ref_tmp); + let ref_tmp = Place::Local(ref_tmp); let ref_stmt = Statement { source_info: callsite.location, @@ -506,62 +535,151 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; let ptr_ty = self.tcx.mk_mut_ptr(pointee_ty); - let raw_ptr = Rvalue::Cast(CastKind::Misc, Operand::Consume(ref_tmp), ptr_ty); + let raw_ptr = Rvalue::Cast(CastKind::Misc, Operand::Move(ref_tmp), ptr_ty); let cast_tmp = LocalDecl::new_temp(ptr_ty, callsite.location.span); let cast_tmp = caller_mir.local_decls.push(cast_tmp); - let cast_tmp = Lvalue::Local(cast_tmp); let cast_stmt = Statement { source_info: callsite.location, - kind: StatementKind::Assign(cast_tmp.clone(), raw_ptr) + kind: StatementKind::Assign(Place::Local(cast_tmp), raw_ptr) }; caller_mir[callsite.bb] .statements.push(cast_stmt); - Operand::Consume(cast_tmp) + cast_tmp } - fn make_call_args(&self, args: Vec>, - callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Vec> { + fn make_call_args( + &self, + args: Vec>, + callsite: &CallSite<'tcx>, + caller_mir: &mut Mir<'tcx>, + ) -> Vec { let tcx = self.tcx; + + // There is a bit of a mismatch between the *caller* of a closure and the *callee*. + // The caller provides the arguments wrapped up in a tuple: + // + // tuple_tmp = (a, b, c) + // Fn::call(closure_ref, tuple_tmp) + // + // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) + // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, trans has + // the job of unpacking this tuple. But here, we are trans. =) So we want to create + // a vector like + // + // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] + // + // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient + // if we "spill" that into *another* temporary, so that we can map the argument + // variable in the callee MIR directly to an argument variable on our side. + // So we introduce temporaries like: + // + // tmp0 = tuple_tmp.0 + // tmp1 = tuple_tmp.1 + // tmp2 = tuple_tmp.2 + // + // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. + if tcx.is_closure(callsite.callee) { + let mut args = args.into_iter(); + let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir); + let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir); + assert!(args.next().is_none()); + + let tuple = Place::Local(tuple); + let tuple_tys = if let ty::TyTuple(s, _) = tuple.ty(caller_mir, tcx).to_ty(tcx).sty { + s + } else { + bug!("Closure arguments are not passed as a tuple"); + }; + + // The `closure_ref` in our example above. + let closure_ref_arg = iter::once(self_); + + // The `tmp0`, `tmp1`, and `tmp2` in our example abonve. + let tuple_tmp_args = + tuple_tys.iter().enumerate().map(|(i, ty)| { + // This is e.g. `tuple_tmp.0` in our example above. + let tuple_field = Operand::Move(tuple.clone().field(Field::new(i), ty)); + + // Spill to a local to make e.g. `tmp0`. + self.create_temp_if_necessary(tuple_field, callsite, caller_mir) + }); + + closure_ref_arg.chain(tuple_tmp_args).collect() + } else { + args.into_iter() + .map(|a| self.create_temp_if_necessary(a, callsite, caller_mir)) + .collect() + } + } + + /// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh + /// temporary `T` and an instruction `T = arg`, and returns `T`. + fn create_temp_if_necessary( + &self, + arg: Operand<'tcx>, + callsite: &CallSite<'tcx>, + caller_mir: &mut Mir<'tcx>, + ) -> Local { // FIXME: Analysis of the usage of the arguments to avoid // unnecessary temporaries. - args.into_iter().map(|a| { - if let Operand::Consume(Lvalue::Local(local)) = a { - if caller_mir.local_kind(local) == LocalKind::Temp { - // Reuse the operand if it's a temporary already - return a; - } + + if let Operand::Move(Place::Local(local)) = arg { + if caller_mir.local_kind(local) == LocalKind::Temp { + // Reuse the operand if it's a temporary already + return local; } + } - debug!("Creating temp for argument"); - // Otherwise, create a temporary for the arg - let arg = Rvalue::Use(a); + debug!("Creating temp for argument {:?}", arg); + // Otherwise, create a temporary for the arg + let arg = Rvalue::Use(arg); - let ty = arg.ty(caller_mir, tcx); + let ty = arg.ty(caller_mir, self.tcx); - let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span); - let arg_tmp = caller_mir.local_decls.push(arg_tmp); - let arg_tmp = Lvalue::Local(arg_tmp); + let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span); + let arg_tmp = caller_mir.local_decls.push(arg_tmp); - let stmt = Statement { - source_info: callsite.location, - kind: StatementKind::Assign(arg_tmp.clone(), arg) - }; - caller_mir[callsite.bb].statements.push(stmt); - Operand::Consume(arg_tmp) - }).collect() + let stmt = Statement { + source_info: callsite.location, + kind: StatementKind::Assign(Place::Local(arg_tmp), arg), + }; + caller_mir[callsite.bb].statements.push(stmt); + arg_tmp } } fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option { - ty.layout(tcx, param_env).ok().map(|layout| { - layout.size(&tcx.data_layout).bytes() - }) + (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes()) +} + +fn subst_and_normalize<'a, 'tcx: 'a>( + mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + substs: &'tcx ty::subst::Substs<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Mir<'tcx> { + struct Folder<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + substs: &'tcx ty::subst::Substs<'tcx>, + } + impl<'a, 'tcx: 'a> ty::fold::TypeFolder<'tcx, 'tcx> for Folder<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + self.tcx.trans_apply_param_substs_env(&self.substs, self.param_env, &t) + } + } + let mut f = Folder { tcx, param_env, substs }; + mir.fold_with(&mut f) } /** @@ -573,12 +691,12 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, */ struct Integrator<'a, 'tcx: 'a> { block_idx: usize, - args: &'a [Operand<'tcx>], + args: &'a [Local], local_map: IndexVec, scope_map: IndexVec, promoted_map: IndexVec, _callsite: CallSite<'tcx>, - destination: Lvalue<'tcx>, + destination: Place<'tcx>, return_block: BasicBlock, cleanup_block: Option, in_cleanup_block: bool, @@ -590,65 +708,40 @@ impl<'a, 'tcx> Integrator<'a, 'tcx> { debug!("Updating target `{:?}`, new: `{:?}`", tgt, new); new } - - fn arg_index(&self, arg: Local) -> Option { - let idx = arg.index(); - if idx > 0 && idx <= self.args.len() { - Some(idx - 1) - } else { - None - } - } } impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { fn visit_local(&mut self, local: &mut Local, - _ctxt: LvalueContext<'tcx>, + _ctxt: PlaceContext<'tcx>, _location: Location) { - if *local == RETURN_POINTER { + if *local == RETURN_PLACE { match self.destination { - Lvalue::Local(l) => { + Place::Local(l) => { *local = l; return; }, - ref lval => bug!("Return lvalue is {:?}, not local", lval) + ref place => bug!("Return place is {:?}, not local", place) } } let idx = local.index() - 1; if idx < self.args.len() { - match self.args[idx] { - Operand::Consume(Lvalue::Local(l)) => { - *local = l; - return; - }, - ref op => bug!("Arg operand `{:?}` is {:?}, not local", idx, op) - } + *local = self.args[idx]; + return; } *local = self.local_map[Local::new(idx - self.args.len())]; } - fn visit_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - _ctxt: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &mut Place<'tcx>, + _ctxt: PlaceContext<'tcx>, _location: Location) { - if let Lvalue::Local(RETURN_POINTER) = *lvalue { - // Return pointer; update the lvalue itself - *lvalue = self.destination.clone(); + if let Place::Local(RETURN_PLACE) = *place { + // Return pointer; update the place itself + *place = self.destination.clone(); } else { - self.super_lvalue(lvalue, _ctxt, _location); - } - } - - fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { - if let Operand::Consume(Lvalue::Local(arg)) = *operand { - if let Some(idx) = self.arg_index(arg) { - let new_arg = self.args[idx].clone(); - *operand = new_arg; - return; - } + self.super_place(place, _ctxt, _location); } - self.super_operand(operand, location); } fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { @@ -714,6 +807,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } TerminatorKind::Unreachable => { } + TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => { + *real_target = self.update_target(*real_target); + for target in imaginary_targets { + *target = self.update_target(*target); + } + } } } diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs index 6ccc886577ac6..8856d263864cd 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -10,13 +10,13 @@ //! Performs various peephole optimizations. -use rustc::mir::{Location, Lvalue, Mir, Operand, ProjectionElem, Rvalue, Local}; -use rustc::mir::transform::{MirPass, MirSource}; +use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, ProjectionElem, Rvalue, Local}; use rustc::mir::visit::{MutVisitor, Visitor}; -use rustc::ty::TyCtxt; -use rustc::util::nodemap::FxHashSet; +use rustc::ty::{TyCtxt, TypeVariants}; +use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use std::mem; +use transform::{MirPass, MirSource}; pub struct InstCombine; @@ -32,7 +32,7 @@ impl MirPass for InstCombine { // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. - // `Lvalue::ty()`). + // `Place::ty()`). let optimizations = { let mut optimization_finder = OptimizationFinder::new(mir, tcx); optimization_finder.visit_mir(mir); @@ -44,22 +44,27 @@ impl MirPass for InstCombine { } } -pub struct InstCombineVisitor { - optimizations: OptimizationList, +pub struct InstCombineVisitor<'tcx> { + optimizations: OptimizationList<'tcx>, } -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor { +impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { if self.optimizations.and_stars.remove(&location) { debug!("Replacing `&*`: {:?}", rvalue); - let new_lvalue = match *rvalue { - Rvalue::Ref(_, _, Lvalue::Projection(ref mut projection)) => { + let new_place = match *rvalue { + Rvalue::Ref(_, _, Place::Projection(ref mut projection)) => { // Replace with dummy - mem::replace(&mut projection.base, Lvalue::Local(Local::new(0))) + mem::replace(&mut projection.base, Place::Local(Local::new(0))) } _ => bug!("Detected `&*` but didn't find `&*`!"), }; - *rvalue = Rvalue::Use(Operand::Consume(new_lvalue)) + *rvalue = Rvalue::Use(Operand::Copy(new_place)) + } + + if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { + debug!("Replacing `Len([_; N])`: {:?}", rvalue); + *rvalue = Rvalue::Use(Operand::Constant(box constant)); } self.super_rvalue(rvalue, location) @@ -70,7 +75,7 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor { struct OptimizationFinder<'b, 'a, 'tcx:'a+'b> { mir: &'b Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, - optimizations: OptimizationList, + optimizations: OptimizationList<'tcx>, } impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { @@ -85,7 +90,7 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> { impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - if let Rvalue::Ref(_, _, Lvalue::Projection(ref projection)) = *rvalue { + if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue { if let ProjectionElem::Deref = projection.elem { if projection.base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { self.optimizations.and_stars.insert(location); @@ -93,11 +98,23 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for OptimizationFinder<'b, 'a, 'tcx> { } } + if let Rvalue::Len(ref place) = *rvalue { + let place_ty = place.ty(&self.mir.local_decls, self.tcx).to_ty(self.tcx); + if let TypeVariants::TyArray(_, len) = place_ty.sty { + let span = self.mir.source_info(location).span; + let ty = self.tcx.types.usize; + let literal = Literal::Value { value: len }; + let constant = Constant { span, ty, literal }; + self.optimizations.arrays_lengths.insert(location, constant); + } + } + self.super_rvalue(rvalue, location) } } #[derive(Default)] -struct OptimizationList { +struct OptimizationList<'tcx> { and_stars: FxHashSet, + arrays_lengths: FxHashMap>, } diff --git a/src/librustc_mir/transform/lower_128bit.rs b/src/librustc_mir/transform/lower_128bit.rs new file mode 100644 index 0000000000000..981b0b854bdab --- /dev/null +++ b/src/librustc_mir/transform/lower_128bit.rs @@ -0,0 +1,242 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Replaces 128-bit operators with lang item calls + +use rustc::hir::def_id::DefId; +use rustc::middle::lang_items::LangItem; +use rustc::mir::*; +use rustc::ty::{Slice, Ty, TyCtxt, TypeVariants}; +use rustc_data_structures::indexed_vec::{Idx}; +use transform::{MirPass, MirSource}; +use syntax; + +pub struct Lower128Bit; + +impl MirPass for Lower128Bit { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + let debugging_override = tcx.sess.opts.debugging_opts.lower_128bit_ops; + let target_default = tcx.sess.host.options.i128_lowering; + if !debugging_override.unwrap_or(target_default) { + return + } + + self.lower_128bit_ops(tcx, mir); + } +} + +impl Lower128Bit { + fn lower_128bit_ops<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) { + let mut new_blocks = Vec::new(); + let cur_len = mir.basic_blocks().len(); + + let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut(); + for block in basic_blocks.iter_mut() { + for i in (0..block.statements.len()).rev() { + let (lang_item, rhs_kind) = + if let Some((lang_item, rhs_kind)) = + lower_to(&block.statements[i], local_decls, tcx) + { + (lang_item, rhs_kind) + } else { + continue; + }; + + let rhs_override_ty = rhs_kind.ty(tcx); + let cast_local = + match rhs_override_ty { + None => None, + Some(ty) => { + let local_decl = LocalDecl::new_internal( + ty, block.statements[i].source_info.span); + Some(local_decls.push(local_decl)) + }, + }; + + let storage_dead = cast_local.map(|local| { + Statement { + source_info: block.statements[i].source_info, + kind: StatementKind::StorageDead(local), + } + }); + let after_call = BasicBlockData { + statements: storage_dead.into_iter() + .chain(block.statements.drain((i+1)..)).collect(), + is_cleanup: block.is_cleanup, + terminator: block.terminator.take(), + }; + + let bin_statement = block.statements.pop().unwrap(); + let (source_info, place, lhs, mut rhs) = match bin_statement { + Statement { + source_info, + kind: StatementKind::Assign( + place, + Rvalue::BinaryOp(_, lhs, rhs)) + } => (source_info, place, lhs, rhs), + Statement { + source_info, + kind: StatementKind::Assign( + place, + Rvalue::CheckedBinaryOp(_, lhs, rhs)) + } => (source_info, place, lhs, rhs), + _ => bug!("Statement doesn't match pattern any more?"), + }; + + if let Some(local) = cast_local { + block.statements.push(Statement { + source_info: source_info, + kind: StatementKind::StorageLive(local), + }); + block.statements.push(Statement { + source_info: source_info, + kind: StatementKind::Assign( + Place::Local(local), + Rvalue::Cast( + CastKind::Misc, + rhs, + rhs_override_ty.unwrap())), + }); + rhs = Operand::Move(Place::Local(local)); + } + + let call_did = check_lang_item_type( + lang_item, &place, &lhs, &rhs, local_decls, tcx); + + let bb = BasicBlock::new(cur_len + new_blocks.len()); + new_blocks.push(after_call); + + block.terminator = + Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: Operand::function_handle(tcx, call_did, + Slice::empty(), source_info.span), + args: vec![lhs, rhs], + destination: Some((place, bb)), + cleanup: None, + }, + }); + } + } + + basic_blocks.extend(new_blocks); + } +} + +fn check_lang_item_type<'a, 'tcx, D>( + lang_item: LangItem, + place: &Place<'tcx>, + lhs: &Operand<'tcx>, + rhs: &Operand<'tcx>, + local_decls: &D, + tcx: TyCtxt<'a, 'tcx, 'tcx>) +-> DefId + where D: HasLocalDecls<'tcx> +{ + let did = tcx.require_lang_item(lang_item); + let poly_sig = tcx.fn_sig(did); + let sig = poly_sig.no_late_bound_regions().unwrap(); + let lhs_ty = lhs.ty(local_decls, tcx); + let rhs_ty = rhs.ty(local_decls, tcx); + let place_ty = place.ty(local_decls, tcx).to_ty(tcx); + let expected = [lhs_ty, rhs_ty, place_ty]; + assert_eq!(sig.inputs_and_output[..], expected, + "lang item {}", tcx.def_symbol_name(did)); + did +} + +fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> Option<(LangItem, RhsKind)> + where D: HasLocalDecls<'tcx> +{ + match statement.kind { + StatementKind::Assign(_, Rvalue::BinaryOp(bin_op, ref lhs, _)) => { + let ty = lhs.ty(local_decls, tcx); + if let Some(is_signed) = sign_of_128bit(ty) { + return item_for_op(bin_op, is_signed); + } + }, + StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => { + let ty = lhs.ty(local_decls, tcx); + if let Some(is_signed) = sign_of_128bit(ty) { + return item_for_checked_op(bin_op, is_signed); + } + }, + _ => {}, + } + None +} + +#[derive(Copy, Clone)] +enum RhsKind { + Unchanged, + ForceU128, + ForceU32, +} + +impl RhsKind { + fn ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option> { + match *self { + RhsKind::Unchanged => None, + RhsKind::ForceU128 => Some(tcx.types.u128), + RhsKind::ForceU32 => Some(tcx.types.u32), + } + } +} + +fn sign_of_128bit(ty: Ty) -> Option { + match ty.sty { + TypeVariants::TyInt(syntax::ast::IntTy::I128) => Some(true), + TypeVariants::TyUint(syntax::ast::UintTy::U128) => Some(false), + _ => None, + } +} + +fn item_for_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> { + let i = match (bin_op, is_signed) { + (BinOp::Add, true) => (LangItem::I128AddFnLangItem, RhsKind::Unchanged), + (BinOp::Add, false) => (LangItem::U128AddFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, true) => (LangItem::I128SubFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, false) => (LangItem::U128SubFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, true) => (LangItem::I128MulFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, false) => (LangItem::U128MulFnLangItem, RhsKind::Unchanged), + (BinOp::Div, true) => (LangItem::I128DivFnLangItem, RhsKind::Unchanged), + (BinOp::Div, false) => (LangItem::U128DivFnLangItem, RhsKind::Unchanged), + (BinOp::Rem, true) => (LangItem::I128RemFnLangItem, RhsKind::Unchanged), + (BinOp::Rem, false) => (LangItem::U128RemFnLangItem, RhsKind::Unchanged), + (BinOp::Shl, true) => (LangItem::I128ShlFnLangItem, RhsKind::ForceU32), + (BinOp::Shl, false) => (LangItem::U128ShlFnLangItem, RhsKind::ForceU32), + (BinOp::Shr, true) => (LangItem::I128ShrFnLangItem, RhsKind::ForceU32), + (BinOp::Shr, false) => (LangItem::U128ShrFnLangItem, RhsKind::ForceU32), + _ => return None, + }; + Some(i) +} + +fn item_for_checked_op(bin_op: BinOp, is_signed: bool) -> Option<(LangItem, RhsKind)> { + let i = match (bin_op, is_signed) { + (BinOp::Add, true) => (LangItem::I128AddoFnLangItem, RhsKind::Unchanged), + (BinOp::Add, false) => (LangItem::U128AddoFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, true) => (LangItem::I128SuboFnLangItem, RhsKind::Unchanged), + (BinOp::Sub, false) => (LangItem::U128SuboFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, true) => (LangItem::I128MuloFnLangItem, RhsKind::Unchanged), + (BinOp::Mul, false) => (LangItem::U128MuloFnLangItem, RhsKind::Unchanged), + (BinOp::Shl, true) => (LangItem::I128ShloFnLangItem, RhsKind::ForceU128), + (BinOp::Shl, false) => (LangItem::U128ShloFnLangItem, RhsKind::ForceU128), + (BinOp::Shr, true) => (LangItem::I128ShroFnLangItem, RhsKind::ForceU128), + (BinOp::Shr, false) => (LangItem::U128ShroFnLangItem, RhsKind::ForceU128), + _ => bug!("That should be all the checked ones?"), + }; + Some(i) +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 7245b2daa1260..fb9daf07c71dc 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -10,21 +10,20 @@ use build; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc::mir::Mir; -use rustc::mir::transform::{MirPassIndex, MirSuite, MirSource, - MIR_CONST, MIR_VALIDATED, MIR_OPTIMIZED}; -use rustc::ty::{self, TyCtxt}; +use rustc::mir::{Mir, Promoted}; +use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc::ty::steal::Steal; use rustc::hir; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::util::nodemap::DefIdSet; +use std::borrow::Cow; use std::rc::Rc; use syntax::ast; -use syntax_pos::{DUMMY_SP, Span}; -use transform; +use syntax_pos::Span; pub mod add_validation; +pub mod add_moves_for_packed_drops; pub mod clean_end_regions; pub mod check_unsafety; pub mod simplify_branches; @@ -37,19 +36,21 @@ pub mod elaborate_drops; pub mod add_call_guards; pub mod promote_consts; pub mod qualify_consts; +pub mod remove_noop_landing_pads; pub mod dump_mir; pub mod deaggregator; pub mod instcombine; pub mod copy_prop; pub mod generator; pub mod inline; -pub mod nll; +pub mod lower_128bit; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); self::check_unsafety::provide(providers); *providers = Providers { mir_keys, + mir_built, mir_const, mir_validated, optimized_mir, @@ -103,66 +104,167 @@ fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum) Rc::new(set) } +fn mir_built<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal> { + let mir = build::mir_build(tcx, def_id); + tcx.alloc_steal_mir(mir) +} + +/// Where a specific Mir comes from. +#[derive(Debug, Copy, Clone)] +pub struct MirSource { + pub def_id: DefId, + + /// If `Some`, this is a promoted rvalue within the parent function. + pub promoted: Option, +} + +impl MirSource { + pub fn item(def_id: DefId) -> Self { + MirSource { + def_id, + promoted: None + } + } +} + +/// Generates a default name for the pass based on the name of the +/// type `T`. +pub fn default_name() -> Cow<'static, str> { + let name = unsafe { ::std::intrinsics::type_name::() }; + if let Some(tail) = name.rfind(":") { + Cow::from(&name[tail+1..]) + } else { + Cow::from(name) + } +} + +/// A streamlined trait that you can implement to create a pass; the +/// pass will be named after the type, and it will consist of a main +/// loop that goes over each available MIR and applies `run_pass`. +pub trait MirPass { + fn name<'a>(&'a self) -> Cow<'a, str> { + default_name::() + } + + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>); +} + +pub macro run_passes($tcx:ident, $mir:ident, $def_id:ident, $suite_index:expr; $($pass:expr,)*) {{ + let suite_index: usize = $suite_index; + let run_passes = |mir: &mut _, promoted| { + let source = MirSource { + def_id: $def_id, + promoted + }; + let mut index = 0; + let mut run_pass = |pass: &MirPass| { + let run_hooks = |mir: &_, index, is_after| { + dump_mir::on_mir_pass($tcx, &format_args!("{:03}-{:03}", suite_index, index), + &pass.name(), source, mir, is_after); + }; + run_hooks(mir, index, false); + pass.run_pass($tcx, source, mir); + run_hooks(mir, index, true); + + index += 1; + }; + $(run_pass(&$pass);)* + }; + + run_passes(&mut $mir, None); + + for (index, promoted_mir) in $mir.promoted.iter_enumerated_mut() { + run_passes(promoted_mir, Some(index)); + + // Let's make sure we don't miss any nested instances + assert!(promoted_mir.promoted.is_empty()); + } +}} + fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal> { - let mut mir = build::mir_build(tcx, def_id); - let source = MirSource::from_local_def_id(tcx, def_id); - transform::run_suite(tcx, source, MIR_CONST, &mut mir); + // Unsafety check uses the raw mir, so make sure it is run + let _ = tcx.unsafety_check_result(def_id); + + let mut mir = tcx.mir_built(def_id).steal(); + run_passes![tcx, mir, def_id, 0; + // Remove all `EndRegion` statements that are not involved in borrows. + clean_end_regions::CleanEndRegions, + + // What we need to do constant evaluation. + simplify::SimplifyCfg::new("initial"), + type_check::TypeckMir, + rustc_peek::SanityCheck, + ]; tcx.alloc_steal_mir(mir) } fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal> { - let source = MirSource::from_local_def_id(tcx, def_id); - if let MirSource::Const(_) = source { + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); + if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(node_id) { // Ensure that we compute the `mir_const_qualif` for constants at - // this point, before we steal the mir-const result. We don't - // directly need the result or `mir_const_qualif`, so we can just force it. - ty::queries::mir_const_qualif::force(tcx, DUMMY_SP, def_id); + // this point, before we steal the mir-const result. + let _ = tcx.mir_const_qualif(def_id); } - ty::queries::unsafety_violations::force(tcx, DUMMY_SP, def_id); let mut mir = tcx.mir_const(def_id).steal(); - transform::run_suite(tcx, source, MIR_VALIDATED, &mut mir); + run_passes![tcx, mir, def_id, 1; + // What we need to run borrowck etc. + qualify_consts::QualifyAndPromoteConstants, + simplify::SimplifyCfg::new("qualify-consts"), + ]; tcx.alloc_steal_mir(mir) } fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Mir<'tcx> { // (Mir-)Borrowck uses `mir_validated`, so we have to force it to // execute before we can steal. - ty::queries::mir_borrowck::force(tcx, DUMMY_SP, def_id); - ty::queries::borrowck::force(tcx, DUMMY_SP, def_id); + let _ = tcx.mir_borrowck(def_id); + let _ = tcx.borrowck(def_id); let mut mir = tcx.mir_validated(def_id).steal(); - let source = MirSource::from_local_def_id(tcx, def_id); - transform::run_suite(tcx, source, MIR_OPTIMIZED, &mut mir); - tcx.alloc_mir(mir) -} + run_passes![tcx, mir, def_id, 2; + // Remove all things not needed by analysis + no_landing_pads::NoLandingPads, + simplify_branches::SimplifyBranches::new("initial"), + remove_noop_landing_pads::RemoveNoopLandingPads, + simplify::SimplifyCfg::new("early-opt"), -fn run_suite<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, - suite: MirSuite, - mir: &mut Mir<'tcx>) -{ - let passes = tcx.mir_passes.passes(suite); + // These next passes must be executed together + add_call_guards::CriticalCallEdges, + elaborate_drops::ElaborateDrops, + no_landing_pads::NoLandingPads, + // AddValidation needs to run after ElaborateDrops and before EraseRegions, and it needs + // an AllCallEdges pass right before it. + add_call_guards::AllCallEdges, + add_validation::AddValidation, + // AddMovesForPackedDrops needs to run after drop + // elaboration. + add_moves_for_packed_drops::AddMovesForPackedDrops, - for (pass, index) in passes.iter().zip(0..) { - let pass_num = MirPassIndex(index); + simplify::SimplifyCfg::new("elaborate-drops"), - for hook in tcx.mir_passes.hooks() { - hook.on_mir_pass(tcx, suite, pass_num, &pass.name(), source, &mir, false); - } + // No lifetime analysis based on borrowing can be done from here on out. - pass.run_pass(tcx, source, mir); + // From here on out, regions are gone. + erase_regions::EraseRegions, - for (index, promoted_mir) in mir.promoted.iter_enumerated_mut() { - let promoted_source = MirSource::Promoted(source.item_id(), index); - pass.run_pass(tcx, promoted_source, promoted_mir); + lower_128bit::Lower128Bit, - // Let's make sure we don't miss any nested instances - assert!(promoted_mir.promoted.is_empty()); - } + // Optimizations begin. + inline::Inline, + instcombine::InstCombine, + deaggregator::Deaggregator, + copy_prop::CopyPropagation, + remove_noop_landing_pads::RemoveNoopLandingPads, + simplify::SimplifyCfg::new("final"), + simplify::SimplifyLocals, - for hook in tcx.mir_passes.hooks() { - hook.on_mir_pass(tcx, suite, pass_num, &pass.name(), source, &mir, true); - } - } + generator::StateTransform, + add_call_guards::CriticalCallEdges, + dump_mir::Marker("PreTrans"), + ]; + tcx.alloc_mir(mir) } diff --git a/src/librustc_mir/transform/nll.rs b/src/librustc_mir/transform/nll.rs deleted file mode 100644 index bd02788df1653..0000000000000 --- a/src/librustc_mir/transform/nll.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::ty::TypeFoldable; -use rustc::ty::subst::{Kind, Substs}; -use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind}; -use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; -use rustc::mir::visit::{MutVisitor, Lookup}; -use rustc::mir::transform::{MirPass, MirSource}; -use rustc::infer::{self, InferCtxt}; -use syntax_pos::DUMMY_SP; -use std::collections::HashMap; - -#[allow(dead_code)] -struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - lookup_map: HashMap, - infcx: InferCtxt<'a, 'gcx, 'tcx>, -} - -impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - pub fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>) -> Self { - NLLVisitor { - infcx, - lookup_map: HashMap::new(), - } - } - - pub fn into_results(self) -> HashMap { - self.lookup_map - } - - fn renumber_regions(&self, value: &T) -> T where T: TypeFoldable<'tcx> { - self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { - self.infcx.next_region_var(infer::MiscVariable(DUMMY_SP)) - }) - } - - fn store_region(&mut self, region: &RegionKind, lookup: Lookup) { - if let RegionKind::ReVar(rid) = *region { - self.lookup_map.entry(rid).or_insert(lookup); - } - } - - fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) { - for region in ty.regions() { - self.store_region(region, lookup); - } - } - - fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) { - if let Some(ty) = kind.as_type() { - self.store_ty_regions(&ty, lookup); - } else if let Some(region) = kind.as_region() { - self.store_region(region, lookup); - } - } -} - -impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) { - let old_ty = *ty; - *ty = self.renumber_regions(&old_ty); - self.store_ty_regions(ty, lookup); - } - - fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { - *substs = self.renumber_regions(&{*substs}); - let lookup = Lookup::Loc(location); - for kind in *substs { - self.store_kind_regions(kind, lookup); - } - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - match *rvalue { - Rvalue::Ref(ref mut r, _, _) => { - let old_r = *r; - *r = self.renumber_regions(&old_r); - let lookup = Lookup::Loc(location); - self.store_region(r, lookup); - } - Rvalue::Use(..) | - Rvalue::Repeat(..) | - Rvalue::Len(..) | - Rvalue::Cast(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) | - Rvalue::NullaryOp(..) | - Rvalue::Aggregate(..) => { - // These variants don't contain regions. - } - } - self.super_rvalue(rvalue, location); - } - - fn visit_closure_substs(&mut self, - substs: &mut ClosureSubsts<'tcx>, - location: Location) { - *substs = self.renumber_regions(substs); - let lookup = Lookup::Loc(location); - for kind in substs.substs { - self.store_kind_regions(kind, lookup); - } - } - - fn visit_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>, - location: Location) { - if let StatementKind::EndRegion(_) = statement.kind { - statement.kind = StatementKind::Nop; - } - self.super_statement(block, statement, location); - } -} - -// MIR Pass for non-lexical lifetimes -pub struct NLL; - -impl MirPass for NLL { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - _: MirSource, - mir: &mut Mir<'tcx>) { - if !tcx.sess.opts.debugging_opts.nll { - return; - } - - tcx.infer_ctxt().enter(|infcx| { - // Clone mir so we can mutate it without disturbing the rest of the compiler - let mut renumbered_mir = mir.clone(); - let mut visitor = NLLVisitor::new(infcx); - visitor.visit_mir(&mut renumbered_mir); - let _results = visitor.into_results(); - }) - } -} \ No newline at end of file diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index fa6bb644871dc..c8f171d4160c6 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -14,7 +14,7 @@ use rustc::ty::TyCtxt; use rustc::mir::*; use rustc::mir::visit::MutVisitor; -use rustc::mir::transform::{MirPass, MirSource}; +use transform::{MirPass, MirSource}; pub struct NoLandingPads; @@ -38,22 +38,8 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { bb: BasicBlock, terminator: &mut Terminator<'tcx>, location: Location) { - match terminator.kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::Unreachable | - TerminatorKind::GeneratorDrop | - TerminatorKind::Yield { .. } | - TerminatorKind::SwitchInt { .. } => { - /* nothing to do */ - }, - TerminatorKind::Call { cleanup: ref mut unwind, .. } | - TerminatorKind::Assert { cleanup: ref mut unwind, .. } | - TerminatorKind::DropAndReplace { ref mut unwind, .. } | - TerminatorKind::Drop { ref mut unwind, .. } => { - unwind.take(); - }, + if let Some(unwind) = terminator.kind.unwind_mut() { + unwind.take(); } self.super_terminator(bb, terminator, location); } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 339ea8a414b1e..1e5b0bc1392bc 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -23,7 +23,7 @@ //! move analysis runs after promotion on broken MIR. use rustc::mir::*; -use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor}; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; use rustc::mir::traversal::ReversePostorder; use rustc::ty::TyCtxt; use syntax_pos::Span; @@ -85,7 +85,7 @@ struct TempCollector<'tcx> { impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { fn visit_local(&mut self, &index: &Local, - context: LvalueContext<'tcx>, + context: PlaceContext<'tcx>, location: Location) { // We're only interested in temporaries if self.mir.local_kind(index) != LocalKind::Temp { @@ -102,8 +102,8 @@ impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { let temp = &mut self.temps[index]; if *temp == TempState::Undefined { match context { - LvalueContext::Store | - LvalueContext::Call => { + PlaceContext::Store | + PlaceContext::Call => { *temp = TempState::Defined { location, uses: 0 @@ -116,7 +116,7 @@ impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { // 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, + PlaceContext::Borrow {..} => true, _ => context.is_nonmutating_use() }; if allowed_use { @@ -179,7 +179,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { span, scope: ARGUMENT_VISIBILITY_SCOPE }, - kind: StatementKind::Assign(Lvalue::Local(dest), rvalue) + kind: StatementKind::Assign(Place::Local(dest), rvalue) }); } @@ -268,7 +268,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { func, args, cleanup: None, - destination: Some((Lvalue::Local(new_temp), new_target)) + destination: Some((Place::Local(new_temp), new_target)) }, ..terminator }; @@ -287,7 +287,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let span = self.promoted.span; let new_operand = Operand::Constant(box Constant { span, - ty: self.promoted.return_ty, + ty: self.promoted.return_ty(), literal: Literal::Promoted { index: Promoted::new(self.source.promoted.len()) } @@ -316,7 +316,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { statement_index: usize::MAX }); - self.assign(RETURN_POINTER, rvalue, span); + self.assign(RETURN_PLACE, rvalue, span); self.source.promoted.push(self.promoted); } } @@ -325,7 +325,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { fn visit_local(&mut self, local: &mut Local, - _: LvalueContext<'tcx>, + _: PlaceContext<'tcx>, _: Location) { if self.source.local_kind(*local) == LocalKind::Temp { *local = self.promote_temp(*local); @@ -350,7 +350,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, "expected assignment to promote"); } }; - if let Lvalue::Local(index) = *dest { + if let Place::Local(index) = *dest { if temps[index] == TempState::PromotedOut { // Already promoted. continue; @@ -373,8 +373,8 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, } }; - // Declare return pointer local - let initial_locals = iter::once(LocalDecl::new_return_pointer(ty, span)) + // Declare return place local + let initial_locals = iter::once(LocalDecl::new_return_place(ty, span)) .collect(); let mut promoter = Promoter { @@ -385,7 +385,6 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, mir.visibility_scopes.clone(), mir.visibility_scope_info.clone(), IndexVec::new(), - ty, None, initial_locals, 0, @@ -405,7 +404,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, for block in mir.basic_blocks_mut() { block.statements.retain(|statement| { match statement.kind { - StatementKind::Assign(Lvalue::Local(index), _) | + StatementKind::Assign(Place::Local(index), _) | StatementKind::StorageLive(index) | StatementKind::StorageDead(index) => { !promoted(index) @@ -415,7 +414,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, }); let terminator = block.terminator_mut(); match terminator.kind { - TerminatorKind::Drop { location: Lvalue::Local(index), target, .. } => { + TerminatorKind::Drop { location: Place::Local(index), target, .. } => { if promoted(index) { terminator.kind = TerminatorKind::Goto { target, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 3f3b9d177d70c..b9b86dd6e840f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -26,8 +26,7 @@ use rustc::ty::cast::CastTy; use rustc::ty::maps::Providers; use rustc::mir::*; use rustc::mir::traversal::ReversePostorder; -use rustc::mir::transform::{MirPass, MirSource}; -use rustc::mir::visit::{LvalueContext, Visitor}; +use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::middle::lang_items; use syntax::abi::Abi; use syntax::attr; @@ -38,6 +37,7 @@ use std::fmt; use std::rc::Rc; use std::usize; +use transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; bitflags! { @@ -51,7 +51,7 @@ bitflags! { // Function argument. const FN_ARGUMENT = 1 << 2; - // Static lvalue or move from a static. + // Static place or move from a static. const STATIC = 1 << 3; // Reference to a static. @@ -197,7 +197,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { self.add(original); } - /// Check if an Lvalue with the current qualifications could + /// Check if an Place with the current qualifications could /// be consumed, by either an operand or a Deref projection. fn try_consume(&mut self) -> bool { if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn { @@ -224,7 +224,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } /// Assign the current qualification to the given destination. - fn assign(&mut self, dest: &Lvalue<'tcx>, location: Location) { + fn assign(&mut self, dest: &Place<'tcx>, location: Location) { let qualif = self.qualif; let span = self.span; let store = |slot: &mut Option| { @@ -236,7 +236,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // Only handle promotable temps in non-const functions. if self.mode == Mode::Fn { - if let Lvalue::Local(index) = *dest { + if let Place::Local(index) = *dest { if self.mir.local_kind(index) == LocalKind::Temp && self.temp_promotion_state[index].is_promotable() { debug!("store to promotable temp {:?}", index); @@ -249,24 +249,24 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // When initializing a local, record whether the *value* being // stored in it needs dropping, which it may not, even if its // type does, e.g. `None::`. - if let Lvalue::Local(local) = *dest { + if let Place::Local(local) = *dest { if qualif.intersects(Qualif::NEEDS_DROP) { self.local_needs_drop[local] = Some(self.span); } } match *dest { - Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => { + Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => { debug!("store to temp {:?}", index); store(&mut self.temp_qualif[index]) } - Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => { - debug!("store to return pointer {:?}", index); + Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => { + debug!("store to return place {:?}", index); store(&mut self.return_qualif) } - Lvalue::Projection(box Projection { - base: Lvalue::Local(index), + Place::Projection(box Projection { + base: Place::Local(index), elem: ProjectionElem::Deref }) if self.mir.local_kind(index) == LocalKind::Temp && self.mir.local_decls[index].ty.is_box() @@ -280,7 +280,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // This must be an explicit assignment. _ => { // Catch more errors in the destination. - self.visit_lvalue(dest, LvalueContext::Store, location); + self.visit_place(dest, PlaceContext::Store, location); self.statement_like(); } } @@ -317,7 +317,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { TerminatorKind::Resume | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } | - TerminatorKind::Unreachable => None, + TerminatorKind::Unreachable | + TerminatorKind::FalseEdges { .. } => None, TerminatorKind::Return => { // Check for unused values. This usually means @@ -350,7 +351,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { for index in mir.vars_iter() { if !self.const_fn_arg_vars.contains(index.index()) { debug!("unassigned variable {:?}", index); - self.assign(&Lvalue::Local(index), Location { + self.assign(&Place::Local(index), Location { block: bb, statement_index: usize::MAX, }); @@ -379,7 +380,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // conservative type qualification instead. if self.qualif.intersects(Qualif::CONST_ERROR) { self.qualif = Qualif::empty(); - let return_ty = mir.return_ty; + let return_ty = mir.return_ty(); self.add_type(return_ty); } @@ -391,7 +392,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { match *candidate { Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { match self.mir[bb].statements[stmt_idx].kind { - StatementKind::Assign(_, Rvalue::Ref(_, _, Lvalue::Local(index))) => { + StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => { promoted_temps.add(&index); } _ => {} @@ -411,7 +412,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_local(&mut self, &local: &Local, - _: LvalueContext<'tcx>, + _: PlaceContext<'tcx>, _: Location) { match self.mir.local_kind(local) { LocalKind::ReturnPointer => { @@ -437,13 +438,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - match *lvalue { - Lvalue::Local(ref local) => self.visit_local(local, context, location), - Lvalue::Static(ref global) => { + match *place { + Place::Local(ref local) => self.visit_local(local, context, location), + Place::Static(ref global) => { self.add(Qualif::STATIC); if self.mode != Mode::Fn { @@ -464,9 +465,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { a constant instead", self.mode); } } - Lvalue::Projection(ref proj) => { + Place::Projection(ref proj) => { self.nest(|this| { - this.super_lvalue(lvalue, context, location); + this.super_place(place, context, location); match proj.elem { ProjectionElem::Deref => { if !this.try_consume() { @@ -501,7 +502,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { "cannot refer to the interior of another \ static, use a constant instead"); } - let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx); + let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); this.qualif.restrict(ty, this.tcx, this.param_env); } @@ -518,14 +519,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { match *operand { - Operand::Consume(ref lvalue) => { + Operand::Copy(ref place) | + Operand::Move(ref place) => { self.nest(|this| { this.super_operand(operand, location); this.try_consume(); }); // Mark the consumed locals to indicate later drops are noops. - if let Lvalue::Local(local) = *lvalue { + if let Place::Local(local) = *place { self.local_needs_drop[local] = None; } } @@ -553,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - // Recurse through operands and lvalues. + // Recurse through operands and places. self.super_rvalue(rvalue, location); match *rvalue { @@ -570,20 +572,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::Discriminant(..) => {} Rvalue::Len(_) => { - // Static lvalues in consts would have errored already, + // Static places in consts would have errored already, // don't treat length checks as reads from statics. self.qualif = self.qualif - Qualif::STATIC; } - Rvalue::Ref(_, kind, ref lvalue) => { - // Static lvalues in consts would have errored already, + Rvalue::Ref(_, kind, ref place) => { + // Static places in consts would have errored already, // only keep track of references to them here. if self.qualif.intersects(Qualif::STATIC) { self.qualif = self.qualif - Qualif::STATIC; self.add(Qualif::STATIC_REF); } - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); + let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); if kind == BorrowKind::Mut { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] @@ -633,7 +635,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { let candidate = Candidate::Ref(location); if !self.qualif.intersects(Qualif::NEVER_PROMOTE) { // We can only promote direct borrows of temps. - if let Lvalue::Local(local) = *lvalue { + if let Place::Local(local) = *place { if self.mir.local_kind(local) == LocalKind::Temp { self.promotion_candidates.push(candidate); } @@ -827,14 +829,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } self.assign(dest, location); } - } else if let TerminatorKind::Drop { location: ref lvalue, .. } = *kind { + } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { self.super_terminator_kind(bb, kind, location); // Deny *any* live drops anywhere other than functions. if self.mode != Mode::Fn { // HACK(eddyb) Emulate a bit of dataflow analysis, // conservatively, that drop elaboration will do. - let needs_drop = if let Lvalue::Local(local) = *lvalue { + let needs_drop = if let Place::Local(local) = *place { self.local_needs_drop[local] } else { None @@ -842,7 +844,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if let Some(span) = needs_drop { // Double-check the type being dropped, to minimize false positives. - let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); + let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); if ty.needs_drop(self.tcx, self.param_env) { struct_span_err!(self.tcx.sess, span, E0493, "destructors cannot be evaluated at compile-time") @@ -860,21 +862,25 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_assign(&mut self, _: BasicBlock, - dest: &Lvalue<'tcx>, + dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { self.visit_rvalue(rvalue, location); // Check the allowed const fn argument forms. - if let (Mode::ConstFn, &Lvalue::Local(index)) = (self.mode, dest) { + if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { if self.mir.local_kind(index) == LocalKind::Var && self.const_fn_arg_vars.insert(index.index()) { // Direct use of an argument is permitted. - if let Rvalue::Use(Operand::Consume(Lvalue::Local(local))) = *rvalue { - if self.mir.local_kind(local) == LocalKind::Arg { - return; + match *rvalue { + Rvalue::Use(Operand::Copy(Place::Local(local))) | + Rvalue::Use(Operand::Move(Place::Local(local))) => { + if self.mir.local_kind(local) == LocalKind::Arg { + return; + } } + _ => {} } // Avoid a generic error for other uses of arguments. @@ -899,8 +905,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { self.nest(|this| { this.visit_source_info(&statement.source_info); match statement.kind { - StatementKind::Assign(ref lvalue, ref rvalue) => { - this.visit_assign(bb, lvalue, rvalue, location); + StatementKind::Assign(ref place, ref rvalue) => { + this.visit_assign(bb, place, rvalue, location); } StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(_) | @@ -937,7 +943,7 @@ fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // performing the steal. let mir = &tcx.mir_const(def_id).borrow(); - if mir.return_ty.references_error() { + if mir.return_ty().references_error() { tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors"); return (Qualif::NOT_CONST.bits(), Rc::new(IdxSetBuf::new_empty(0))); } @@ -955,30 +961,32 @@ impl MirPass for QualifyAndPromoteConstants { src: MirSource, mir: &mut Mir<'tcx>) { // There's not really any point in promoting errorful MIR. - if mir.return_ty.references_error() { + if mir.return_ty().references_error() { tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors"); return; } - let id = src.item_id(); - let def_id = tcx.hir.local_def_id(id); + if src.promoted.is_some() { + return; + } + + let def_id = src.def_id; + let id = tcx.hir.as_local_node_id(def_id).unwrap(); let mut const_promoted_temps = None; - let mode = match src { - MirSource::Fn(_) => { + let mode = match tcx.hir.body_owner_kind(id) { + hir::BodyOwnerKind::Fn => { if tcx.is_const_fn(def_id) { Mode::ConstFn } else { Mode::Fn } } - MirSource::Const(_) => { + hir::BodyOwnerKind::Const => { const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1); Mode::Const } - MirSource::Static(_, hir::MutImmutable) => Mode::Static, - MirSource::Static(_, hir::MutMutable) => Mode::StaticMut, - MirSource::GeneratorDrop(_) | - MirSource::Promoted(..) => return + hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static, + hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut, }; if mode == Mode::Fn || mode == Mode::ConstFn { @@ -1022,7 +1030,7 @@ impl MirPass for QualifyAndPromoteConstants { }); let terminator = block.terminator_mut(); match terminator.kind { - TerminatorKind::Drop { location: Lvalue::Local(index), target, .. } => { + TerminatorKind::Drop { location: Place::Local(index), target, .. } => { if promoted_temps.contains(&index) { terminator.kind = TerminatorKind::Goto { target, @@ -1042,7 +1050,7 @@ impl MirPass for QualifyAndPromoteConstants { return; } } - let ty = mir.return_ty; + let ty = mir.return_ty(); tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::empty(Reveal::UserFacing); let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic); diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs new file mode 100644 index 0000000000000..d29174d57192b --- /dev/null +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -0,0 +1,137 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::ty::TyCtxt; +use rustc::mir::*; +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::indexed_vec::Idx; +use transform::{MirPass, MirSource}; +use util::patch::MirPatch; + +/// A pass that removes no-op landing pads and replaces jumps to them with +/// `None`. This is important because otherwise LLVM generates terrible +/// code for these. +pub struct RemoveNoopLandingPads; + +impl MirPass for RemoveNoopLandingPads { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + if tcx.sess.no_landing_pads() { + return + } + + debug!("remove_noop_landing_pads({:?})", mir); + self.remove_nop_landing_pads(mir); + } +} + +impl RemoveNoopLandingPads { + fn is_nop_landing_pad(&self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitVector) + -> bool + { + for stmt in &mir[bb].statements { + match stmt.kind { + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) | + StatementKind::EndRegion(_) | + StatementKind::Nop => { + // These are all nops in a landing pad (there's some + // borrowck interaction between EndRegion and storage + // instructions, but this should all run after borrowck). + } + + StatementKind::Assign(Place::Local(_), Rvalue::Use(_)) => { + // Writing to a local (e.g. a drop flag) does not + // turn a landing pad to a non-nop + } + + StatementKind::Assign(_, _) | + StatementKind::SetDiscriminant { .. } | + StatementKind::InlineAsm { .. } | + StatementKind::Validate { .. } => { + return false; + } + } + } + + let terminator = mir[bb].terminator(); + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::FalseEdges { .. } => { + terminator.successors().iter().all(|succ| { + nop_landing_pads.contains(succ.index()) + }) + }, + TerminatorKind::GeneratorDrop | + TerminatorKind::Yield { .. } | + TerminatorKind::Return | + TerminatorKind::Unreachable | + TerminatorKind::Call { .. } | + TerminatorKind::Assert { .. } | + TerminatorKind::DropAndReplace { .. } | + TerminatorKind::Drop { .. } => { + false + } + } + } + + fn remove_nop_landing_pads(&self, mir: &mut Mir) { + // make sure there's a single resume block + let resume_block = { + let patch = MirPatch::new(mir); + let resume_block = patch.resume_block(); + patch.apply(mir); + resume_block + }; + debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); + + let mut jumps_folded = 0; + let mut landing_pads_removed = 0; + let mut nop_landing_pads = BitVector::new(mir.basic_blocks().len()); + + // This is a post-order traversal, so that if A post-dominates B + // then A will be visited before B. + let postorder: Vec<_> = traversal::postorder(mir).map(|(bb, _)| bb).collect(); + for bb in postorder { + debug!(" processing {:?}", bb); + for target in mir[bb].terminator_mut().successors_mut() { + if *target != resume_block && nop_landing_pads.contains(target.index()) { + debug!(" folding noop jump to {:?} to resume block", target); + *target = resume_block; + jumps_folded += 1; + } + } + + match mir[bb].terminator_mut().unwind_mut() { + Some(unwind) => { + if *unwind == Some(resume_block) { + debug!(" removing noop landing pad"); + jumps_folded -= 1; + landing_pads_removed += 1; + *unwind = None; + } + } + _ => {} + } + + let is_nop_landing_pad = self.is_nop_landing_pad(bb, mir, &nop_landing_pads); + if is_nop_landing_pad { + nop_landing_pads.insert(bb.index()); + } + debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); + } + + debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); + } +} diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index ceff52409b2f0..08508143976e6 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -14,9 +14,9 @@ use syntax_pos::Span; use rustc::ty::{self, TyCtxt}; use rustc::mir::{self, Mir, Location}; -use rustc::mir::transform::{MirPass, MirSource}; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; +use transform::{MirPass, MirSource}; use dataflow::do_dataflow; use dataflow::MoveDataParamEnv; @@ -34,8 +34,8 @@ pub struct SanityCheck; impl MirPass for SanityCheck { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { - let id = src.item_id(); - let def_id = tcx.hir.local_def_id(id); + let def_id = src.def_id; + let id = tcx.hir.as_local_node_id(def_id).unwrap(); if !tcx.has_attr(def_id, "rustc_mir_borrowck") { debug!("skipping rustc_peek::SanityCheck on {}", tcx.item_path_str(def_id)); return; @@ -45,7 +45,7 @@ impl MirPass for SanityCheck { let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(mir, tcx, param_env); + let move_data = MoveData::gather_moves(mir, tcx).unwrap(); let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let flow_inits = @@ -123,12 +123,13 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, None => return, }; assert!(args.len() == 1); - let peek_arg_lval = match args[0] { - mir::Operand::Consume(ref lval @ mir::Lvalue::Local(_)) => Some(lval), + let peek_arg_place = match args[0] { + mir::Operand::Copy(ref place @ mir::Place::Local(_)) | + mir::Operand::Move(ref place @ mir::Place::Local(_)) => Some(place), _ => None, }; - let peek_arg_lval = match peek_arg_lval { + let peek_arg_place = match peek_arg_place { Some(arg) => arg, None => { tcx.sess.diagnostic().span_err( @@ -142,8 +143,8 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned(); // Emulate effect of all statements in the block up to (but not - // including) the borrow within `peek_arg_lval`. Do *not* include - // call to `peek_arg_lval` itself (since we are peeking the state + // including) the borrow within `peek_arg_place`. Do *not* include + // call to `peek_arg_place` itself (since we are peeking the state // of the argument at time immediate preceding Call to // `rustc_peek`). @@ -153,9 +154,9 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, for (j, stmt) in statements.iter().enumerate() { debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); - let (lvalue, rvalue) = match stmt.kind { - mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - (lvalue, rvalue) + let (place, rvalue) = match stmt.kind { + mir::StatementKind::Assign(ref place, ref rvalue) => { + (place, rvalue) } mir::StatementKind::StorageLive(_) | mir::StatementKind::StorageDead(_) | @@ -168,14 +169,14 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "sanity_check should run before Deaggregator inserts SetDiscriminant"), }; - if lvalue == peek_arg_lval { - if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_lval) = *rvalue { + if place == peek_arg_place { + if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue { // Okay, our search is over. - match move_data.rev_lookup.find(peeking_at_lval) { + match move_data.rev_lookup.find(peeking_at_place) { LookupResult::Exact(peek_mpi) => { let bit_state = sets.on_entry.contains(&peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", - lvalue, peeking_at_lval, bit_state); + place, peeking_at_place, bit_state); if !bit_state { tcx.sess.span_err(span, "rustc_peek: bit not set"); } @@ -195,10 +196,10 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let lhs_mpi = move_data.rev_lookup.find(lvalue); + let lhs_mpi = move_data.rev_lookup.find(place); - debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", - lvalue, lhs_mpi, stmt); + debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}", + place, lhs_mpi, stmt); // reset GEN and KILL sets before emulating their effect. for e in sets.gen_set.words_mut() { *e = 0; } for e in sets.kill_set.words_mut() { *e = 0; } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 89828cf375aa7..e7675b4ceaf29 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -41,9 +41,9 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::transform::{MirPass, MirSource}; -use rustc::mir::visit::{MutVisitor, Visitor, LvalueContext}; +use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext}; use std::borrow::Cow; +use transform::{MirPass, MirSource}; pub struct SimplifyCfg { label: String } @@ -124,8 +124,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> { self.collapse_goto_chain(successor, &mut changed); } - changed |= self.simplify_unwind(&mut terminator); - let mut new_stmts = vec![]; let mut inner_changed = true; while inner_changed { @@ -238,38 +236,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> { true } - // turn an unwind branch to a resume block into a None - fn simplify_unwind(&mut self, terminator: &mut Terminator<'tcx>) -> bool { - let unwind = match terminator.kind { - TerminatorKind::Drop { ref mut unwind, .. } | - TerminatorKind::DropAndReplace { ref mut unwind, .. } | - TerminatorKind::Call { cleanup: ref mut unwind, .. } | - TerminatorKind::Assert { cleanup: ref mut unwind, .. } => - unwind, - _ => return false - }; - - if let &mut Some(unwind_block) = unwind { - let is_resume_block = match self.basic_blocks[unwind_block] { - BasicBlockData { - ref statements, - terminator: Some(Terminator { - kind: TerminatorKind::Resume, .. - }), .. - } if statements.is_empty() => true, - _ => false - }; - if is_resume_block { - debug!("simplifying unwind to {:?} from {:?}", - unwind_block, terminator.source_info); - *unwind = None; - } - return is_resume_block; - } - - false - } - fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { blk.statements.retain(|stmt| if let StatementKind::Nop = stmt.kind { @@ -352,9 +318,9 @@ struct DeclMarker { } impl<'tcx> Visitor<'tcx> for DeclMarker { - fn visit_local(&mut self, local: &Local, ctx: LvalueContext<'tcx>, _: Location) { + fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) { // ignore these altogether, they get removed along with their otherwise unused decls. - if ctx != LvalueContext::StorageLive && ctx != LvalueContext::StorageDead { + if ctx != PlaceContext::StorageLive && ctx != PlaceContext::StorageDead { self.locals.insert(local.index()); } } @@ -377,7 +343,7 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater { }); self.super_basic_block_data(block, data); } - fn visit_local(&mut self, l: &mut Local, _: LvalueContext<'tcx>, _: Location) { + fn visit_local(&mut self, l: &mut Local, _: PlaceContext<'tcx>, _: Location) { *l = Local::new(self.map[l.index()]); } } diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 0dff145ecbce9..20c33bab1aacb 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -12,8 +12,8 @@ use rustc::ty::{self, TyCtxt}; use rustc::middle::const_val::ConstVal; -use rustc::mir::transform::{MirPass, MirSource}; use rustc::mir::*; +use transform::{MirPass, MirSource}; use std::borrow::Cow; @@ -61,6 +61,9 @@ impl MirPass for SimplifyBranches { }), expected, .. } if cond == expected => { TerminatorKind::Goto { target: target } }, + TerminatorKind::FalseEdges { real_target, .. } => { + TerminatorKind::Goto { target: real_target } + }, _ => continue }; } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index ab5998a34805b..f0b62e28a0da6 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,22 +11,52 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::infer::{self, InferCtxt, InferOk}; -use rustc::traits; +use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::traits::{self, FulfillmentContext}; +use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, Ty, TyCtxt, TypeVariants}; use rustc::middle::const_val::ConstVal; use rustc::mir::*; -use rustc::mir::tcx::LvalueTy; -use rustc::mir::transform::{MirPass, MirSource}; -use rustc::mir::visit::Visitor; +use rustc::mir::tcx::PlaceTy; +use rustc::mir::visit::{PlaceContext, Visitor}; use std::fmt; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; +use transform::{MirPass, MirSource}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +/// Type checks the given `mir` in the context of the inference +/// context `infcx`. Returns any region constraints that have yet to +/// be proven. +/// +/// This phase of type-check ought to be infallible -- this is because +/// the original, HIR-based type-check succeeded. So if any errors +/// occur here, we will get a `bug!` reported. +pub fn type_check<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + let mut checker = TypeChecker::new(infcx, body_id, param_env); + let errors_reported = { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + verifier.errors_reported + }; + + if !errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + checker.typeck_mir(mir); + } + + checker.constraints +} + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { tcx.sess.diagnostic().span_bug(span, msg); } @@ -51,7 +81,7 @@ macro_rules! span_mirbug_and_err { } enum FieldAccessError { - OutOfRange { field_count: usize } + OutOfRange { field_count: usize }, } /// Verifies that MIR types are sane to not crash further checks. @@ -59,12 +89,12 @@ enum FieldAccessError { /// The sanitize_XYZ methods here take an MIR object and compute its /// type, calling `span_mirbug` and returning an error type if there /// is a problem. -struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> { +struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> { cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, last_span: Span, body_id: ast::NodeId, - errors_reported: bool + errors_reported: bool, } impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { @@ -74,11 +104,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - _context: visit::LvalueContext, - location: Location) { - self.sanitize_lvalue(lvalue, location); + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.sanitize_place(place, location, context); } fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { @@ -92,13 +119,13 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.sanitize_type(rvalue, rval_ty); } - fn visit_local_decl(&mut self, local_decl: &LocalDecl<'tcx>) { - self.super_local_decl(local_decl); + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); self.sanitize_type(local_decl, local_decl.ty); } fn visit_mir(&mut self, mir: &Mir<'tcx>) { - self.sanitize_type(&"return type", mir.return_ty); + self.sanitize_type(&"return type", mir.return_ty()); for local_decl in &mir.local_decls { self.sanitize_type(local_decl, local_decl.ty); } @@ -116,7 +143,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { body_id: cx.body_id, cx, last_span: mir.span, - errors_reported: false + errors_reported: false, } } @@ -125,150 +152,183 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() { + if ty.has_escaping_regions() || ty.references_error() { span_mirbug_and_err!(self, parent, "bad type {:?}", ty) } else { ty } } - fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> { - debug!("sanitize_lvalue: {:?}", lvalue); - match *lvalue { - Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty }, - Lvalue::Static(box Static { def_id, ty: sty }) => { - let sty = self.sanitize_type(lvalue, sty); + fn sanitize_place( + &mut self, + place: &Place<'tcx>, + location: Location, + context: PlaceContext, + ) -> PlaceTy<'tcx> { + debug!("sanitize_place: {:?}", place); + let place_ty = match *place { + Place::Local(index) => PlaceTy::Ty { + ty: self.mir.local_decls[index].ty, + }, + Place::Static(box Static { def_id, ty: sty }) => { + let sty = self.sanitize_type(place, sty); let ty = self.tcx().type_of(def_id); - let ty = self.cx.normalize(&ty); - if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) { + let ty = self.cx.normalize(&ty, location); + if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) { span_mirbug!( - self, lvalue, "bad static type ({:?}: {:?}): {:?}", - ty, sty, terr); + self, + place, + "bad static type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); } - LvalueTy::Ty { ty: sty } - - }, - Lvalue::Projection(ref proj) => { - let base_ty = self.sanitize_lvalue(&proj.base, location); - if let LvalueTy::Ty { ty } = base_ty { + PlaceTy::Ty { ty: sty } + } + Place::Projection(ref proj) => { + let base_context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + let base_ty = self.sanitize_place(&proj.base, location, base_context); + if let PlaceTy::Ty { ty } = base_ty { if ty.references_error() { assert!(self.errors_reported); - return LvalueTy::Ty { ty: self.tcx().types.err }; + return PlaceTy::Ty { + ty: self.tcx().types.err, + }; } } - self.sanitize_projection(base_ty, &proj.elem, lvalue, location) + self.sanitize_projection(base_ty, &proj.elem, place, location) + } + }; + if let PlaceContext::Copy = context { + let ty = place_ty.to_ty(self.tcx()); + if self.cx + .infcx + .type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) + { + span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty); } } + place_ty } - fn sanitize_projection(&mut self, - base: LvalueTy<'tcx>, - pi: &LvalueElem<'tcx>, - lvalue: &Lvalue<'tcx>, - _: Location) - -> LvalueTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); + fn sanitize_projection( + &mut self, + base: PlaceTy<'tcx>, + pi: &PlaceElem<'tcx>, + place: &Place<'tcx>, + location: Location, + ) -> PlaceTy<'tcx> { + debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); - let span = self.last_span; match *pi { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); - LvalueTy::Ty { + PlaceTy::Ty { ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "deref of non-pointer {:?}", base_ty) - }) + span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) + }), } } ProjectionElem::Index(i) => { - let index_ty = Lvalue::Local(i).ty(self.mir, tcx).to_ty(tcx); + let index_ty = Place::Local(i).ty(self.mir, tcx).to_ty(tcx); if index_ty != tcx.types.usize { - LvalueTy::Ty { - ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i) + PlaceTy::Ty { + ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), } } else { - LvalueTy::Ty { + PlaceTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + }), } } } ProjectionElem::ConstantIndex { .. } => { // consider verifying in-bounds - LvalueTy::Ty { + PlaceTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + }), } } - ProjectionElem::Subslice { from, to } => { - LvalueTy::Ty { - ty: match base_ty.sty { - ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); - let min_size = (from as u64) + (to as u64); - if let Some(rest_size) = size.checked_sub(min_size) { - tcx.mk_array(inner, rest_size) - } else { - span_mirbug_and_err!( - self, lvalue, "taking too-small slice of {:?}", base_ty) - } - } - ty::TySlice(..) => base_ty, - _ => { + ProjectionElem::Subslice { from, to } => PlaceTy::Ty { + ty: match base_ty.sty { + ty::TyArray(inner, size) => { + let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let min_size = (from as u64) + (to as u64); + if let Some(rest_size) = size.checked_sub(min_size) { + tcx.mk_array(inner, rest_size) + } else { span_mirbug_and_err!( - self, lvalue, "slice of non-array {:?}", base_ty) + self, + place, + "taking too-small slice of {:?}", + base_ty + ) } } - } - } - ProjectionElem::Downcast(adt_def1, index) => - match base_ty.sty { - ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { - if index >= adt_def.variants.len() { - LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, - lvalue, - "cast to variant #{:?} but enum only has {:?}", - index, - adt_def.variants.len()) - } - } else { - LvalueTy::Downcast { - adt_def, - substs, - variant_index: index - } + ty::TySlice(..) => base_ty, + _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), + }, + }, + ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { + ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { + if index >= adt_def.variants.len() { + PlaceTy::Ty { + ty: span_mirbug_and_err!( + self, + place, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len() + ), + } + } else { + PlaceTy::Downcast { + adt_def, + substs, + variant_index: index, } } - _ => LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, lvalue, "can't downcast {:?} as {:?}", - base_ty, adt_def1) - } + } + _ => PlaceTy::Ty { + ty: span_mirbug_and_err!( + self, + place, + "can't downcast {:?} as {:?}", + base_ty, + adt_def1 + ), }, + }, ProjectionElem::Field(field, fty) => { - let fty = self.sanitize_type(lvalue, fty); - match self.field_ty(lvalue, base, field) { - Ok(ty) => { - if let Err(terr) = self.cx.eq_types(span, ty, fty) { - span_mirbug!( - self, lvalue, "bad field access ({:?}: {:?}): {:?}", - ty, fty, terr); - } - } - Err(FieldAccessError::OutOfRange { field_count }) => { + let fty = self.sanitize_type(place, fty); + match self.field_ty(place, base, field, location) { + Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { span_mirbug!( - self, lvalue, "accessed field #{} but variant only has {}", - field.index(), field_count) - } + self, + place, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + }, + Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( + self, + place, + "accessed field #{} but variant only has {}", + field.index(), + field_count + ), } - LvalueTy::Ty { ty: fty } + PlaceTy::Ty { ty: fty } } } } @@ -278,28 +338,29 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.tcx().types.err } - fn field_ty(&mut self, - parent: &fmt::Debug, - base_ty: LvalueTy<'tcx>, - field: Field) - -> Result, FieldAccessError> - { + fn field_ty( + &mut self, + parent: &fmt::Debug, + base_ty: PlaceTy<'tcx>, + field: Field, + location: Location, + ) -> Result, FieldAccessError> { let tcx = self.tcx(); let (variant, substs) = match base_ty { - LvalueTy::Downcast { adt_def, substs, variant_index } => { - (&adt_def.variants[variant_index], substs) - } - LvalueTy::Ty { ty } => match ty.sty { - ty::TyAdt(adt_def, substs) if adt_def.is_univariant() => { - (&adt_def.variants[0], substs) - } + PlaceTy::Downcast { + adt_def, + substs, + variant_index, + } => (&adt_def.variants[variant_index], substs), + PlaceTy::Ty { ty } => match ty.sty { + ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => (&adt_def.variants[0], substs), ty::TyClosure(def_id, substs) => { return match substs.upvar_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.upvar_tys(def_id, tcx).count() - }) + field_count: substs.upvar_tys(def_id, tcx).count(), + }), } } ty::TyGenerator(def_id, substs, _) => { @@ -311,52 +372,109 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { return match substs.field_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.field_tys(def_id, tcx).count() + 1 - }) - } + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + }; } ty::TyTuple(tys, _) => { return match tys.get(field.index()) { Some(&ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: tys.len() - }) + field_count: tys.len(), + }), } } - _ => return Ok(span_mirbug_and_err!( - self, parent, "can't project out of {:?}", base_ty)) - } + _ => { + return Ok(span_mirbug_and_err!( + self, + parent, + "can't project out of {:?}", + base_ty + )) + } + }, }; if let Some(field) = variant.fields.get(field.index()) { - Ok(self.cx.normalize(&field.ty(tcx, substs))) + Ok(self.cx.normalize(&field.ty(tcx, substs), location)) } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) } } } -pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. +pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, - fulfillment_cx: traits::FulfillmentContext<'tcx>, last_span: Span, body_id: ast::NodeId, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, + constraints: MirTypeckRegionConstraints<'tcx>, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +#[derive(Default)] +pub struct MirTypeckRegionConstraints<'tcx> { + /// In general, the type-checker is not responsible for enforcing + /// liveness constraints; this job falls to the region inferencer, + /// which performs a liveness analysis. However, in some limited + /// cases, the MIR type-checker creates temporary regions that do + /// not otherwise appear in the MIR -- in particular, the + /// late-bound regions that it instantiates at call-sites -- and + /// hence it must report on their liveness constraints. + pub liveness_set: Vec<(ty::Region<'tcx>, Location)>, + + /// During the course of type-checking, we will accumulate region + /// constraints due to performing subtyping operations or solving + /// traits. These are accumulated into this vector for later use. + pub outlives_sets: Vec>, +} + +/// Outlives relationships between regions and types created at a +/// particular point within the control-flow graph. +pub struct OutlivesSet<'tcx> { + /// The locations associated with these constraints. + pub locations: Locations, + + /// Constraints generated. In terms of the NLL RFC, when you have + /// a constraint `R1: R2 @ P`, the data in there specifies things + /// like `R1: R2`. + pub data: RegionConstraintData<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +pub struct Locations { + /// The location in the MIR that generated these constraints. + /// This is intended for error reporting and diagnosis; the + /// constraints may *take effect* at a distinct spot. + pub from_location: Location, + + /// The constraints must be met at this location. In terms of the + /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field + /// is the `P` value. + pub at_location: Location, } impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { - fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'gcx>) - -> Self { + fn new( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + ) -> Self { TypeChecker { infcx, - fulfillment_cx: traits::FulfillmentContext::new(), last_span: DUMMY_SP, body_id, param_env, reported_errors: FxHashSet(), + constraints: MirTypeckRegionConstraints::default(), } } @@ -364,61 +482,100 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { traits::ObligationCause::misc(span, self.body_id) } - pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { - for obligation in infer_ok.obligations { - self.fulfillment_cx.register_predicate_obligation(self.infcx, obligation); + fn fully_perform_op( + &mut self, + locations: Locations, + op: OP, + ) -> Result> + where + OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, + { + let mut fulfill_cx = FulfillmentContext::new(); + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; + fulfill_cx.register_predicate_obligations(self.infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", e); + } + + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + self.constraints + .outlives_sets + .push(OutlivesSet { locations, data }); } - infer_ok.value + + Ok(value) } - fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(self.last_span), self.param_env) - .sup(sup, sub) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + ) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .sup(sup, sub) + }) } - fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(span), self.param_env) - .eq(b, a) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .eq(b, a) + }) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } - fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) { + fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>, location: Location) { debug!("check_stmt: {:?}", stmt); let tcx = self.tcx(); match stmt.kind { - StatementKind::Assign(ref lv, ref rv) => { - let lv_ty = lv.ty(mir, tcx).to_ty(tcx); + StatementKind::Assign(ref place, ref rv) => { + let place_ty = place.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { - span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + if let Err(terr) = + self.sub_types(rv_ty, place_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); } + self.check_rvalue(mir, rv, location); } - StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { - let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx); - let adt = match lvalue_type.sty { + StatementKind::SetDiscriminant { + ref place, + variant_index, + } => { + let place_type = place.ty(mir, tcx).to_ty(tcx); + let adt = match place_type.sty { TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt, _ => { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): lhs is not an enum", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + place, + variant_index + ); } }; if variant_index >= adt.variants.len() { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): value of of range", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + place, + variant_index + ); }; } StatementKind::StorageLive(_) | @@ -430,9 +587,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_terminator(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>) { + fn check_terminator( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + term_location: Location, + ) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { @@ -441,37 +601,82 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | - TerminatorKind::Drop { .. } => { + TerminatorKind::Drop { .. } | + TerminatorKind::FalseEdges { .. } => { // no checks needed for these } - TerminatorKind::DropAndReplace { ref location, ref value, - .. + target, + unwind, } => { - let lv_ty = location.ty(mir, tcx).to_ty(tcx); + let place_ty = location.ty(mir, tcx).to_ty(tcx); let rv_ty = value.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { - span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + + let locations = Locations { + from_location: term_location, + at_location: target.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + + // Subtle: this assignment occurs at the start of + // *both* blocks, so we need to ensure that it holds + // at both locations. + if let Some(unwind) = unwind { + let locations = Locations { + from_location: term_location, + at_location: unwind.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } } } - TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { + ref discr, + switch_ty, + .. + } => { let discr_ty = discr.ty(mir, tcx); - if let Err(terr) = self.sub_types(discr_ty, switch_ty) { - span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, discr_ty, terr); + if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad SwitchInt ({:?} on {:?}): {:?}", + switch_ty, + discr_ty, + terr + ); } - if !switch_ty.is_integral() && !switch_ty.is_char() && - !switch_ty.is_bool() - { - span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty); + if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); } // FIXME: check the values } - TerminatorKind::Call { ref func, ref args, ref destination, .. } => { + TerminatorKind::Call { + ref func, + ref args, + ref destination, + .. + } => { let func_ty = func.ty(mir, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); let sig = match func_ty.sty { @@ -481,17 +686,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { return; } }; - let sig = tcx.erase_late_bound_regions(&sig); - let sig = self.normalize(&sig); - self.check_call_dest(mir, term, &sig, destination); + let (sig, map) = self.infcx.replace_late_bound_regions_with_fresh_var( + term.source_info.span, + LateBoundRegionConversionTime::FnCall, + &sig, + ); + let sig = self.normalize(&sig, term_location); + self.check_call_dest(mir, term, &sig, destination, term_location); + + // The ordinary liveness rules will ensure that all + // regions in the type of the callee are live here. We + // then further constrain the late-bound regions that + // were instantiated at the call site to be live as + // well. The resulting is that all the input (and + // output) types in the signature must be live, since + // all the inputs that fed into it were live. + for &late_bound_region in map.values() { + self.constraints + .liveness_set + .push((late_bound_region, term_location)); + } if self.is_box_free(func) { - self.check_box_free_inputs(mir, term, &sig, args); + self.check_box_free_inputs(mir, term, &sig, args, term_location); } else { - self.check_call_inputs(mir, term, &sig, args); + self.check_call_inputs(mir, term, &sig, args, term_location); } } - TerminatorKind::Assert { ref cond, ref msg, .. } => { + TerminatorKind::Assert { + ref cond, ref msg, .. + } => { let cond_ty = cond.ty(mir, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); @@ -511,13 +735,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { match mir.yield_ty { None => span_mirbug!(self, term, "yield in non-generator"), Some(ty) => { - if let Err(terr) = self.sub_types(value_ty, ty) { - span_mirbug!(self, + if let Err(terr) = self.sub_types(value_ty, ty, term_location.at_self()) { + span_mirbug!( + self, term, "type of yield value is {:?}, but the yield type is {:?}: {:?}", value_ty, ty, - terr); + terr + ); } } } @@ -525,46 +751,66 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_call_dest(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: &Option<(Lvalue<'tcx>, BasicBlock)>) { + fn check_call_dest( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: &Option<(Place<'tcx>, BasicBlock)>, + term_location: Location, + ) { let tcx = self.tcx(); match *destination { - Some((ref dest, _)) => { + Some((ref dest, target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = self.sub_types(sig.output(), dest_ty) { - span_mirbug!(self, term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, sig.output(), terr); + let locations = Locations { + from_location: term_location, + at_location: target_block.start_location(), + }; + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); } - }, + } None => { // FIXME(canndrew): This is_never should probably be an is_uninhabited if !sig.output().is_never() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } - }, + } } } - fn check_call_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) - { + fn check_call_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { debug!("check_call_inputs({:?}, {:?})", sig, args); - if args.len() < sig.inputs().len() || - (args.len() > sig.inputs().len() && !sig.variadic) { + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); - if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) { - span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, fn_arg, op_arg_ty, terr); + if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); } } } @@ -572,22 +818,29 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { match operand { &Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. - }, .. - }) => { - Some(def_id) == self.tcx().lang_items().box_free_fn() - } + literal: + Literal::Value { + value: + &ty::Const { + val: ConstVal::Function(def_id, _), + .. + }, + .. + }, + .. + }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), _ => false, } } - fn check_box_free_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) - { + fn check_box_free_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { debug!("check_box_free_inputs"); // box_free takes a Box as a pointer. Allow for that. @@ -620,87 +873,108 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; - if let Err(terr) = self.sub_types(arg_ty, pointee_ty) { - span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", - pointee_ty, arg_ty, terr); + if let Err(terr) = self.sub_types(arg_ty, pointee_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad box_free arg ({:?} <- {:?}): {:?}", + pointee_ty, + arg_ty, + terr + ); } } - fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>) - { - let is_cleanup = block.is_cleanup; - self.last_span = block.terminator().source_info.span; - match block.terminator().kind { - TerminatorKind::Goto { target } => - self.assert_iscleanup(mir, block, target, is_cleanup), - TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.assert_iscleanup(mir, block, *target, is_cleanup); - } - } - TerminatorKind::Resume => { - if !is_cleanup { - span_mirbug!(self, block, "resume on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block, "return on cleanup block") - } - } - TerminatorKind::GeneratorDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block, "generator_drop in cleanup block") - } + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + self.last_span = block_data.terminator().source_info.span; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup) } + TerminatorKind::SwitchInt { ref targets, .. } => for target in targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); + }, + TerminatorKind::Resume => if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + }, + TerminatorKind::Return => if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + }, + TerminatorKind::GeneratorDrop { .. } => if is_cleanup { + span_mirbug!(self, block_data, "generator_drop in cleanup block") + }, TerminatorKind::Yield { resume, drop, .. } => { if is_cleanup { - span_mirbug!(self, block, "yield in cleanup block") + span_mirbug!(self, block_data, "yield in cleanup block") } - self.assert_iscleanup(mir, block, resume, is_cleanup); + self.assert_iscleanup(mir, block_data, resume, is_cleanup); if let Some(drop) = drop { - self.assert_iscleanup(mir, block, drop, is_cleanup); + self.assert_iscleanup(mir, block_data, drop, is_cleanup); } } TerminatorKind::Unreachable => {} TerminatorKind::Drop { target, unwind, .. } | TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { target, cleanup: unwind, .. } => { - self.assert_iscleanup(mir, block, target, is_cleanup); + TerminatorKind::Assert { + target, + cleanup: unwind, + .. + } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup); if let Some(unwind) = unwind { if is_cleanup { - span_mirbug!(self, block, "unwind on cleanup block") + span_mirbug!(self, block_data, "unwind on cleanup block") } - self.assert_iscleanup(mir, block, unwind, true); + self.assert_iscleanup(mir, block_data, unwind, true); } } - TerminatorKind::Call { ref destination, cleanup, .. } => { + TerminatorKind::Call { + ref destination, + cleanup, + .. + } => { if let &Some((_, target)) = destination { - self.assert_iscleanup(mir, block, target, is_cleanup); + self.assert_iscleanup(mir, block_data, target, is_cleanup); } if let Some(cleanup) = cleanup { if is_cleanup { - span_mirbug!(self, block, "cleanup on cleanup block") + span_mirbug!(self, block_data, "cleanup on cleanup block") } - self.assert_iscleanup(mir, block, cleanup, true); + self.assert_iscleanup(mir, block_data, cleanup, true); + } + } + TerminatorKind::FalseEdges { + real_target, + ref imaginary_targets, + } => { + self.assert_iscleanup(mir, block_data, real_target, is_cleanup); + for target in imaginary_targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); } } } } - fn assert_iscleanup(&mut self, - mir: &Mir<'tcx>, - ctxt: &fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool) - { + fn assert_iscleanup( + &mut self, + mir: &Mir<'tcx>, + ctxt: &fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool, + ) { if mir[bb].is_cleanup != iscleanuppad { - span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", - bb, iscleanuppad); + span_mirbug!( + self, + ctxt, + "cleanuppad mismatch: {:?} should be {:?}", + bb, + iscleanuppad + ); } } - fn check_local(&mut self, mir: &Mir<'gcx>, local: Local, local_decl: &LocalDecl<'gcx>) { + fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { match mir.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -709,27 +983,150 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // // Unbound parts of arguments were never required to be Sized // - maybe we should make that a warning. - return + return; } LocalKind::Var | LocalKind::Temp => {} } let span = local_decl.source_info.span; let ty = local_decl.ty; - if !ty.is_sized(self.tcx().global_tcx(), self.param_env, span) { + + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let gcx = self.tcx().global_tcx(); + let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap(); + if !erased_ty.is_sized(gcx, self.param_env, span) { // in current MIR construction, all non-control-flow rvalue // expressions evaluate through `as_temp` or `into` a return // slot or local, so to find all unsized rvalues it is enough // to check all temps, return slots and locals. if let None = self.reported_errors.replace((ty, span)) { - span_err!(self.tcx().sess, span, E0161, - "cannot move a value of type {0}: the size of {0} \ - cannot be statically determined", ty); + span_err!( + self.tcx().sess, + span, + E0161, + "cannot move a value of type {0}: the size of {0} \ + cannot be statically determined", + ty + ); + } + } + } + + fn aggregate_field_ty( + &mut self, + ak: &AggregateKind<'tcx>, + field_index: usize, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + match *ak { + AggregateKind::Adt(def, variant_index, substs, active_field_index) => { + let variant = &def.variants[variant_index]; + let adj_field_index = active_field_index.unwrap_or(field_index); + if let Some(field) = variant.fields.get(adj_field_index) { + Ok(self.normalize(&field.ty(tcx, substs), location)) + } else { + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) + } + } + AggregateKind::Closure(def_id, substs) => { + match substs.upvar_tys(def_id, tcx).nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.upvar_tys(def_id, tcx).count(), + }), + } + } + AggregateKind::Generator(def_id, substs, _) => { + if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field_index) { + Ok(ty) + } else { + match substs.field_tys(def_id, tcx).nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + } + } + } + AggregateKind::Array(ty) => Ok(ty), + AggregateKind::Tuple => { + unreachable!("This should have been covered in check_rvalues"); } } } - fn typeck_mir(&mut self, mir: &Mir<'gcx>) { + fn check_rvalue(&mut self, mir: &Mir<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + match rvalue { + Rvalue::Aggregate(ak, ops) => { + self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) + } + // FIXME: These other cases have to be implemented in future PRs + Rvalue::Use(..) | + Rvalue::Repeat(..) | + Rvalue::Ref(..) | + Rvalue::Len(..) | + Rvalue::Cast(..) | + Rvalue::BinaryOp(..) | + Rvalue::CheckedBinaryOp(..) | + Rvalue::UnaryOp(..) | + Rvalue::Discriminant(..) | + Rvalue::NullaryOp(..) => {} + } + } + + fn check_aggregate_rvalue( + &mut self, + mir: &Mir<'tcx>, + rvalue: &Rvalue<'tcx>, + aggregate_kind: &AggregateKind<'tcx>, + operands: &[Operand<'tcx>], + location: Location, + ) { + match aggregate_kind { + // tuple rvalue field type is always the type of the op. Nothing to check here. + AggregateKind::Tuple => return, + _ => {} + } + + let tcx = self.tcx(); + + for (i, operand) in operands.iter().enumerate() { + let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { + Ok(field_ty) => field_ty, + Err(FieldAccessError::OutOfRange { field_count }) => { + span_mirbug!( + self, + rvalue, + "accessed field #{} but variant only has {}", + i, + field_count + ); + continue; + } + }; + let operand_ty = operand.ty(mir, tcx); + if let Err(terr) = + self.sub_types(operand_ty, field_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + rvalue, + "{:?} is not a subtype of {:?}: {:?}", + operand_ty, + field_ty, + terr + ); + } + } + } + + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); @@ -737,58 +1134,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_local(mir, local, local_decl); } - for block in mir.basic_blocks() { - for stmt in &block.statements { + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + let mut location = Location { + block, + statement_index: 0, + }; + for stmt in &block_data.statements { if stmt.source_info.span != DUMMY_SP { self.last_span = stmt.source_info.span; } - self.check_stmt(mir, stmt); + self.check_stmt(mir, stmt, location); + location.statement_index += 1; } - self.check_terminator(mir, block.terminator()); - self.check_iscleanup(mir, block); + self.check_terminator(mir, block_data.terminator(), location); + self.check_iscleanup(mir, block_data); } } - - fn normalize(&mut self, value: &T) -> T - where T: fmt::Debug + TypeFoldable<'tcx> + fn normalize(&mut self, value: &T, location: Location) -> T + where + T: fmt::Debug + TypeFoldable<'tcx>, { - let mut selcx = traits::SelectionContext::new(self.infcx); - let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, self.param_env, cause, value); - - debug!("normalize: value={:?} obligations={:?}", - value, - obligations); - - let fulfill_cx = &mut self.fulfillment_cx; - for obligation in obligations { - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - } - - value - } - - fn verify_obligations(&mut self, mir: &Mir<'tcx>) { - self.last_span = mir.span; - if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", - e); - } + self.fully_perform_op(location.at_self(), |this| { + let mut selcx = traits::SelectionContext::new(this.infcx); + let cause = traits::ObligationCause::misc(this.last_span, ast::CRATE_NODE_ID); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, this.param_env, cause, value); + Ok(InferOk { value, obligations }) + }).unwrap() } } pub struct TypeckMir; impl MirPass for TypeckMir { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &mut Mir<'tcx>) { - let item_id = src.item_id(); - let def_id = tcx.hir.local_def_id(item_id); + fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { + let def_id = src.def_id; + let id = tcx.hir.as_local_node_id(def_id).unwrap(); debug!("run_pass: {:?}", def_id); if tcx.sess.err_count() > 0 { @@ -798,17 +1181,44 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let mut checker = TypeChecker::new(&infcx, item_id, param_env); - { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - if verifier.errors_reported { - // don't do further checks to avoid ICEs - return; - } - } - checker.typeck_mir(mir); - checker.verify_obligations(mir); + let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + + // For verification purposes, we just ignore the resulting + // region constraint sets. Not our problem. =) }); } } + +trait AtLocation { + /// Creates a `Locations` where `self` is both the from-location + /// and the at-location. This means that any required region + /// relationships must hold upon entering the statement/terminator + /// indicated by `self`. This is typically used when processing + /// "inputs" to the given location. + fn at_self(self) -> Locations; + + /// Creates a `Locations` where `self` is the from-location and + /// its successor within the block is the at-location. This means + /// that any required region relationships must hold only upon + /// **exiting** the statement/terminator indicated by `self`. This + /// is for example used when you have a `place = rv` statement: it + /// indicates that the `typeof(rv) <: typeof(place)` as of the + /// **next** statement. + fn at_successor_within_block(self) -> Locations; +} + +impl AtLocation for Location { + fn at_self(self) -> Locations { + Locations { + from_location: self, + at_location: self, + } + } + + fn at_successor_within_block(self) -> Locations { + Locations { + from_location: self, + at_location: self.successor_within_block(), + } + } +} diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs new file mode 100644 index 0000000000000..d1410210bda96 --- /dev/null +++ b/src/librustc_mir/util/alignment.rs @@ -0,0 +1,74 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use rustc::ty::{self, TyCtxt}; +use rustc::mir::*; + +/// Return `true` if this place is allowed to be less aligned +/// than its containing struct (because it is within a packed +/// struct). +pub fn is_disaligned<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &L, + param_env: ty::ParamEnv<'tcx>, + place: &Place<'tcx>) + -> bool + where L: HasLocalDecls<'tcx> +{ + debug!("is_disaligned({:?})", place); + if !is_within_packed(tcx, local_decls, place) { + debug!("is_disaligned({:?}) - not within packed", place); + return false + } + + let ty = place.ty(local_decls, tcx).to_ty(tcx); + match tcx.layout_raw(param_env.and(ty)) { + Ok(layout) if layout.align.abi() == 1 => { + // if the alignment is 1, the type can't be further + // disaligned. + debug!("is_disaligned({:?}) - align = 1", place); + false + } + _ => { + debug!("is_disaligned({:?}) - true", place); + true + } + } +} + +fn is_within_packed<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &L, + place: &Place<'tcx>) + -> bool + where L: HasLocalDecls<'tcx> +{ + let mut place = place; + while let &Place::Projection(box Projection { + ref base, ref elem + }) = place { + match *elem { + // encountered a Deref, which is ABI-aligned + ProjectionElem::Deref => break, + ProjectionElem::Field(..) => { + let ty = base.ty(local_decls, tcx).to_ty(tcx); + match ty.sty { + ty::TyAdt(def, _) if def.repr.packed() => { + return true + } + _ => {} + } + } + _ => {} + } + place = base; + } + + false +} diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 9de3072658660..00248400c553c 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -9,7 +9,8 @@ // except according to those terms. use rustc::ty::{self, TyCtxt}; -use rustc_errors::DiagnosticBuilder; +use rustc::session::config::BorrowckMode; +use rustc_errors::{DiagnosticBuilder, DiagnosticId}; use syntax_pos::{MultiSpan, Span}; use std::fmt; @@ -19,20 +20,34 @@ pub enum Origin { Ast, Mir } impl fmt::Display for Origin { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - match *self { - Origin::Mir => write!(w, " (Mir)"), - Origin::Ast => ty::tls::with_opt(|opt_tcx| { - // If user passed `-Z borrowck-mir`, then include an - // AST origin as part of the error report - if let Some(tcx) = opt_tcx { - if tcx.sess.opts.debugging_opts.borrowck_mir { - return write!(w, " (Ast)"); - } - } - // otherwise, do not include the origin (i.e., print - // nothing at all) - Ok(()) - }), + // If the user passed `-Z borrowck=compare`, then include + // origin info as part of the error report, + // otherwise + let display_origin = ty::tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + tcx.sess.opts.borrowck_mode == BorrowckMode::Compare + } else { + false + } + }); + if display_origin { + match *self { + Origin::Mir => write!(w, " (Mir)"), + Origin::Ast => write!(w, " (Ast)"), + } + } else { + // Print no origin info + Ok(()) + } + } +} + +impl Origin { + /// Whether we should emit errors for the origin in the given mode + pub fn should_emit_errors(self, mode: BorrowckMode) -> bool { + match self { + Origin::Ast => mode.use_ast(), + Origin::Mir => mode.use_mir(), } } } @@ -41,7 +56,7 @@ pub trait BorrowckErrors { fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a>; fn struct_span_err<'a, S: Into>(&'a self, @@ -49,20 +64,41 @@ pub trait BorrowckErrors { msg: &str) -> DiagnosticBuilder<'a>; + /// Cancels the given error if we shouldn't emit errors for a given + /// origin in the current mode. + /// + /// Always make sure that the error gets passed through this function + /// before you return it. + fn cancel_if_wrong_origin<'a>(&'a self, + diag: DiagnosticBuilder<'a>, + o: Origin) + -> DiagnosticBuilder<'a>; + fn cannot_move_when_borrowed(&self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0505, - "cannot move out of `{}` because it is borrowed{OGN}", - desc, OGN=o) + let err = struct_span_err!(self, span, E0505, + "cannot move out of `{}` because it is borrowed{OGN}", + desc, OGN=o); + self.cancel_if_wrong_origin(err, o) } - fn cannot_use_when_mutably_borrowed(&self, span: Span, desc: &str, o: Origin) + fn cannot_use_when_mutably_borrowed(&self, + span: Span, + desc: &str, + borrow_span: Span, + borrow_desc: &str, + o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0503, + let mut err = struct_span_err!(self, span, E0503, "cannot use `{}` because it was mutably borrowed{OGN}", - desc, OGN=o) + desc, OGN=o); + + err.span_label(borrow_span, format!("borrow of `{}` occurs here", borrow_desc)); + err.span_label(span, format!("use of borrowed `{}`", borrow_desc)); + + self.cancel_if_wrong_origin(err, o) } fn cannot_act_on_uninitialized_variable(&self, @@ -72,56 +108,118 @@ pub trait BorrowckErrors { o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0381, - "{} of possibly uninitialized variable: `{}`{OGN}", - verb, desc, OGN=o) + let err = struct_span_err!(self, span, E0381, + "{} of possibly uninitialized variable: `{}`{OGN}", + verb, desc, OGN=o); + self.cancel_if_wrong_origin(err, o) } fn cannot_mutably_borrow_multiply(&self, - span: Span, + new_loan_span: Span, desc: &str, opt_via: &str, + old_loan_span: Span, + old_opt_via: &str, + old_load_end_span: Option, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0499, + let mut err = struct_span_err!(self, new_loan_span, E0499, "cannot borrow `{}`{} as mutable more than once at a time{OGN}", - desc, opt_via, OGN=o) + desc, opt_via, OGN=o); + if old_loan_span == new_loan_span { + // Both borrows are happening in the same place + // Meaning the borrow is occurring in a loop + err.span_label(new_loan_span, + format!("mutable borrow starts here in previous \ + iteration of loop{}", opt_via)); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, "mutable borrow ends here"); + } + } else { + err.span_label(old_loan_span, + format!("first mutable borrow occurs here{}", old_opt_via)); + err.span_label(new_loan_span, + format!("second mutable borrow occurs here{}", opt_via)); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, "first borrow ends here"); + } + } + self.cancel_if_wrong_origin(err, o) } - fn cannot_uniquely_borrow_by_two_closures(&self, span: Span, desc: &str, o: Origin) + fn cannot_uniquely_borrow_by_two_closures(&self, + new_loan_span: Span, + desc: &str, + old_loan_span: Span, + old_load_end_span: Option, + o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0524, + let mut err = struct_span_err!(self, new_loan_span, E0524, "two closures require unique access to `{}` at the same time{OGN}", - desc, OGN=o) + desc, OGN=o); + err.span_label( + old_loan_span, + "first closure is constructed here"); + err.span_label( + new_loan_span, + "second closure is constructed here"); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label( + old_load_end_span, + "borrow from first closure ends here"); + } + self.cancel_if_wrong_origin(err, o) } fn cannot_uniquely_borrow_by_one_closure(&self, - span: Span, + new_loan_span: Span, desc_new: &str, + opt_via: &str, + old_loan_span: Span, noun_old: &str, - msg_old: &str, + old_opt_via: &str, + previous_end_span: Option, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0500, + let mut err = struct_span_err!(self, new_loan_span, E0500, "closure requires unique access to `{}` but {} is already borrowed{}{OGN}", - desc_new, noun_old, msg_old, OGN=o) + desc_new, noun_old, old_opt_via, OGN=o); + err.span_label(new_loan_span, + format!("closure construction occurs here{}", opt_via)); + err.span_label(old_loan_span, + format!("borrow occurs here{}", old_opt_via)); + if let Some(previous_end_span) = previous_end_span { + err.span_label(previous_end_span, "borrow ends here"); + } + self.cancel_if_wrong_origin(err, o) } fn cannot_reborrow_already_uniquely_borrowed(&self, - span: Span, + new_loan_span: Span, desc_new: &str, - msg_new: &str, + opt_via: &str, kind_new: &str, + old_loan_span: Span, + old_opt_via: &str, + previous_end_span: Option, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0501, + let mut err = struct_span_err!(self, new_loan_span, E0501, "cannot borrow `{}`{} as {} because previous closure \ requires unique access{OGN}", - desc_new, msg_new, kind_new, OGN=o) + desc_new, opt_via, kind_new, OGN=o); + err.span_label(new_loan_span, + format!("borrow occurs here{}", opt_via)); + err.span_label(old_loan_span, + format!("closure construction occurs here{}", old_opt_via)); + if let Some(previous_end_span) = previous_end_span { + err.span_label(previous_end_span, "borrow from closure ends here"); + } + self.cancel_if_wrong_origin(err, o) } fn cannot_reborrow_already_borrowed(&self, @@ -129,54 +227,270 @@ pub trait BorrowckErrors { desc_new: &str, msg_new: &str, kind_new: &str, + old_span: Span, noun_old: &str, kind_old: &str, msg_old: &str, + old_load_end_span: Option, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0502, + let mut err = struct_span_err!(self, span, E0502, "cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}", - desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o) + desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o); + err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new)); + err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, msg_old)); + if let Some(old_load_end_span) = old_load_end_span { + err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old)); + } + self.cancel_if_wrong_origin(err, o) } - fn cannot_assign_to_borrowed(&self, span: Span, desc: &str, o: Origin) + fn cannot_assign_to_borrowed(&self, span: Span, borrow_span: Span, desc: &str, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0506, + let mut err = struct_span_err!(self, span, E0506, "cannot assign to `{}` because it is borrowed{OGN}", - desc, OGN=o) + desc, OGN=o); + + err.span_label(borrow_span, format!("borrow of `{}` occurs here", desc)); + err.span_label(span, format!("assignment to borrowed `{}` occurs here", desc)); + + self.cancel_if_wrong_origin(err, o) } fn cannot_move_into_closure(&self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0504, - "cannot move `{}` into closure because it is borrowed{OGN}", - desc, OGN=o) + let err = struct_span_err!(self, span, E0504, + "cannot move `{}` into closure because it is borrowed{OGN}", + desc, OGN=o); + + self.cancel_if_wrong_origin(err, o) } fn cannot_reassign_immutable(&self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder { - struct_span_err!(self, span, E0384, - "re-assignment of immutable variable `{}`{OGN}", - desc, OGN=o) + let err = struct_span_err!(self, span, E0384, + "cannot assign twice to immutable variable `{}`{OGN}", + desc, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_assign(&self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0594, + "cannot assign to {}{OGN}", + desc, OGN=o); + self.cancel_if_wrong_origin(err, o) } fn cannot_assign_static(&self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder { - self.struct_span_err(span, &format!("cannot assign to immutable static item {}{OGN}", - desc, OGN=o)) + self.cannot_assign(span, &format!("immutable static item `{}`", desc), o) + } + + fn cannot_move_out_of(&self, move_from_span: Span, move_from_desc: &str, o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, move_from_span, E0507, + "cannot move out of {}{OGN}", + move_from_desc, OGN=o); + err.span_label( + move_from_span, + format!("cannot move out of {}", move_from_desc)); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_move_out_of_interior_noncopy(&self, + move_from_span: Span, + ty: ty::Ty, + is_index: bool, + o: Origin) + -> DiagnosticBuilder + { + let type_name = match (&ty.sty, is_index) { + (&ty::TyArray(_, _), true) => "array", + (&ty::TySlice(_), _) => "slice", + _ => span_bug!(move_from_span, "this path should not cause illegal move"), + }; + let mut err = struct_span_err!(self, move_from_span, E0508, + "cannot move out of type `{}`, \ + a non-copy {}{OGN}", + ty, type_name, OGN=o); + err.span_label(move_from_span, "cannot move out of here"); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_move_out_of_interior_of_drop(&self, + move_from_span: Span, + container_ty: ty::Ty, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, move_from_span, E0509, + "cannot move out of type `{}`, \ + which implements the `Drop` trait{OGN}", + container_ty, OGN=o); + err.span_label(move_from_span, "cannot move out of here"); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_act_on_moved_value(&self, + use_span: Span, + verb: &str, + optional_adverb_for_moved: &str, + moved_path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, use_span, E0382, + "{} of {}moved value: `{}`{OGN}", + verb, optional_adverb_for_moved, moved_path, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_partially_reinit_an_uninit_struct(&self, + span: Span, + uninit_path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, + span, + E0383, + "partial reinitialization of uninitialized structure `{}`{OGN}", + uninit_path, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn closure_cannot_assign_to_borrowed(&self, + span: Span, + descr: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0595, "closure cannot assign to {}{OGN}", + descr, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_borrow_path_as_mutable(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{OGN}", + path, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_borrow_across_generator_yield(&self, + span: Span, + yield_span: Span, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, + span, + E0626, + "borrow may still be in use when generator yields{OGN}", + OGN=o); + err.span_label(yield_span, "possible yield occurs here"); + + self.cancel_if_wrong_origin(err, o) + } + + fn path_does_not_live_long_enough(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0597, "{} does not live long enough{OGN}", + path, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn lifetime_too_short_for_reborrow(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0598, + "lifetime of {} is too short to guarantee \ + its contents can be safely reborrowed{OGN}", + path, OGN=o); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_act_on_capture_in_sharable_fn(&self, + span: Span, + bad_thing: &str, + help: (Span, &str), + o: Origin) + -> DiagnosticBuilder + { + let (help_span, help_msg) = help; + let mut err = struct_span_err!(self, span, E0387, + "{} in a captured outer variable in an `Fn` closure{OGN}", + bad_thing, OGN=o); + err.span_help(help_span, help_msg); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_assign_into_immutable_reference(&self, + span: Span, + bad_thing: &str, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, span, E0389, "{} in a `&` reference{OGN}", + bad_thing, OGN=o); + err.span_label(span, "assignment into an immutable reference"); + + self.cancel_if_wrong_origin(err, o) + } + + fn cannot_capture_in_long_lived_closure(&self, + closure_span: Span, + borrowed_path: &str, + capture_span: Span, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, closure_span, E0373, + "closure may outlive the current function, \ + but it borrows {}, \ + which is owned by the current function{OGN}", + borrowed_path, OGN=o); + err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) + .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); + + self.cancel_if_wrong_origin(err, o) } } -impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> { +impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> { fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.sess.struct_span_err_with_code(sp, msg, code) @@ -189,4 +503,15 @@ impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> { { self.sess.struct_span_err(sp, msg) } + + fn cancel_if_wrong_origin<'a>(&'a self, + mut diag: DiagnosticBuilder<'a>, + o: Origin) + -> DiagnosticBuilder<'a> + { + if !o.should_emit_errors(self.sess.opts.borrowck_mode) { + self.sess.diagnostic().cancel(&mut diag); + } + diag + } } diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/util/def_use.rs index bd9fb4bc3cc5f..07de346e795f9 100644 --- a/src/librustc_mir/util/def_use.rs +++ b/src/librustc_mir/util/def_use.rs @@ -11,10 +11,12 @@ //! Def-use analysis. use rustc::mir::{Local, Location, Mir}; -use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor}; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; use rustc_data_structures::indexed_vec::IndexVec; use std::marker::PhantomData; use std::mem; +use std::slice; +use std::iter; pub struct DefUseAnalysis<'tcx> { info: IndexVec>, @@ -27,7 +29,7 @@ pub struct Info<'tcx> { #[derive(Clone)] pub struct Use<'tcx> { - pub context: LvalueContext<'tcx>, + pub context: PlaceContext<'tcx>, pub location: Location, } @@ -39,6 +41,8 @@ impl<'tcx> DefUseAnalysis<'tcx> { } pub fn analyze(&mut self, mir: &Mir<'tcx>) { + self.clear(); + let mut finder = DefUseFinder { info: mem::replace(&mut self.info, IndexVec::new()), }; @@ -46,18 +50,24 @@ impl<'tcx> DefUseAnalysis<'tcx> { self.info = finder.info } + fn clear(&mut self) { + for info in &mut self.info { + info.clear(); + } + } + pub fn local_info(&self, local: Local) -> &Info<'tcx> { &self.info[local] } fn mutate_defs_and_uses(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F) where F: for<'a> FnMut(&'a mut Local, - LvalueContext<'tcx>, + PlaceContext<'tcx>, Location) { - for lvalue_use in &self.info[local].defs_and_uses { + for place_use in &self.info[local].defs_and_uses { MutateUseVisitor::new(local, &mut callback, - mir).visit_location(mir, lvalue_use.location) + mir).visit_location(mir, place_use.location) } } @@ -77,7 +87,7 @@ struct DefUseFinder<'tcx> { impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> { fn visit_local(&mut self, &local: &Local, - context: LvalueContext<'tcx>, + context: PlaceContext<'tcx>, location: Location) { self.info[local].defs_and_uses.push(Use { context, @@ -93,19 +103,29 @@ impl<'tcx> Info<'tcx> { } } + fn clear(&mut self) { + self.defs_and_uses.clear(); + } + pub fn def_count(&self) -> usize { - self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count() + self.defs_and_uses.iter().filter(|place_use| place_use.context.is_mutating_use()).count() } pub fn def_count_not_including_drop(&self) -> usize { - self.defs_and_uses.iter().filter(|lvalue_use| { - lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop() - }).count() + self.defs_not_including_drop().count() + } + + pub fn defs_not_including_drop( + &self, + ) -> iter::Filter>, fn(&&Use<'tcx>) -> bool> { + self.defs_and_uses.iter().filter(|place_use| { + place_use.context.is_mutating_use() && !place_use.context.is_drop() + }) } pub fn use_count(&self) -> usize { - self.defs_and_uses.iter().filter(|lvalue_use| { - lvalue_use.context.is_nonmutating_use() + self.defs_and_uses.iter().filter(|place_use| { + place_use.context.is_nonmutating_use() }).count() } } @@ -119,7 +139,7 @@ struct MutateUseVisitor<'tcx, F> { impl<'tcx, F> MutateUseVisitor<'tcx, F> { fn new(query: Local, callback: F, _: &Mir<'tcx>) -> MutateUseVisitor<'tcx, F> - where F: for<'a> FnMut(&'a mut Local, LvalueContext<'tcx>, Location) { + where F: for<'a> FnMut(&'a mut Local, PlaceContext<'tcx>, Location) { MutateUseVisitor { query, callback, @@ -129,10 +149,10 @@ impl<'tcx, F> MutateUseVisitor<'tcx, F> { } impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F> - where F: for<'a> FnMut(&'a mut Local, LvalueContext<'tcx>, Location) { + where F: for<'a> FnMut(&'a mut Local, PlaceContext<'tcx>, Location) { fn visit_local(&mut self, local: &mut Local, - context: LvalueContext<'tcx>, + context: PlaceContext<'tcx>, location: Location) { if *local == self.query { (self.callback)(local, context, location) diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 3b9772079adb9..3331bc9e59e0b 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -19,7 +19,7 @@ use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; -use std::iter; +use std::{iter, u32}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum DropFlagState { @@ -95,6 +95,7 @@ pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug { fn field_subpath(&self, path: Self::Path, field: Field) -> Option; fn deref_subpath(&self, path: Self::Path) -> Option; fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option; + fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option; } #[derive(Debug)] @@ -105,7 +106,7 @@ struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D> source_info: SourceInfo, - lvalue: &'l Lvalue<'tcx>, + place: &'l Place<'tcx>, path: D::Path, succ: BasicBlock, unwind: Unwind, @@ -114,7 +115,7 @@ struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D> pub fn elaborate_drop<'b, 'tcx, D>( elaborator: &mut D, source_info: SourceInfo, - lvalue: &Lvalue<'tcx>, + place: &Place<'tcx>, path: D::Path, succ: BasicBlock, unwind: Unwind, @@ -122,15 +123,15 @@ pub fn elaborate_drop<'b, 'tcx, D>( where D: DropElaborator<'b, 'tcx> { DropCtxt { - elaborator, source_info, lvalue, path, succ, unwind + elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) } impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> where D: DropElaborator<'b, 'tcx> { - fn lvalue_ty(&self, lvalue: &Lvalue<'tcx>) -> Ty<'tcx> { - lvalue.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx()) + fn place_ty(&self, place: &Place<'tcx>) -> Ty<'tcx> { + place.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx()) } fn tcx(&self) -> TyCtxt<'b, 'tcx, 'tcx> { @@ -169,7 +170,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let loc = self.terminator_loc(bb); self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop { - location: self.lvalue.clone(), + location: self.place.clone(), target: self.succ, unwind: self.unwind.into_option(), }); @@ -191,14 +192,14 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> } } - /// Return the lvalue and move path for each field of `variant`, + /// Return the place and move path for each field of `variant`, /// (the move path is `None` if the field is a rest field). fn move_paths_for_fields(&self, - base_lv: &Lvalue<'tcx>, + base_place: &Place<'tcx>, variant_path: D::Path, variant: &'tcx ty::VariantDef, substs: &'tcx Substs<'tcx>) - -> Vec<(Lvalue<'tcx>, Option)> + -> Vec<(Place<'tcx>, Option)> { variant.fields.iter().enumerate().map(|(i, f)| { let field = Field::new(i); @@ -209,32 +210,32 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> &f.ty(self.tcx(), substs), self.elaborator.param_env() ); - (base_lv.clone().field(field, field_ty), subpath) + (base_place.clone().field(field, field_ty), subpath) }).collect() } fn drop_subpath(&mut self, - lvalue: &Lvalue<'tcx>, + place: &Place<'tcx>, path: Option, succ: BasicBlock, unwind: Unwind) -> BasicBlock { if let Some(path) = path { - debug!("drop_subpath: for std field {:?}", lvalue); + debug!("drop_subpath: for std field {:?}", place); DropCtxt { elaborator: self.elaborator, source_info: self.source_info, - path, lvalue, succ, unwind, + path, place, succ, unwind, }.elaborated_drop_block() } else { - debug!("drop_subpath: for rest field {:?}", lvalue); + debug!("drop_subpath: for rest field {:?}", place); DropCtxt { elaborator: self.elaborator, source_info: self.source_info, - lvalue, succ, unwind, + place, succ, unwind, // Using `self.path` here to condition the drop on // our own drop flag. path: self.path @@ -251,13 +252,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn drop_halfladder(&mut self, unwind_ladder: &[Unwind], mut succ: BasicBlock, - fields: &[(Lvalue<'tcx>, Option)]) + fields: &[(Place<'tcx>, Option)]) -> Vec { Some(succ).into_iter().chain( fields.iter().rev().zip(unwind_ladder) - .map(|(&(ref lv, path), &unwind_succ)| { - succ = self.drop_subpath(lv, path, succ, unwind_succ); + .map(|(&(ref place, path), &unwind_succ)| { + succ = self.drop_subpath(place, path, succ, unwind_succ); succ }) ).collect() @@ -294,7 +295,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// NOTE: this does not clear the master drop flag, so you need /// to point succ/unwind on a `drop_ladder_bottom`. fn drop_ladder<'a>(&mut self, - fields: Vec<(Lvalue<'tcx>, Option)>, + fields: Vec<(Place<'tcx>, Option)>, succ: BasicBlock, unwind: Unwind) -> (BasicBlock, Unwind) @@ -302,8 +303,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> debug!("drop_ladder({:?}, {:?})", self, fields); let mut fields = fields; - fields.retain(|&(ref lvalue, _)| { - self.lvalue_ty(lvalue).needs_drop(self.tcx(), self.elaborator.param_env()) + fields.retain(|&(ref place, _)| { + self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env()) }); debug!("drop_ladder - fields needing drop: {:?}", fields); @@ -328,7 +329,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> debug!("open_drop_for_tuple({:?}, {:?})", self, tys); let fields = tys.iter().enumerate().map(|(i, &ty)| { - (self.lvalue.clone().field(Field::new(i), ty), + (self.place.clone().field(Field::new(i), ty), self.elaborator.field_subpath(self.path, Field::new(i))) }).collect(); @@ -340,7 +341,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> { debug!("open_drop_for_box({:?}, {:?})", self, ty); - let interior = self.lvalue.clone().deref(); + let interior = self.place.clone().deref(); let interior_path = self.elaborator.deref_subpath(self.path); let succ = self.succ; // FIXME(#6393) @@ -384,9 +385,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> substs: &'tcx Substs<'tcx>) -> (BasicBlock, Unwind) { let (succ, unwind) = self.drop_ladder_bottom(); - if adt.variants.len() == 1 { + if !adt.is_enum() { let fields = self.move_paths_for_fields( - self.lvalue, + self.place, self.path, &adt.variants[0], substs @@ -416,11 +417,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let subpath = self.elaborator.downcast_subpath( self.path, variant_index); if let Some(variant_path) = subpath { - let base_lv = self.lvalue.clone().elem( + let base_place = self.place.clone().elem( ProjectionElem::Downcast(adt, variant_index) ); let fields = self.move_paths_for_fields( - &base_lv, + &base_place, variant_path, &adt.variants[variant_index], substs); @@ -491,14 +492,14 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // discriminant after it is free-ed, because that // way lies only trouble. let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); - let discr = Lvalue::Local(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); + let discr = Place::Local(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.place.clone()); let switch_block = BasicBlockData { statements: vec![self.assign(&discr, discr_rv)], terminator: Some(Terminator { source_info: self.source_info, kind: TerminatorKind::SwitchInt { - discr: Operand::Consume(discr), + discr: Operand::Move(discr), switch_ty: discr_ty, values: From::from(values.to_owned()), targets: blocks, @@ -517,26 +518,26 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let tcx = self.tcx(); let drop_trait = tcx.lang_items().drop_trait().unwrap(); let drop_fn = tcx.associated_items(drop_trait).next().unwrap(); - let ty = self.lvalue_ty(self.lvalue); + let ty = self.place_ty(self.place); let substs = tcx.mk_substs(iter::once(Kind::from(ty))); let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::MutMutable }); - let ref_lvalue = self.new_temp(ref_ty); - let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + let ref_place = self.new_temp(ref_ty); + let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); let result = BasicBlockData { statements: vec![self.assign( - &Lvalue::Local(ref_lvalue), - Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut, self.lvalue.clone()) + &Place::Local(ref_place), + Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut, self.place.clone()) )], terminator: Some(Terminator { kind: TerminatorKind::Call { func: Operand::function_handle(tcx, drop_fn.def_id, substs, self.source_info.span), - args: vec![Operand::Consume(Lvalue::Local(ref_lvalue))], + args: vec![Operand::Move(Place::Local(ref_place))], destination: Some((unit_temp, succ)), cleanup: unwind.into_option(), }, @@ -566,38 +567,39 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn drop_loop(&mut self, succ: BasicBlock, cur: Local, - length_or_end: &Lvalue<'tcx>, + length_or_end: &Place<'tcx>, ety: Ty<'tcx>, unwind: Unwind, ptr_based: bool) -> BasicBlock { - let use_ = |lv: &Lvalue<'tcx>| Operand::Consume(lv.clone()); + let copy = |place: &Place<'tcx>| Operand::Copy(place.clone()); + let move_ = |place: &Place<'tcx>| Operand::Move(place.clone()); let tcx = self.tcx(); let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::MutMutable }); - let ptr = &Lvalue::Local(self.new_temp(ref_ty)); - let can_go = &Lvalue::Local(self.new_temp(tcx.types.bool)); + let ptr = &Place::Local(self.new_temp(ref_ty)); + let can_go = &Place::Local(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); let (ptr_next, cur_next) = if ptr_based { - (Rvalue::Use(use_(&Lvalue::Local(cur))), - Rvalue::BinaryOp(BinOp::Offset, use_(&Lvalue::Local(cur)), one)) + (Rvalue::Use(copy(&Place::Local(cur))), + Rvalue::BinaryOp(BinOp::Offset, copy(&Place::Local(cur)), one)) } else { (Rvalue::Ref( tcx.types.re_erased, BorrowKind::Mut, - self.lvalue.clone().index(cur)), - Rvalue::BinaryOp(BinOp::Add, use_(&Lvalue::Local(cur)), one)) + self.place.clone().index(cur)), + Rvalue::BinaryOp(BinOp::Add, copy(&Place::Local(cur)), one)) }; let drop_block = BasicBlockData { statements: vec![ self.assign(ptr, ptr_next), - self.assign(&Lvalue::Local(cur), cur_next) + self.assign(&Place::Local(cur), cur_next) ], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { @@ -611,13 +613,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let loop_block = BasicBlockData { statements: vec![ self.assign(can_go, Rvalue::BinaryOp(BinOp::Eq, - use_(&Lvalue::Local(cur)), - use_(length_or_end))) + copy(&Place::Local(cur)), + copy(length_or_end))) ], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { source_info: self.source_info, - kind: TerminatorKind::if_(tcx, use_(can_go), succ, drop_block) + kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block) }) }; let loop_block = self.elaborator.patch().new_block(loop_block); @@ -631,8 +633,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> loop_block } - fn open_drop_for_array(&mut self, ety: Ty<'tcx>) -> BasicBlock { - debug!("open_drop_for_array({:?})", ety); + fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option) -> BasicBlock { + debug!("open_drop_for_array({:?}, {:?})", ety, opt_size); // if size_of::() == 0 { // index_based_loop @@ -640,16 +642,34 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // ptr_based_loop // } - let tcx = self.tcx(); + if let Some(size) = opt_size { + assert!(size <= (u32::MAX as u64), + "move out check doesn't implemented for array bigger then u32"); + let size = size as u32; + let fields: Vec<(Place<'tcx>, Option)> = (0..size).map(|i| { + (self.place.clone().elem(ProjectionElem::ConstantIndex{ + offset: i, + min_length: size, + from_end: false + }), + self.elaborator.array_subpath(self.path, i, size)) + }).collect(); + + if fields.iter().any(|(_,path)| path.is_some()) { + let (succ, unwind) = self.drop_ladder_bottom(); + return self.drop_ladder(fields, succ, unwind).0 + } + } - let use_ = |lv: &Lvalue<'tcx>| Operand::Consume(lv.clone()); - let size = &Lvalue::Local(self.new_temp(tcx.types.usize)); - let size_is_zero = &Lvalue::Local(self.new_temp(tcx.types.bool)); + let move_ = |place: &Place<'tcx>| Operand::Move(place.clone()); + let tcx = self.tcx(); + let size = &Place::Local(self.new_temp(tcx.types.usize)); + let size_is_zero = &Place::Local(self.new_temp(tcx.types.bool)); let base_block = BasicBlockData { statements: vec![ self.assign(size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), self.assign(size_is_zero, Rvalue::BinaryOp(BinOp::Eq, - use_(size), + move_(size), self.constant_usize(0))) ], is_cleanup: self.unwind.is_cleanup(), @@ -657,7 +677,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> source_info: self.source_info, kind: TerminatorKind::if_( tcx, - use_(size_is_zero), + move_(size_is_zero), self.drop_loop_pair(ety, false), self.drop_loop_pair(ety, true) ) @@ -666,7 +686,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.elaborator.patch().new_block(base_block) } - // create a pair of drop-loops of `lvalue`, which drops its contents + // create a pair of drop-loops of `place`, which drops its contents // even in the case of 1 panic. If `ptr_based`, create a pointer loop, // otherwise create an index loop. fn drop_loop_pair(&mut self, ety: Ty<'tcx>, ptr_based: bool) -> BasicBlock { @@ -679,9 +699,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }; let cur = self.new_temp(iter_ty); - let length = Lvalue::Local(self.new_temp(tcx.types.usize)); + let length = Place::Local(self.new_temp(tcx.types.usize)); let length_or_end = if ptr_based { - Lvalue::Local(self.new_temp(iter_ty)) + Place::Local(self.new_temp(iter_ty)) } else { length.clone() }; @@ -704,25 +724,25 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> unwind, ptr_based); - let cur = Lvalue::Local(cur); + let cur = Place::Local(cur); let zero = self.constant_usize(0); let mut drop_block_stmts = vec![]; - drop_block_stmts.push(self.assign(&length, Rvalue::Len(self.lvalue.clone()))); + drop_block_stmts.push(self.assign(&length, Rvalue::Len(self.place.clone()))); if ptr_based { - let tmp_ty = tcx.mk_mut_ptr(self.lvalue_ty(self.lvalue)); - let tmp = Lvalue::Local(self.new_temp(tmp_ty)); + let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place)); + let tmp = Place::Local(self.new_temp(tmp_ty)); // tmp = &LV; // cur = tmp as *mut T; // end = Offset(cur, len); drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref( - tcx.types.re_erased, BorrowKind::Mut, self.lvalue.clone() + tcx.types.re_erased, BorrowKind::Mut, self.place.clone() ))); drop_block_stmts.push(self.assign(&cur, Rvalue::Cast( - CastKind::Misc, Operand::Consume(tmp.clone()), iter_ty + CastKind::Misc, Operand::Move(tmp.clone()), iter_ty ))); drop_block_stmts.push(self.assign(&length_or_end, Rvalue::BinaryOp(BinOp::Offset, - Operand::Consume(cur.clone()), Operand::Consume(length.clone()) + Operand::Copy(cur.clone()), Operand::Move(length.clone()) ))); } else { // index = 0 (length already pushed) @@ -751,7 +771,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// This creates a "drop ladder" that drops the needed fields of the /// ADT, both in the success case or if one of the destructors fail. fn open_drop<'a>(&mut self) -> BasicBlock { - let ty = self.lvalue_ty(self.lvalue); + let ty = self.place_ty(self.place); match ty.sty { ty::TyClosure(def_id, substs) | // Note that `elaborate_drops` only drops the upvars of a generator, @@ -778,20 +798,21 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let succ = self.succ; self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) } - ty::TyArray(ety, _) | ty::TySlice(ety) => { - self.open_drop_for_array(ety) - } + ty::TyArray(ety, size) => self.open_drop_for_array( + ety, size.val.to_const_int().and_then(|v| v.to_u64())), + ty::TySlice(ety) => self.open_drop_for_array(ety, None), + _ => bug!("open drop from non-ADT `{:?}`", ty) } } - /// Return a basic block that drop an lvalue using the context + /// Return a basic block that drop a place using the context /// and path in `c`. If `mode` is something, also clear `c` /// according to it. /// /// if FLAG(self.path) /// if let Some(mode) = mode: FLAG(self.path)[mode] = false - /// drop(self.lv) + /// drop(self.place) fn complete_drop<'a>(&mut self, drop_mode: Option, succ: BasicBlock, @@ -848,13 +869,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> unwind: Unwind ) -> BasicBlock { let tcx = self.tcx(); - let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); + let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); let substs = tcx.mk_substs(iter::once(Kind::from(ty))); let call = TerminatorKind::Call { func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args: vec![Operand::Consume(self.lvalue.clone())], + args: vec![Operand::Move(self.place.clone())], destination: Some((unit_temp, target)), cleanup: None }; // FIXME(#6393) @@ -867,7 +888,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn drop_block<'a>(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = TerminatorKind::Drop { - location: self.lvalue.clone(), + location: self.place.clone(), target, unwind: unwind.into_option() }; @@ -931,7 +952,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }) } - fn assign(&self, lhs: &Lvalue<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { + fn assign(&self, lhs: &Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { Statement { source_info: self.source_info, kind: StatementKind::Assign(lhs.clone(), rhs) diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index cf13a80e677b1..85b66c29be1ad 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -14,45 +14,52 @@ use rustc::mir::*; use rustc::ty::TyCtxt; use std::fmt::Debug; use std::io::{self, Write}; -use syntax::ast::NodeId; use rustc_data_structures::indexed_vec::Idx; use super::pretty::dump_mir_def_ids; /// Write a graphviz DOT graph of a list of MIRs. -pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - single: Option, - w: &mut W) - -> io::Result<()> +pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, + single: Option, + w: &mut W) + -> io::Result<()> where W: Write { for def_id in dump_mir_def_ids(tcx, single) { - let nodeid = tcx.hir.as_local_node_id(def_id).unwrap(); let mir = &tcx.optimized_mir(def_id); + write_mir_fn_graphviz(tcx, def_id, mir, w)?; + } + Ok(()) +} - writeln!(w, "digraph Mir_{} {{", nodeid)?; +/// Write a graphviz DOT graph of the MIR. +pub fn write_mir_fn_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, + def_id: DefId, + mir: &Mir, + w: &mut W) -> io::Result<()> + where W: Write +{ + writeln!(w, "digraph Mir_{} {{", tcx.hir.as_local_node_id(def_id).unwrap())?; - // Global graph properties - writeln!(w, r#" graph [fontname="monospace"];"#)?; - writeln!(w, r#" node [fontname="monospace"];"#)?; - writeln!(w, r#" edge [fontname="monospace"];"#)?; + // Global graph properties + writeln!(w, r#" graph [fontname="monospace"];"#)?; + writeln!(w, r#" node [fontname="monospace"];"#)?; + writeln!(w, r#" edge [fontname="monospace"];"#)?; - // Graph label - write_graph_label(tcx, nodeid, mir, w)?; + // Graph label + write_graph_label(tcx, def_id, mir, w)?; - // Nodes - for (block, _) in mir.basic_blocks().iter_enumerated() { - write_node(block, mir, w)?; - } + // Nodes + for (block, _) in mir.basic_blocks().iter_enumerated() { + write_node(block, mir, w)?; + } - // Edges - for (source, _) in mir.basic_blocks().iter_enumerated() { - write_edges(source, mir, w)?; - } - writeln!(w, "}}")? + // Edges + for (source, _) in mir.basic_blocks().iter_enumerated() { + write_edges(source, mir, w)?; } - Ok(()) + writeln!(w, "}}") } /// Write a graphviz HTML-styled label for the given basic block, with @@ -128,22 +135,22 @@ fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of /// all the variables and temporaries. -fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - nid: NodeId, - mir: &Mir, - w: &mut W) - -> io::Result<()> { - write!(w, " label=(tcx: TyCtxt<'a, 'gcx, 'tcx>, + def_id: DefId, + mir: &Mir, + w: &mut W) + -> io::Result<()> { + write!(w, " label= 0 { write!(w, ", ")?; } - write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?; + write!(w, "{:?}: {}", Place::Local(arg), escape(&mir.local_decls[arg].ty))?; } - write!(w, ") -> {}", escape(mir.return_ty))?; + write!(w, ") -> {}", escape(mir.return_ty()))?; write!(w, r#"
"#)?; for local in mir.vars_and_temps_iter() { @@ -156,10 +163,10 @@ fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Some(name) = decl.name { write!(w, r#"{:?}: {}; // {}
"#, - Lvalue::Local(local), escape(&decl.ty), name)?; + Place::Local(local), escape(&decl.ty), name)?; } else { write!(w, r#"let mut {:?}: {};
"#, - Lvalue::Local(local), escape(&decl.ty))?; + Place::Local(local), escape(&decl.ty))?; } } diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index e6d3a82ff9b53..45c3fcd8a615d 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -34,177 +34,350 @@ //! doesn't matter). use rustc::mir::*; -use rustc::mir::visit::{LvalueContext, Visitor}; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc::mir::visit::{PlaceContext, Visitor}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::indexed_set::IdxSetBuf; -use util::pretty::{write_basic_block, dump_enabled, write_mir_intro}; -use rustc::mir::transform::MirSource; +use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; use rustc::ty::item_path; -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; use std::fs; use rustc::ty::TyCtxt; use std::io::{self, Write}; +use transform::MirSource; pub type LocalSet = IdxSetBuf; +/// This gives the result of the liveness analysis at the boundary of +/// basic blocks. You can use `simulate_block` to obtain the +/// intra-block results. +pub struct LivenessResult { + /// Liveness mode in use when these results were computed. + pub mode: LivenessMode, + + /// Live variables on entry to each basic block. + pub ins: IndexVec, + + /// Live variables on exit to each basic block. This is equal to + /// the union of the `ins` for each successor. + pub outs: IndexVec, +} + +#[derive(Copy, Clone, Debug)] +pub struct LivenessMode { + /// If true, then we will consider "regular uses" of a variable to be live. + /// For example, if the user writes `foo(x)`, then this is a regular use of + /// the variable `x`. + pub include_regular_use: bool, + + /// If true, then we will consider (implicit) drops of a variable + /// to be live. For example, if the user writes `{ let x = + /// vec![...]; .. }`, then the drop at the end of the block is an + /// implicit drop. + /// + /// NB. Despite its name, a call like `::std::mem::drop(x)` is + /// **not** considered a drop for this purposes, but rather a + /// regular use. + pub include_drops: bool, +} + +/// Compute which local variables are live within the given function +/// `mir`. The liveness mode `mode` determines what sorts of uses are +/// considered to make a variable live (e.g., do drops count?). +pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult { + let locals = mir.local_decls.len(); + let def_use: IndexVec<_, _> = mir.basic_blocks() + .iter() + .map(|b| block(mode, b, locals)) + .collect(); + + let mut ins: IndexVec<_, _> = mir.basic_blocks() + .indices() + .map(|_| LocalSet::new_empty(locals)) + .collect(); + let mut outs = ins.clone(); + + let mut changed = true; + let mut bits = LocalSet::new_empty(locals); + while changed { + changed = false; + + for b in mir.basic_blocks().indices().rev() { + // outs[b] = ∪ {ins of successors} + bits.clear(); + for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() { + bits.union(&ins[successor]); + } + outs[b].clone_from(&bits); + + // bits = use ∪ (bits - def) + def_use[b].apply(&mut bits); + + // update bits on entry and flag if they have changed + if ins[b] != bits { + ins[b].clone_from(&bits); + changed = true; + } + } + } + + LivenessResult { mode, ins, outs } +} + +impl LivenessResult { + /// Walks backwards through the statements/terminator in the given + /// basic block `block`. At each point within `block`, invokes + /// the callback `op` with the current location and the set of + /// variables that are live on entry to that location. + pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP) + where + OP: FnMut(Location, &LocalSet), + { + let data = &mir[block]; + + // Get a copy of the bits on exit from the block. + let mut bits = self.outs[block].clone(); + + // Start with the maximal statement index -- i.e., right before + // the terminator executes. + let mut statement_index = data.statements.len(); + + // Compute liveness right before terminator and invoke callback. + let terminator_location = Location { + block, + statement_index, + }; + let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator); + terminator_defs_uses.apply(&mut bits); + callback(terminator_location, &bits); + + // Compute liveness before each statement (in rev order) and invoke callback. + for statement in data.statements.iter().rev() { + statement_index -= 1; + let statement_location = Location { + block, + statement_index, + }; + let statement_defs_uses = self.defs_uses(mir, statement_location, statement); + statement_defs_uses.apply(&mut bits); + callback(statement_location, &bits); + } + + assert_eq!(bits, self.ins[block]); + } + + fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses + where + V: MirVisitable<'tcx>, + { + let locals = mir.local_decls.len(); + let mut visitor = DefsUsesVisitor { + mode: self.mode, + defs_uses: DefsUses { + defs: LocalSet::new_empty(locals), + uses: LocalSet::new_empty(locals), + }, + }; + + // Visit the various parts of the basic block in reverse. If we go + // forward, the logic in `add_def` and `add_use` would be wrong. + thing.apply(location, &mut visitor); + + visitor.defs_uses + } +} + +struct DefsUsesVisitor { + mode: LivenessMode, + defs_uses: DefsUses, +} + #[derive(Eq, PartialEq, Clone)] -struct BlockInfo { +struct DefsUses { defs: LocalSet, uses: LocalSet, } -struct BlockInfoVisitor { - pre_defs: LocalSet, - defs: LocalSet, - uses: LocalSet, +impl DefsUses { + fn apply(&self, bits: &mut LocalSet) -> bool { + bits.subtract(&self.defs) | bits.union(&self.uses) + } + + fn add_def(&mut self, index: Local) { + // If it was used already in the block, remove that use + // now that we found a definition. + // + // Example: + // + // // Defs = {X}, Uses = {} + // X = 5 + // // Defs = {}, Uses = {X} + // use(X) + self.uses.remove(&index); + self.defs.add(&index); + } + + fn add_use(&mut self, index: Local) { + // Inverse of above. + // + // Example: + // + // // Defs = {}, Uses = {X} + // use(X) + // // Defs = {X}, Uses = {} + // X = 5 + // // Defs = {}, Uses = {X} + // use(X) + self.defs.remove(&index); + self.uses.add(&index); + } } -impl<'tcx> Visitor<'tcx> for BlockInfoVisitor { - fn visit_local(&mut self, - &local: &Local, - context: LvalueContext<'tcx>, - _: Location) { +impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { + fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { match context { - LvalueContext::Store | + /////////////////////////////////////////////////////////////////////////// + // DEFS + + PlaceContext::Store | - // We let Call defined the result in both the success and unwind cases. - // This may not be right. - LvalueContext::Call | + // We let Call define the result in both the success and + // unwind cases. This is not really correct, however it + // does not seem to be observable due to the way that we + // generate MIR. See the test case + // `mir-opt/nll/liveness-call-subtlety.rs`. To do things + // properly, we would apply the def in call only to the + // input from the success path and not the unwind + // path. -nmatsakis + PlaceContext::Call | // Storage live and storage dead aren't proper defines, but we can ignore // values that come before them. - LvalueContext::StorageLive | - LvalueContext::StorageDead => { - self.defs.add(&local); + PlaceContext::StorageLive | + PlaceContext::StorageDead => { + self.defs_uses.add_def(local); } - LvalueContext::Projection(..) | + + /////////////////////////////////////////////////////////////////////////// + // REGULAR USES + // + // These are uses that occur *outside* of a drop. For the + // purposes of NLL, these are special in that **all** the + // lifetimes appearing in the variable must be live for each regular use. + + PlaceContext::Projection(..) | // Borrows only consider their local used at the point of the borrow. // This won't affect the results since we use this analysis for generators // and we only care about the result at suspension points. Borrows cannot // cross suspension points so this behavior is unproblematic. - LvalueContext::Borrow { .. } | - - LvalueContext::Inspect | - LvalueContext::Consume | - LvalueContext::Validate | - - // We consider drops to always be uses of locals. - // Drop eloboration should be run before this analysis otherwise - // the results might be too pessimistic. - LvalueContext::Drop => { - // Ignore uses which are already defined in this block - if !self.pre_defs.contains(&local) { - self.uses.add(&local); + PlaceContext::Borrow { .. } | + + PlaceContext::Inspect | + PlaceContext::Copy | + PlaceContext::Move | + PlaceContext::Validate => { + if self.mode.include_regular_use { + self.defs_uses.add_use(local); + } + } + + /////////////////////////////////////////////////////////////////////////// + // DROP USES + // + // These are uses that occur in a DROP (a MIR drop, not a + // call to `std::mem::drop()`). For the purposes of NLL, + // uses in drop are special because `#[may_dangle]` + // attributes can affect whether lifetimes must be live. + + PlaceContext::Drop => { + if self.mode.include_drops { + self.defs_uses.add_use(local); } } } } } -fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo { - let mut visitor = BlockInfoVisitor { - pre_defs: LocalSet::new_empty(locals), - defs: LocalSet::new_empty(locals), - uses: LocalSet::new_empty(locals), +fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses { + let mut visitor = DefsUsesVisitor { + mode, + defs_uses: DefsUses { + defs: LocalSet::new_empty(locals), + uses: LocalSet::new_empty(locals), + }, }; - let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 }; + let dummy_location = Location { + block: BasicBlock::new(0), + statement_index: 0, + }; - for statement in &b.statements { + // Visit the various parts of the basic block in reverse. If we go + // forward, the logic in `add_def` and `add_use` would be wrong. + visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location); + for statement in b.statements.iter().rev() { visitor.visit_statement(BasicBlock::new(0), statement, dummy_location); - visitor.pre_defs.union(&visitor.defs); } - visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location); - BlockInfo { - defs: visitor.defs, - uses: visitor.uses, - } + visitor.defs_uses } -// This gives the result of the liveness analysis at the boundary of basic blocks -pub struct LivenessResult { - pub ins: IndexVec, - pub outs: IndexVec, +trait MirVisitable<'tcx> { + fn apply(&self, location: Location, visitor: &mut V) + where + V: Visitor<'tcx>; } -pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { - let locals = mir.local_decls.len(); - let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| { - block(b, locals) - }).collect(); - - let copy = |from: &IndexVec, to: &mut IndexVec| { - for (i, set) in to.iter_enumerated_mut() { - set.clone_from(&from[i]); - } - }; - - let mut ins: IndexVec<_, _> = mir.basic_blocks() - .indices() - .map(|_| LocalSet::new_empty(locals)).collect(); - let mut outs = ins.clone(); - - let mut ins_ = ins.clone(); - let mut outs_ = outs.clone(); - - loop { - copy(&ins, &mut ins_); - copy(&outs, &mut outs_); - - for b in mir.basic_blocks().indices().rev() { - // out = ∪ {ins of successors} - outs[b].clear(); - for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() { - outs[b].union(&ins[successor]); - } - - // in = use ∪ (out - def) - ins[b].clone_from(&outs[b]); - ins[b].subtract(&def_use[b].defs); - ins[b].union(&def_use[b].uses); - } - - if ins_ == ins && outs_ == outs { - break; - } +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&self, location: Location, visitor: &mut V) + where + V: Visitor<'tcx>, + { + visitor.visit_statement(location.block, self, location) } +} - LivenessResult { - ins, - outs, +impl<'tcx> MirVisitable<'tcx> for Option> { + fn apply(&self, location: Location, visitor: &mut V) + where + V: Visitor<'tcx>, + { + visitor.visit_terminator(location.block, self.as_ref().unwrap(), location) } } -pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - source: MirSource, - mir: &Mir<'tcx>, - result: &LivenessResult) { +pub fn dump_mir<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + pass_name: &str, + source: MirSource, + mir: &Mir<'tcx>, + result: &LivenessResult, +) { if !dump_enabled(tcx, pass_name, source) { return; } - let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below - tcx.item_path_str(tcx.hir.local_def_id(source.item_id())) + let node_path = item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 below + tcx.item_path_str(source.def_id) }); - dump_matched_mir_node(tcx, pass_name, &node_path, - source, mir, result); + dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result); } -fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - node_path: &str, - source: MirSource, - mir: &Mir<'tcx>, - result: &LivenessResult) { +fn dump_matched_mir_node<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + pass_name: &str, + node_path: &str, + source: MirSource, + mir: &Mir<'tcx>, + result: &LivenessResult, +) { let mut file_path = PathBuf::new(); if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir { let p = Path::new(file_dir); file_path.push(p); }; - let file_name = format!("rustc.node{}{}-liveness.mir", - source.item_id(), pass_name); + let item_id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name); file_path.push(&file_name); let _ = fs::File::create(&file_path).and_then(|mut file| { writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?; @@ -216,23 +389,25 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }); } -pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - w: &mut Write, - result: &LivenessResult) - -> io::Result<()> { +pub fn write_mir_fn<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + w: &mut Write, + result: &LivenessResult, +) -> io::Result<()> { write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { let print = |w: &mut Write, prefix, result: &IndexVec| { - let live: Vec = mir.local_decls.indices() + let live: Vec = mir.local_decls + .indices() .filter(|i| result[block].contains(i)) .map(|i| format!("{:?}", i)) .collect(); writeln!(w, "{} {{{}}}", prefix, live.join(", ")) }; print(w, " ", &result.ins)?; - write_basic_block(tcx, block, mir, w)?; + write_basic_block(tcx, block, mir, &mut |_, _| Ok(()), w)?; print(w, " ", &result.outs)?; if block.index() + 1 != mir.basic_blocks().len() { writeln!(w, "")?; @@ -242,4 +417,3 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, writeln!(w, "}}")?; Ok(()) } - diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index 4b6da96824dcd..eebe5a86018ea 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -13,10 +13,12 @@ pub mod elaborate_drops; pub mod def_use; pub mod patch; +mod alignment; mod graphviz; -mod pretty; +pub(crate) mod pretty; pub mod liveness; -pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty}; +pub use self::alignment::is_disaligned; +pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; pub use self::graphviz::{write_mir_graphviz}; pub use self::graphviz::write_node_label as write_graphviz_node_label; diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs index 66607a9e0986f..9da593fb48e3b 100644 --- a/src/librustc_mir/util/patch.rs +++ b/src/librustc_mir/util/patch.rs @@ -127,8 +127,8 @@ impl<'tcx> MirPatch<'tcx> { self.new_statements.push((loc, stmt)); } - pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) { - self.add_statement(loc, StatementKind::Assign(lv, rv)); + pub fn add_assign(&mut self, loc: Location, place: Place<'tcx>, rv: Rvalue<'tcx>) { + self.add_statement(loc, StatementKind::Assign(place, rv)); } pub fn apply(self, mir: &mut Mir<'tcx>) { diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 0811783a9e57f..8a3db0eb25b99 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -11,19 +11,39 @@ use rustc::hir; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::mir::*; -use rustc::mir::transform::{MirSuite, MirPassIndex, MirSource}; use rustc::ty::TyCtxt; use rustc::ty::item_path; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::{Idx}; +use rustc_data_structures::indexed_vec::Idx; use std::fmt::Display; use std::fs; use std::io::{self, Write}; -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; +use super::graphviz::write_mir_fn_graphviz; +use transform::MirSource; const INDENT: &'static str = " "; /// Alignment for lining up comments following MIR statements -const ALIGN: usize = 40; +pub(crate) const ALIGN: usize = 40; + +/// An indication of where we are in the control flow graph. Used for printing +/// extra information in `dump_mir` +pub enum PassWhere { + /// We have not started dumping the control flow graph, but we are about to. + BeforeCFG, + + /// We just finished dumping the control flow graph. This is right before EOF + AfterCFG, + + /// We are about to start dumping the given basic block. + BeforeBlock(BasicBlock), + + /// We are just about to dump the given statement or terminator. + BeforeLocation(Location), + + /// We just dumped the given statement or terminator. + AfterLocation(Location), +} /// If the session is properly configured, dumps a human-readable /// representation of the mir into: @@ -39,63 +59,124 @@ const ALIGN: usize = 40; /// - `substring1&substring2,...` -- `&`-separated list of substrings /// that can appear in the pass-name or the `item_path_str` for the given /// node-id. If any one of the substrings match, the data is dumped out. -pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: Option<(MirSuite, MirPassIndex)>, - pass_name: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>) { +pub fn dump_mir<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + extra_data: F, +) where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, +{ if !dump_enabled(tcx, pass_name, source) { return; } - let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below - tcx.item_path_str(tcx.hir.local_def_id(source.item_id())) + let node_path = item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 below + tcx.item_path_str(source.def_id) }); - dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, - disambiguator, source, mir); - for (index, promoted_mir) in mir.promoted.iter_enumerated() { - let promoted_source = MirSource::Promoted(source.item_id(), index); - dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, disambiguator, - promoted_source, promoted_mir); - } + dump_matched_mir_node( + tcx, + pass_num, + pass_name, + &node_path, + disambiguator, + source, + mir, + extra_data, + ); } -pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - source: MirSource) - -> bool { +pub fn dump_enabled<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_name: &str, + source: MirSource, +) -> bool { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return false, Some(ref filters) => filters, }; - let node_id = source.item_id(); - let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below - tcx.item_path_str(tcx.hir.local_def_id(node_id)) + let node_path = item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 below + tcx.item_path_str(source.def_id) }); - filters.split("&") - .any(|filter| { - filter == "all" || - pass_name.contains(filter) || - node_path.contains(filter) - }) + filters.split("&").any(|filter| { + filter == "all" || pass_name.contains(filter) || node_path.contains(filter) + }) } // #41697 -- we use `with_forced_impl_filename_line()` because // `item_path_str()` would otherwise trigger `type_of`, and this can // run while we are already attempting to evaluate `type_of`. -fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: Option<(MirSuite, MirPassIndex)>, - pass_name: &str, - node_path: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>) { - let promotion_id = match source { - MirSource::Promoted(_, id) => format!("-{:?}", id), - MirSource::GeneratorDrop(_) => format!("-drop"), - _ => String::new() +fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<&Display>, + pass_name: &str, + node_path: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + mut extra_data: F, +) where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, +{ + let _: io::Result<()> = do catch { + let mut file = create_dump_file( + tcx, + "mir", + pass_num, + pass_name, + disambiguator, + source, + )?; + writeln!(file, "// MIR for `{}`", node_path)?; + writeln!(file, "// source = {:?}", source)?; + writeln!(file, "// pass_name = {}", pass_name)?; + writeln!(file, "// disambiguator = {}", disambiguator)?; + if let Some(ref layout) = mir.generator_layout { + writeln!(file, "// generator_layout = {:?}", layout)?; + } + writeln!(file, "")?; + extra_data(PassWhere::BeforeCFG, &mut file)?; + write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; + extra_data(PassWhere::AfterCFG, &mut file)?; + Ok(()) + }; + + if tcx.sess.opts.debugging_opts.dump_mir_graphviz { + let _: io::Result<()> = do catch { + let mut file = create_dump_file( + tcx, + "dot", + pass_num, + pass_name, + disambiguator, + source, + )?; + write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; + Ok(()) + }; + } +} + +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path( + tcx: TyCtxt<'_, '_, '_>, + extension: &str, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, +) -> PathBuf { + let promotion_id = match source.promoted { + Some(id) => format!("-{:?}", id), + None => String::new(), }; let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { @@ -103,41 +184,69 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } else { match pass_num { None => format!(".-------"), - Some((suite, pass_num)) => format!(".{:03}-{:03}", suite.0, pass_num.0), + Some(pass_num) => format!(".{}", pass_num), } }; let mut file_path = PathBuf::new(); + if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir { let p = Path::new(file_dir); file_path.push(p); }; - let _ = fs::create_dir_all(&file_path); - let file_name = format!("rustc.node{}{}{}.{}.{}.mir", - source.item_id(), promotion_id, pass_num, pass_name, disambiguator); + + let item_name = tcx.hir + .def_path(source.def_id) + .to_filename_friendly_no_crate(); + + let file_name = format!( + "rustc.{}{}{}.{}.{}.{}", + item_name, + promotion_id, + pass_num, + pass_name, + disambiguator, + extension, + ); + file_path.push(&file_name); - let _ = fs::File::create(&file_path).and_then(|mut file| { - writeln!(file, "// MIR for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file, "// disambiguator = {}", disambiguator)?; - if let Some(ref layout) = mir.generator_layout { - writeln!(file, "// generator_layout = {:?}", layout)?; - } - writeln!(file, "")?; - write_mir_fn(tcx, source, mir, &mut file)?; - Ok(()) - }); + + file_path +} + +/// Attempts to open a file where we should dump a given MIR or other +/// bit of MIR-related data. Used by `mir-dump`, but also by other +/// bits of code (e.g., NLL inference) that dump graphviz data or +/// other things, and hence takes the extension as an argument. +pub(crate) fn create_dump_file( + tcx: TyCtxt<'_, '_, '_>, + extension: &str, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, +) -> io::Result { + let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent)?; + } + fs::File::create(&file_path) } /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - single: Option, - w: &mut Write) - -> io::Result<()> -{ - writeln!(w, "// WARNING: This output format is intended for human consumers only")?; - writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; +pub fn write_mir_pretty<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + single: Option, + w: &mut Write, +) -> io::Result<()> { + writeln!( + w, + "// WARNING: This output format is intended for human consumers only" + )?; + writeln!( + w, + "// and is subject to change without notice. Knock yourself out." + )?; let mut first = true; for def_id in dump_mir_def_ids(tcx, single) { @@ -150,26 +259,34 @@ pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, writeln!(w, "")?; } - let id = tcx.hir.as_local_node_id(def_id).unwrap(); - let src = MirSource::from_node(tcx, id); - write_mir_fn(tcx, src, mir, w)?; + write_mir_fn(tcx, MirSource::item(def_id), mir, &mut |_, _| Ok(()), w)?; for (i, mir) in mir.promoted.iter_enumerated() { writeln!(w, "")?; - write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?; + let src = MirSource { + def_id, + promoted: Some(i), + }; + write_mir_fn(tcx, src, mir, &mut |_, _| Ok(()), w)?; } } Ok(()) } -pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - w: &mut Write) - -> io::Result<()> { +pub fn write_mir_fn<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + extra_data: &mut F, + w: &mut Write, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, +{ write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { - write_basic_block(tcx, block, mir, w)?; + extra_data(PassWhere::BeforeBlock(block), w)?; + write_basic_block(tcx, block, mir, extra_data, w)?; if block.index() + 1 != mir.basic_blocks().len() { writeln!(w, "")?; } @@ -180,52 +297,79 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } /// Write out a human-readable textual representation for the given basic block. -pub fn write_basic_block(tcx: TyCtxt, - block: BasicBlock, - mir: &Mir, - w: &mut Write) - -> io::Result<()> { +pub fn write_basic_block( + tcx: TyCtxt, + block: BasicBlock, + mir: &Mir, + extra_data: &mut F, + w: &mut Write, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, +{ let data = &mir[block]; // Basic block label at the top. - writeln!(w, "{}{:?}: {{", INDENT, block)?; + let cleanup_text = if data.is_cleanup { " // cleanup" } else { "" }; + let lbl = format!("{}{:?}: {{", INDENT, block); + writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?; // List of statements in the middle. - let mut current_location = Location { block: block, statement_index: 0 }; + let mut current_location = Location { + block: block, + statement_index: 0, + }; for statement in &data.statements { + extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_mir = format!("{0}{0}{1:?};", INDENT, statement); - writeln!(w, "{0:1$} // {2}", - indented_mir, - ALIGN, - comment(tcx, statement.source_info))?; + writeln!( + w, + "{:A$} // {:?}: {}", + indented_mir, + current_location, + comment(tcx, statement.source_info), + A = ALIGN, + )?; + extra_data(PassWhere::AfterLocation(current_location), w)?; current_location.statement_index += 1; } // Terminator at the bottom. + extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); - writeln!(w, "{0:1$} // {2}", - indented_terminator, - ALIGN, - comment(tcx, data.terminator().source_info))?; + writeln!( + w, + "{:A$} // {:?}: {}", + indented_terminator, + current_location, + comment(tcx, data.terminator().source_info), + A = ALIGN, + )?; + extra_data(PassWhere::AfterLocation(current_location), w)?; writeln!(w, "{}}}", INDENT) } fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String { - format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span)) + format!( + "scope {} at {}", + scope.index(), + tcx.sess.codemap().span_to_string(span) + ) } /// Prints user-defined variables in a scope tree. /// /// Returns the total number of variables printed. -fn write_scope_tree(tcx: TyCtxt, - mir: &Mir, - scope_tree: &FxHashMap>, - w: &mut Write, - parent: VisibilityScope, - depth: usize) - -> io::Result<()> { +fn write_scope_tree( + tcx: TyCtxt, + mir: &Mir, + scope_tree: &FxHashMap>, + w: &mut Write, + parent: VisibilityScope, + depth: usize, +) -> io::Result<()> { let indent = depth * INDENT.len(); let children = match scope_tree.get(&parent) { @@ -255,17 +399,22 @@ fn write_scope_tree(tcx: TyCtxt, }; let indent = indent + INDENT.len(); - let indented_var = format!("{0:1$}let {2}{3:?}: {4};", - INDENT, - indent, - mut_str, - local, - var.ty); - writeln!(w, "{0:1$} // \"{2}\" in {3}", - indented_var, - ALIGN, - name, - comment(tcx, source_info))?; + let indented_var = format!( + "{0:1$}let {2}{3:?}: {4:?};", + INDENT, + indent, + mut_str, + local, + var.ty + ); + writeln!( + w, + "{0:1$} // \"{2}\" in {3}", + indented_var, + ALIGN, + name, + comment(tcx, source_info) + )?; } write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?; @@ -278,11 +427,12 @@ fn write_scope_tree(tcx: TyCtxt, /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir, - w: &mut Write) - -> io::Result<()> { +pub fn write_mir_intro<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir, + w: &mut Write, +) -> io::Result<()> { write_mir_sig(tcx, src, mir, w)?; writeln!(w, " {{")?; @@ -290,21 +440,22 @@ pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut scope_tree: FxHashMap> = FxHashMap(); for (index, scope_data) in mir.visibility_scopes.iter().enumerate() { if let Some(parent) = scope_data.parent_scope { - scope_tree.entry(parent) - .or_insert(vec![]) - .push(VisibilityScope::new(index)); + scope_tree + .entry(parent) + .or_insert(vec![]) + .push(VisibilityScope::new(index)); } else { // Only the argument scope has no parent, because it's the root. assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index()); } } - // Print return pointer + // Print return place let indented_retptr = format!("{}let mut {:?}: {};", INDENT, - RETURN_POINTER, - mir.return_ty); - writeln!(w, "{0:1$} // return pointer", + RETURN_PLACE, + mir.local_decls[RETURN_PLACE].ty); + writeln!(w, "{0:1$} // return place", indented_retptr, ALIGN)?; @@ -318,24 +469,24 @@ pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Ok(()) } -fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) - -> io::Result<()> -{ - match src { - MirSource::Fn(_) => write!(w, "fn")?, - MirSource::Const(_) => write!(w, "const")?, - MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?, - MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?, - MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?, - MirSource::GeneratorDrop(_) => write!(w, "drop_glue")?, +fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> { + let id = tcx.hir.as_local_node_id(src.def_id).unwrap(); + let body_owner_kind = tcx.hir.body_owner_kind(id); + match (body_owner_kind, src.promoted) { + (_, Some(i)) => write!(w, "{:?} in", i)?, + (hir::BodyOwnerKind::Fn, _) => write!(w, "fn")?, + (hir::BodyOwnerKind::Const, _) => write!(w, "const")?, + (hir::BodyOwnerKind::Static(hir::MutImmutable), _) => write!(w, "static")?, + (hir::BodyOwnerKind::Static(hir::MutMutable), _) => write!(w, "static mut")?, } - item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere - write!(w, " {}", tcx.node_path_str(src.item_id())) + item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 elsewhere + write!(w, " {}", tcx.item_path_str(src.def_id)) })?; - match src { - MirSource::Fn(_) | MirSource::GeneratorDrop(_) => { + match (body_owner_kind, src.promoted) { + (hir::BodyOwnerKind::Fn, None) => { write!(w, "(")?; // fn argument types. @@ -343,16 +494,14 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) if i != 0 { write!(w, ", ")?; } - write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?; + write!(w, "{:?}: {}", Place::Local(arg), mir.local_decls[arg].ty)?; } - write!(w, ") -> {}", mir.return_ty) + write!(w, ") -> {}", mir.return_ty()) } - MirSource::Const(..) | - MirSource::Static(..) | - MirSource::Promoted(..) => { + (hir::BodyOwnerKind::Const, _) | (hir::BodyOwnerKind::Static(_), _) | (_, Some(_)) => { assert_eq!(mir.arg_count, 0); - write!(w, ": {} =", mir.return_ty) + write!(w, ": {} =", mir.return_ty()) } } } @@ -360,7 +509,13 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> { // Compiler-introduced temporary types. for temp in mir.temps_iter() { - writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?; + writeln!( + w, + "{}let mut {:?}: {};", + INDENT, + temp, + mir.local_decls[temp].ty + )?; } Ok(()) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index efb5b03180998..97cea5c9d6452 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -21,7 +21,6 @@ use rustc::session::Session; use syntax::ast::*; use syntax::attr; use syntax::codemap::Spanned; -use syntax::parse::token; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax_pos::Span; @@ -42,6 +41,15 @@ impl<'a> AstValidator<'a> { } } + fn invalid_non_exhaustive_attribute(&self, variant: &Variant) { + let has_non_exhaustive = variant.node.attrs.iter() + .any(|attr| attr.check_name("non_exhaustive")); + if has_non_exhaustive { + self.err_handler().span_err(variant.span, + "#[non_exhaustive] is not yet supported on variants"); + } + } + fn invalid_visibility(&self, vis: &Visibility, span: Span, note: Option<&str>) { if vis != &Visibility::Inherited { let mut err = struct_span_err!(self.session, @@ -63,7 +71,8 @@ impl<'a> AstValidator<'a> { match arg.pat.node { PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), _, None) | PatKind::Wild => {} - PatKind::Ident(..) => report_err(arg.pat.span, true), + PatKind::Ident(BindingMode::ByValue(Mutability::Mutable), _, None) => + report_err(arg.pat.span, true), _ => report_err(arg.pat.span, false), } } @@ -142,17 +151,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match ty.node { TyKind::BareFn(ref bfty) => { self.check_decl_no_pat(&bfty.decl, |span, _| { - let mut err = struct_span_err!(self.session, - span, - E0561, - "patterns aren't allowed in function pointer \ - types"); - err.span_note(span, - "this is a recent error, see issue #35203 for more details"); - err.emit(); + struct_span_err!(self.session, span, E0561, + "patterns aren't allowed in function pointer types").emit(); }); } - TyKind::TraitObject(ref bounds) => { + TyKind::TraitObject(ref bounds, ..) => { let mut any_lifetime_bounds = false; for bound in bounds { if let RegionTyParamBound(ref lifetime) = *bound { @@ -178,27 +181,27 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_ty(self, ty) } - fn visit_path(&mut self, path: &'a Path, _: NodeId) { - if path.segments.len() >= 2 && path.is_global() { - let ident = path.segments[1].identifier; - if token::Ident(ident).is_path_segment_keyword() { - self.err_handler() - .span_err(path.span, &format!("global paths cannot start with `{}`", ident)); - } - } + fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { + // Check if the path in this `use` is not generic, such as `use foo::bar;` While this + // can't happen normally thanks to the parser, a generic might sneak in if the `use` is + // built using a macro. + // + // macro_use foo { + // ($p:path) => { use $p; } + // } + // foo!(bar::baz); + use_tree.prefix.segments.iter().find(|segment| { + segment.parameters.is_some() + }).map(|segment| { + self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(), + "generic arguments in import path"); + }); - visit::walk_path(self, path) + visit::walk_use_tree(self, use_tree, id); } fn visit_item(&mut self, item: &'a Item) { match item.node { - ItemKind::Use(ref view_path) => { - let path = view_path.node.path(); - path.segments.iter().find(|segment| segment.parameters.is_some()).map(|segment| { - self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(), - "generic arguments in import path"); - }); - } ItemKind::Impl(.., Some(..), _, ref impl_items) => { self.invalid_visibility(&item.vis, item.span, None); for impl_item in impl_items { @@ -213,7 +216,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { item.span, Some("place qualifiers on individual impl items instead")); } - ItemKind::DefaultImpl(..) => { + ItemKind::AutoImpl(..) => { self.invalid_visibility(&item.vis, item.span, None); } ItemKind::ForeignMod(..) => { @@ -224,23 +227,43 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::Enum(ref def, _) => { for variant in &def.variants { + self.invalid_non_exhaustive_attribute(variant); for field in variant.node.data.fields() { self.invalid_visibility(&field.vis, field.span, None); } } } - ItemKind::Trait(.., ref bounds, ref trait_items) => { + ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => { + if is_auto == IsAuto::Yes { + // Auto traits cannot have generics, super traits nor contain items. + if !generics.ty_params.is_empty() { + self.err_handler().span_err(item.span, + "auto traits cannot have generics"); + } + if !bounds.is_empty() { + self.err_handler().span_err(item.span, + "auto traits cannot have super traits"); + } + if !trait_items.is_empty() { + self.err_handler().span_err(item.span, + "auto traits cannot contain items"); + } + } self.no_questions_in_bounds(bounds, "supertraits", true); for trait_item in trait_items { if let TraitItemKind::Method(ref sig, ref block) = trait_item.node { self.check_trait_fn_not_const(sig.constness); if block.is_none() { - self.check_decl_no_pat(&sig.decl, |span, _| { - self.session.buffer_lint( - lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY, - trait_item.id, span, - "patterns aren't allowed in methods \ - without bodies"); + self.check_decl_no_pat(&sig.decl, |span, mut_ident| { + if mut_ident { + self.session.buffer_lint( + lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY, + trait_item.id, span, + "patterns aren't allowed in methods without bodies"); + } else { + struct_span_err!(self.session, span, E0642, + "patterns aren't allowed in methods without bodies").emit(); + } }); } } @@ -274,21 +297,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match fi.node { ForeignItemKind::Fn(ref decl, _) => { - self.check_decl_no_pat(decl, |span, is_recent| { - let mut err = struct_span_err!(self.session, - span, - E0130, - "patterns aren't allowed in foreign function \ - declarations"); - err.span_label(span, "pattern not allowed in foreign function"); - if is_recent { - err.span_note(span, - "this is a recent error, see issue #35203 for more details"); - } - err.emit(); + self.check_decl_no_pat(decl, |span, _| { + struct_span_err!(self.session, span, E0130, + "patterns aren't allowed in foreign function declarations") + .span_label(span, "pattern not allowed in foreign function").emit(); }); } - ForeignItemKind::Static(..) => {} + ForeignItemKind::Static(..) | ForeignItemKind::Ty => {} } visit::walk_foreign_item(self, fi) diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 547d63fc3d4aa..776b5f3c984f1 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -37,22 +37,80 @@ use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; -use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::maps::{queries, Providers}; use rustc::ty::subst::Substs; use rustc::traits::Reveal; use rustc::util::common::ErrorReported; -use rustc::util::nodemap::NodeSet; +use rustc::util::nodemap::{ItemLocalSet, NodeSet}; use rustc::lint::builtin::CONST_ERR; - use rustc::hir::{self, PatKind, RangeEnd}; +use std::rc::Rc; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use std::collections::hash_map::Entry; use std::cmp::Ordering; +pub fn provide(providers: &mut Providers) { + *providers = Providers { + rvalue_promotable_map, + const_is_rvalue_promotable_to_static, + ..*providers + }; +} + +pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + for &body_id in &tcx.hir.krate().body_ids { + let def_id = tcx.hir.body_owner_def_id(body_id); + tcx.const_is_rvalue_promotable_to_static(def_id); + } + tcx.sess.abort_if_errors(); +} + +fn const_is_rvalue_promotable_to_static<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool +{ + assert!(def_id.is_local()); + + let node_id = tcx.hir.as_local_node_id(def_id) + .expect("rvalue_promotable_map invoked with non-local def-id"); + let body_id = tcx.hir.body_owned_by(node_id); + let body_hir_id = tcx.hir.node_to_hir_id(body_id.node_id); + tcx.rvalue_promotable_map(def_id).contains(&body_hir_id.local_id) +} + +fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Rc +{ + let outer_def_id = tcx.closure_base_def_id(def_id); + if outer_def_id != def_id { + return tcx.rvalue_promotable_map(outer_def_id); + } + + let mut visitor = CheckCrateVisitor { + tcx, + tables: &ty::TypeckTables::empty(None), + in_fn: false, + in_static: false, + promotable: false, + mut_rvalue_borrows: NodeSet(), + param_env: ty::ParamEnv::empty(Reveal::UserFacing), + identity_substs: Substs::empty(), + result: ItemLocalSet(), + }; + + // `def_id` should be a `Body` owner + let node_id = tcx.hir.as_local_node_id(def_id) + .expect("rvalue_promotable_map invoked with non-local def-id"); + let body_id = tcx.hir.body_owned_by(node_id); + visitor.visit_nested_body(body_id); + + Rc::new(visitor.result) +} + struct CheckCrateVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, in_fn: bool, @@ -62,6 +120,7 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> { param_env: ty::ParamEnv<'tcx>, identity_substs: &'tcx Substs<'tcx>, tables: &'a ty::TypeckTables<'tcx>, + result: ItemLocalSet, } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { @@ -80,8 +139,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { self.tcx.lint_node(CONST_ERR, expr.id, expr.span, - &format!("constant evaluation error: {}. This will \ - become a HARD ERROR in the future", + &format!("constant evaluation error: {}", err.description().into_oneline())); } } @@ -109,18 +167,11 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + // note that we *do* visit nested bodies, because we override `visit_nested_body` below NestedVisitorMap::None } 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. - entry.insert(false); - } - } - let item_id = self.tcx.hir.body_owner(body_id); let item_def_id = self.tcx.hir.local_def_id(item_id); @@ -132,9 +183,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.in_fn = false; self.in_static = false; - match MirSource::from_node(self.tcx, item_id) { - MirSource::Fn(_) => self.in_fn = true, - MirSource::Static(_, _) => self.in_static = true, + match self.tcx.hir.body_owner_kind(item_id) { + hir::BodyOwnerKind::Fn => self.in_fn = true, + hir::BodyOwnerKind::Static(_) => self.in_static = true, _ => {} }; @@ -151,7 +202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { let tcx = self.tcx; let param_env = self.param_env; let region_scope_tree = self.tcx.region_scope_tree(item_def_id); - euv::ExprUseVisitor::new(self, tcx, param_env, ®ion_scope_tree, self.tables) + euv::ExprUseVisitor::new(self, tcx, param_env, ®ion_scope_tree, self.tables, None) .consume_body(body); self.visit_body(body); @@ -270,7 +321,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } } - self.tcx.rvalue_promotable_to_static.borrow_mut().insert(ex.id, self.promotable); + if self.promotable { + self.result.insert(ex.hir_id.local_id); + } self.promotable &= outer; } } @@ -371,16 +424,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node let promotable = if v.tcx.trait_of_item(did).is_some() { // Don't peek inside trait associated constants. false - } else if let Some(node_id) = v.tcx.hir.as_local_node_id(did) { - match v.tcx.hir.maybe_body_owned_by(node_id) { - Some(body) => { - v.visit_nested_body(body); - v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id] - } - None => false - } } else { - v.tcx.const_is_rvalue_promotable_to_static(did) + queries::const_is_rvalue_promotable_to_static::try_get(v.tcx, e.span, did) + .unwrap_or_else(|mut err| { + // A cycle between constants ought to be reported elsewhere. + err.cancel(); + v.tcx.sess.delay_span_bug( + e.span, + &format!("cycle encountered during const qualification: {:?}", + did)); + false + }) }; // Just in case the type is more specific than the definition, @@ -513,20 +567,6 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp } } -pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - tcx.hir.krate().visit_all_item_likes(&mut CheckCrateVisitor { - tcx, - tables: &ty::TypeckTables::empty(None), - in_fn: false, - in_static: false, - promotable: false, - mut_rvalue_borrows: NodeSet(), - param_env: ty::ParamEnv::empty(Reveal::UserFacing), - identity_substs: Substs::empty(), - }.as_deep_visitor()); - tcx.sess.abort_if_errors(); -} - impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> { fn consume(&mut self, _consume_id: ast::NodeId, diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 1bfa5943ee90a..3597a6f18287e 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -264,4 +264,5 @@ register_diagnostics! { E0226, // only a single explicit lifetime bound is permitted E0472, // asm! is unsupported on this target E0561, // patterns aren't allowed in function pointer types + E0642, // patterns aren't allowed in methods without bodies } diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index c6bc045f0de3b..6f93fa133b9e4 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -358,13 +358,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { self.record("Mac", Id::None, mac); } - fn visit_path_list_item(&mut self, - prefix: &'v ast::Path, - item: &'v ast::PathListItem) { - self.record("PathListItem", Id::None, item); - ast_visit::walk_path_list_item(self, prefix, item) - } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) { diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 28b99e1185bd2..9a150abea6691 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -33,6 +33,8 @@ extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; +use rustc::ty::maps::Providers; + mod diagnostics; pub mod ast_validation; @@ -44,3 +46,7 @@ pub mod no_asm; pub mod static_recursion; __build_diagnostic_array! { librustc_passes, DIAGNOSTICS } + +pub fn provide(providers: &mut Providers) { + consts::provide(providers); +} diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 1fa49614580a3..8a9936ecb8bb4 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -14,8 +14,8 @@ use rustc_const_math::{ConstUsize}; use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData}; -use rustc::mir::{Constant, Literal, Location, LocalDecl}; -use rustc::mir::{Lvalue, LvalueElem, LvalueProjection}; +use rustc::mir::{Constant, Literal, Location, Local, LocalDecl}; +use rustc::mir::{Place, PlaceElem, PlaceProjection}; use rustc::mir::{Mir, Operand, ProjectionElem}; use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind}; use rustc::mir::{Terminator, TerminatorKind, VisibilityScope, VisibilityScopeData}; @@ -121,6 +121,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { TerminatorKind::Assert { .. } => "TerminatorKind::Assert", TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop", TerminatorKind::Yield { .. } => "TerminatorKind::Yield", + TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges", }, kind); self.super_terminator_kind(block, kind, location); } @@ -180,47 +181,48 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { location: Location) { self.record("Operand", operand); self.record(match *operand { - Operand::Consume(..) => "Operand::Consume", + Operand::Copy(..) => "Operand::Copy", + Operand::Move(..) => "Operand::Move", Operand::Constant(..) => "Operand::Constant", }, operand); self.super_operand(operand, location); } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - context: mir_visit::LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &Place<'tcx>, + context: mir_visit::PlaceContext<'tcx>, location: Location) { - self.record("Lvalue", lvalue); - self.record(match *lvalue { - Lvalue::Local(..) => "Lvalue::Local", - Lvalue::Static(..) => "Lvalue::Static", - Lvalue::Projection(..) => "Lvalue::Projection", - }, lvalue); - self.super_lvalue(lvalue, context, location); + self.record("Place", place); + self.record(match *place { + Place::Local(..) => "Place::Local", + Place::Static(..) => "Place::Static", + Place::Projection(..) => "Place::Projection", + }, place); + self.super_place(place, context, location); } fn visit_projection(&mut self, - lvalue: &LvalueProjection<'tcx>, - context: mir_visit::LvalueContext<'tcx>, + place: &PlaceProjection<'tcx>, + context: mir_visit::PlaceContext<'tcx>, location: Location) { - self.record("LvalueProjection", lvalue); - self.super_projection(lvalue, context, location); + self.record("PlaceProjection", place); + self.super_projection(place, context, location); } fn visit_projection_elem(&mut self, - lvalue: &LvalueElem<'tcx>, - context: mir_visit::LvalueContext<'tcx>, + place: &PlaceElem<'tcx>, + context: mir_visit::PlaceContext<'tcx>, location: Location) { - self.record("LvalueElem", lvalue); - self.record(match *lvalue { - ProjectionElem::Deref => "LvalueElem::Deref", - ProjectionElem::Subslice { .. } => "LvalueElem::Subslice", - ProjectionElem::Field(..) => "LvalueElem::Field", - ProjectionElem::Index(..) => "LvalueElem::Index", - ProjectionElem::ConstantIndex { .. } => "LvalueElem::ConstantIndex", - ProjectionElem::Downcast(..) => "LvalueElem::Downcast", - }, lvalue); - self.super_projection_elem(lvalue, context, location); + self.record("PlaceElem", place); + self.record(match *place { + ProjectionElem::Deref => "PlaceElem::Deref", + ProjectionElem::Subslice { .. } => "PlaceElem::Subslice", + ProjectionElem::Field(..) => "PlaceElem::Field", + ProjectionElem::Index(..) => "PlaceElem::Index", + ProjectionElem::ConstantIndex { .. } => "PlaceElem::ConstantIndex", + ProjectionElem::Downcast(..) => "PlaceElem::Downcast", + }, place); + self.super_projection_elem(place, context, location); } fn visit_constant(&mut self, @@ -269,9 +271,10 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { } fn visit_local_decl(&mut self, + local: Local, local_decl: &LocalDecl<'tcx>) { self.record("LocalDecl", local_decl); - self.super_local_decl(local_decl); + self.super_local_decl(local, local_decl); } fn visit_visibility_scope(&mut self, diff --git a/src/librustc_platform_intrinsics/powerpc.rs b/src/librustc_platform_intrinsics/powerpc.rs index a9c56309aa8ba..93ee9fe06dadf 100644 --- a/src/librustc_platform_intrinsics/powerpc.rs +++ b/src/librustc_platform_intrinsics/powerpc.rs @@ -397,6 +397,56 @@ pub fn find(name: &str) -> Option { output: &::I32x4, definition: Named("llvm.ppc.altivec.vsumsws") }, + "_vec_madd" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 3] = [&::F32x4, &::F32x4, &::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vmaddfp") + }, + "_vec_nmsub" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 3] = [&::F32x4, &::F32x4, &::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vnmsubfp") + }, + "_vec_expte" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vexptefp") + }, + "_vec_floor" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrfim") + }, + "_vec_ceil" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrfip") + }, + "_vec_round" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrfin") + }, + "_vec_trunc" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrfiz") + }, + "_vec_loge" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vlogefp") + }, + "_vec_re" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrefp") + }, + "_vec_rsqrte" => Intrinsic { + inputs: { static INPUTS: [&'static Type; 1] = [&::F32x4]; &INPUTS }, + output: &::F32x4, + definition: Named("llvm.ppc.altivec.vrsqrtefp") + }, _ => return None, }) } diff --git a/src/librustc_plugin/Cargo.toml b/src/librustc_plugin/Cargo.toml index 7f41d0527617a..d8fa1da1ce219 100644 --- a/src/librustc_plugin/Cargo.toml +++ b/src/librustc_plugin/Cargo.toml @@ -11,7 +11,6 @@ crate-type = ["dylib"] [dependencies] rustc = { path = "../librustc" } -rustc_back = { path = "../librustc_back" } rustc_metadata = { path = "../librustc_metadata" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_plugin/lib.rs b/src/librustc_plugin/lib.rs index a2a6d183e9ccc..3df56c4e728ca 100644 --- a/src/librustc_plugin/lib.rs +++ b/src/librustc_plugin/lib.rs @@ -71,7 +71,6 @@ #[macro_use] extern crate syntax; extern crate rustc; -extern crate rustc_back; extern crate rustc_metadata; extern crate syntax_pos; extern crate rustc_errors as errors; diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs index aba56788928af..8a4ec03b20efc 100644 --- a/src/librustc_plugin/load.rs +++ b/src/librustc_plugin/load.rs @@ -115,7 +115,7 @@ impl<'a> PluginLoader<'a> { span: Span, path: PathBuf, symbol: String) -> PluginRegistrarFun { - use rustc_back::dynamic_lib::DynamicLibrary; + use rustc_metadata::dynamic_lib::DynamicLibrary; // Make sure the path contains a / or the linker will search for it. let path = env::current_dir().unwrap().join(&path); diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index e7a1dd6b043b1..74d92ce1c3e62 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -85,6 +85,7 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { fn item_ty_level(&self, item_def_id: DefId) -> Option { let ty_def_id = match self.tcx.type_of(item_def_id).sty { ty::TyAdt(adt, _) => adt.did, + ty::TyForeign(did) => did, ty::TyDynamic(ref obj, ..) if obj.principal().is_some() => obj.principal().unwrap().def_id(), ty::TyProjection(ref proj) => proj.trait_ref(self.tcx).def_id, @@ -146,7 +147,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { let def_id = self.tcx.hir.local_def_id(item.id); cmp::min(self.item_ty_level(def_id), self.impl_trait_level(def_id)) } - hir::ItemDefaultImpl(..) => { + hir::ItemAutoImpl(..) => { let def_id = self.tcx.hir.local_def_id(item.id); self.impl_trait_level(def_id) } @@ -212,7 +213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { } hir::ItemUse(..) | hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemGlobalAsm(..) | hir::ItemTy(..) | hir::ItemMod(..) | - hir::ItemFn(..) | hir::ItemExternCrate(..) | hir::ItemDefaultImpl(..) => {} + hir::ItemFn(..) | hir::ItemExternCrate(..) | hir::ItemAutoImpl(..) => {} } // Mark all items in interfaces of reachable items as reachable @@ -224,7 +225,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { // Reexports are handled in visit_mod hir::ItemUse(..) => {} // The interface is empty - hir::ItemDefaultImpl(..) => {} + hir::ItemAutoImpl(..) => {} // The interface is empty hir::ItemGlobalAsm(..) => {} // Visit everything @@ -372,7 +373,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - if let hir::TyImplTrait(..) = ty.node { + if let hir::TyImplTraitExistential(..) = ty.node { if self.get(ty.id).is_some() { // Reach the (potentially private) type and the API being exposed. self.reach(ty.id).ty().predicates(); @@ -444,6 +445,7 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { let ty_def_id = match ty.sty { ty::TyAdt(adt, _) => Some(adt.did), + ty::TyForeign(did) => Some(did), ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), ty::TyProjection(ref proj) => Some(proj.item_def_id), ty::TyFnDef(def_id, ..) | @@ -625,6 +627,16 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { ctor_vis = field_vis; } } + + // If the structure is marked as non_exhaustive then lower the + // visibility to within the crate. + let struct_def_id = self.tcx.hir.get_parent_did(node_id); + let adt_def = self.tcx.adt_def(struct_def_id); + if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public { + ctor_vis = ty::Visibility::Restricted( + DefId::local(CRATE_DEF_INDEX)); + } + return ctor_vis; } node => bug!("unexpected node kind: {:?}", node) @@ -800,7 +812,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => { + ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | + ty::TyFnDef(def_id, ..) | + ty::TyForeign(def_id) => { if !self.item_is_accessible(def_id) { let msg = format!("type `{}` is private", ty); self.tcx.sess.span_err(self.span, &msg); @@ -1329,6 +1343,7 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<' fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { let ty_def_id = match ty.sty { ty::TyAdt(adt, _) => Some(adt.did), + ty::TyForeign(did) => Some(did), ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), ty::TyProjection(ref proj) => { if self.required_visibility == ty::Visibility::Invisible { @@ -1349,8 +1364,13 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<' if let Some(def_id) = ty_def_id { // Non-local means public (private items can't leave their crate, modulo bugs) if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { - let item = self.tcx.hir.expect_item(node_id); - let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); + let vis = match self.tcx.hir.find(node_id) { + Some(hir::map::NodeItem(item)) => &item.vis, + Some(hir::map::NodeForeignItem(item)) => &item.vis, + _ => bug!("expected item of foreign item"), + }; + + let vis = ty::Visibility::from_hir(vis, node_id, self.tcx); if !vis.is_at_least(self.min_visibility, self.tcx) { self.min_visibility = vis; @@ -1494,7 +1514,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> } } // The interface is empty - hir::ItemDefaultImpl(..) => {} + hir::ItemAutoImpl(..) => {} // An inherent impl is public when its type is public // Subitems of inherent impls have their own publicity hir::ItemImpl(.., None, _, ref impl_item_refs) => { @@ -1537,7 +1557,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - if let hir::TyImplTrait(..) = ty.node { + if let hir::TyImplTraitExistential(..) = ty.node { // Check the traits being exposed, as they're separate, // e.g. `impl Iterator` has two predicates, // `X: Iterator` and `::Item == T`, @@ -1563,9 +1583,7 @@ pub fn provide(providers: &mut Providers) { } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Rc { - tcx.dep_graph.with_ignore(|| { // FIXME - tcx.privacy_access_levels(LOCAL_CRATE) - }) + tcx.privacy_access_levels(LOCAL_CRATE) } fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_resolve/Cargo.toml b/src/librustc_resolve/Cargo.toml index 0968ea31b754f..ab2d152b724a7 100644 --- a/src/librustc_resolve/Cargo.toml +++ b/src/librustc_resolve/Cargo.toml @@ -16,3 +16,4 @@ rustc = { path = "../librustc" } arena = { path = "../libarena" } rustc_errors = { path = "../librustc_errors" } syntax_pos = { path = "../libsyntax_pos" } +rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index a4d1ae1621571..afca6ea2c0751 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -32,15 +32,14 @@ use std::rc::Rc; use syntax::ast::{Name, Ident}; use syntax::attr; -use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind}; -use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind}; -use syntax::ast::{Variant, ViewPathGlob, ViewPathList, ViewPathSimple}; +use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; +use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind, Variant}; use syntax::codemap::respan; use syntax::ext::base::SyntaxExtension; use syntax::ext::base::Determinacy::Undetermined; use syntax::ext::hygiene::Mark; use syntax::ext::tt::macro_rules; -use syntax::parse::token; +use syntax::parse::token::{self, Token}; use syntax::symbol::keywords; use syntax::symbol::Symbol; use syntax::visit::{self, Visitor}; @@ -102,155 +101,160 @@ impl<'a> Resolver<'a> { } } - /// Constructs the reduced graph for one item. - fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { - let parent = self.current_module; - let ident = item.ident; - let sp = item.span; - let vis = self.resolve_visibility(&item.vis); + fn build_reduced_graph_for_use_tree(&mut self, + use_tree: &ast::UseTree, + id: NodeId, + vis: ty::Visibility, + prefix: &ast::Path, + nested: bool, + item: &Item, + expansion: Mark) { + let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); + let path = &use_tree.prefix; + + let mut module_path: Vec<_> = prefix.segments.iter() + .chain(path.segments.iter()) + .map(|seg| respan(seg.span, seg.identifier)) + .collect(); + + match use_tree.kind { + ast::UseTreeKind::Simple(mut ident) => { + let mut source = module_path.pop().unwrap().node; + let mut type_ns_only = false; + + if nested { + // Correctly handle `self` + if source.name == keywords::SelfValue.name() { + type_ns_only = true; + + let last_segment = *module_path.last().unwrap(); + if last_segment.node.name == keywords::CrateRoot.name() { + resolve_error( + self, + use_tree.span, + ResolutionError:: + SelfImportOnlyInImportListWithNonEmptyPrefix + ); + return; + } - match item.node { - ItemKind::Use(ref view_path) => { - // Extract and intern the module part of the path. For - // globs and lists, the path is found directly in the AST; - // for simple paths we have to munge the path a little. - let mut module_path: Vec<_> = match view_path.node { - ViewPathSimple(_, ref full_path) => { - full_path.segments - .split_last() - .unwrap() - .1 - .iter() - .map(|seg| respan(seg.span, seg.identifier)) - .collect() + // Replace `use foo::self;` with `use foo;` + let _ = module_path.pop(); + source = last_segment.node; + if ident.name == keywords::SelfValue.name() { + ident = last_segment.node; + } } - - ViewPathGlob(ref module_ident_path) | - ViewPathList(ref module_ident_path, _) => { - module_ident_path.segments - .iter() - .map(|seg| respan(seg.span, seg.identifier)) - .collect() + } else { + // Disallow `self` + if source.name == keywords::SelfValue.name() { + resolve_error(self, + use_tree.span, + ResolutionError::SelfImportsOnlyAllowedWithin); } - }; - - // This can be removed once warning cycle #36888 is complete. - if module_path.len() >= 2 && - module_path[0].node.name == keywords::CrateRoot.name() && - token::Ident(module_path[1].node).is_path_segment_keyword() - { - module_path.remove(0); - } - // Build up the import directives. - let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); - - match view_path.node { - ViewPathSimple(mut binding, ref full_path) => { - let mut source = full_path.segments.last().unwrap().identifier; - let source_name = source.name; - if source_name == "mod" || source_name == "self" { - resolve_error(self, - view_path.span, - ResolutionError::SelfImportsOnlyAllowedWithin); - } else if source_name == keywords::DollarCrate.name() && - full_path.segments.len() == 1 { - let crate_root = self.resolve_crate_root(source.ctxt); - let crate_name = match crate_root.kind { - ModuleKind::Def(_, name) => name, - ModuleKind::Block(..) => unreachable!(), - }; - source.name = crate_name; - if binding.name == keywords::DollarCrate.name() { - binding.name = crate_name; - } - - self.session.struct_span_warn(item.span, "`$crate` may not be imported") - .note("`use $crate;` was erroneously allowed and \ - will become a hard error in a future release") - .emit(); + // Disallow `use $crate;` + if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 { + let crate_root = self.resolve_crate_root(source.ctxt); + let crate_name = match crate_root.kind { + ModuleKind::Def(_, name) => name, + ModuleKind::Block(..) => unreachable!(), + }; + source.name = crate_name; + if ident.name == keywords::DollarCrate.name() { + ident.name = crate_name; } - let subclass = SingleImport { - target: binding, - 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, - ); + self.session.struct_span_warn(item.span, "`$crate` may not be imported") + .note("`use $crate;` was erroneously allowed and \ + will become a hard error in a future release") + .emit(); } - ViewPathList(_, ref source_items) => { - // Make sure there's at most one `mod` import in the list. - let mod_spans = source_items.iter().filter_map(|item| { - if item.node.name.name == keywords::SelfValue.name() { - Some(item.span) - } else { - None - } - }).collect::>(); - - if mod_spans.len() > 1 { - let mut e = resolve_struct_error(self, - mod_spans[0], - ResolutionError::SelfImportCanOnlyAppearOnceInTheList); - for other_span in mod_spans.iter().skip(1) { - e.span_note(*other_span, "another `self` import appears here"); - } - e.emit(); - } + } - for source_item in source_items { - let node = source_item.node; - 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(), - respan(source_item.span, node.name), - rename, - false) - } else { - let ident = *module_path.last().unwrap(); - if ident.node.name == keywords::CrateRoot.name() { - resolve_error( - self, - source_item.span, - ResolutionError:: - SelfImportOnlyInImportListWithNonEmptyPrefix - ); - continue; - } - let module_path = module_path.split_last().unwrap().1; - let rename = node.rename.unwrap_or(ident.node); - (module_path.to_vec(), ident, rename, true) - } - }; - let subclass = SingleImport { - target: rename, - source: ident.node, - result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), - type_ns_only, - }; - let id = source_item.node.id; - self.add_import_directive( - module_path, subclass, source_item.span, id, vis, expansion, - ); + let subclass = SingleImport { + target: ident, + source, + result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), + type_ns_only, + }; + self.add_import_directive( + module_path, subclass, use_tree.span, id, vis, expansion, + ); + } + ast::UseTreeKind::Glob => { + let subclass = GlobImport { + is_prelude, + max_vis: Cell::new(ty::Visibility::Invisible), + }; + self.add_import_directive( + module_path, subclass, use_tree.span, id, vis, expansion, + ); + } + ast::UseTreeKind::Nested(ref items) => { + let prefix = ast::Path { + segments: module_path.iter() + .map(|s| ast::PathSegment { + identifier: s.node, + span: s.span, + parameters: None, + }) + .collect(), + span: path.span, + }; + + // Ensure there is at most one `self` in the list + let self_spans = items.iter().filter_map(|&(ref use_tree, _)| { + if let ast::UseTreeKind::Simple(ident) = use_tree.kind { + if ident.name == keywords::SelfValue.name() { + return Some(use_tree.span); } } - ViewPathGlob(_) => { - let subclass = GlobImport { - is_prelude, - max_vis: Cell::new(ty::Visibility::Invisible), - }; - self.add_import_directive( - module_path, subclass, view_path.span, item.id, vis, expansion, - ); + + None + }).collect::>(); + if self_spans.len() > 1 { + let mut e = resolve_struct_error(self, + self_spans[0], + ResolutionError::SelfImportCanOnlyAppearOnceInTheList); + + for other_span in self_spans.iter().skip(1) { + e.span_note(*other_span, "another `self` import appears here"); } + + e.emit(); + } + + for &(ref tree, id) in items { + self.build_reduced_graph_for_use_tree( + tree, id, vis, &prefix, true, item, expansion + ); } } + } + } + + /// Constructs the reduced graph for one item. + fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { + let parent = self.current_module; + let ident = item.ident; + let sp = item.span; + let vis = self.resolve_visibility(&item.vis); + + match item.node { + ItemKind::Use(ref use_tree) => { + // Just an empty prefix to start out + let prefix = ast::Path { + segments: vec![], + span: use_tree.span, + }; + + self.build_reduced_graph_for_use_tree( + use_tree, item.id, vis, &prefix, false, item, expansion, + ); + } - ItemKind::ExternCrate(_) => { + ItemKind::ExternCrate(as_name) => { self.crate_loader.process_item(item, &self.definitions); // n.b. we don't need to look at the path option here, because cstore already did @@ -265,7 +269,7 @@ impl<'a> Resolver<'a> { id: item.id, parent, imported_module: Cell::new(Some(module)), - subclass: ImportDirectiveSubclass::ExternCrate, + subclass: ImportDirectiveSubclass::ExternCrate(as_name), span: item.span, module_path: Vec::new(), vis: Cell::new(vis), @@ -338,11 +342,22 @@ impl<'a> Resolver<'a> { // These items live in both the type and value namespaces. ItemKind::Struct(ref struct_def, _) => { // Define a name in the type namespace. - let def = Def::Struct(self.definitions.local_def_id(item.id)); + let def_id = self.definitions.local_def_id(item.id); + let def = Def::Struct(def_id); self.define(parent, ident, TypeNS, (def, vis, sp, expansion)); - // Record field names for error reporting. let mut ctor_vis = vis; + + let has_non_exhaustive = item.attrs.iter() + .any(|item| item.check_name("non_exhaustive")); + + // If the structure is marked as non_exhaustive then lower the visibility + // to within the crate. + if has_non_exhaustive && vis == ty::Visibility::Public { + ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); + } + + // Record field names for error reporting. let field_names = struct_def.fields().iter().filter_map(|field| { let field_vis = self.resolve_visibility(&field.vis); if ctor_vis.is_at_least(field_vis, &*self) { @@ -376,7 +391,7 @@ impl<'a> Resolver<'a> { self.insert_field_names(item_def_id, field_names); } - ItemKind::DefaultImpl(..) | ItemKind::Impl(..) => {} + ItemKind::AutoImpl(..) | ItemKind::Impl(..) => {} ItemKind::Trait(..) => { let def_id = self.definitions.local_def_id(item.id); @@ -414,22 +429,26 @@ impl<'a> Resolver<'a> { // value namespace, they are reserved for possible future use. let ctor_kind = CtorKind::from_ast(&variant.node.data); let ctor_def = Def::VariantCtor(def_id, ctor_kind); + self.define(parent, ident, ValueNS, (ctor_def, vis, variant.span, expansion)); } /// Constructs the reduced graph for one foreign item. fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem, expansion: Mark) { - let def = match item.node { + let (def, ns) = match item.node { ForeignItemKind::Fn(..) => { - Def::Fn(self.definitions.local_def_id(item.id)) + (Def::Fn(self.definitions.local_def_id(item.id)), ValueNS) } ForeignItemKind::Static(_, m) => { - Def::Static(self.definitions.local_def_id(item.id), m) + (Def::Static(self.definitions.local_def_id(item.id), m), ValueNS) + } + ForeignItemKind::Ty => { + (Def::TyForeign(self.definitions.local_def_id(item.id)), TypeNS) } }; let parent = self.current_module; let vis = self.resolve_visibility(&item.vis); - self.define(parent, item.ident, ValueNS, (def, vis, item.span, expansion)); + self.define(parent, item.ident, ns, (def, vis, item.span, expansion)); } fn build_reduced_graph_for_block(&mut self, block: &Block, expansion: Mark) { @@ -462,7 +481,7 @@ impl<'a> Resolver<'a> { span); self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, expansion)); } - Def::Variant(..) | Def::TyAlias(..) => { + Def::Variant(..) | Def::TyAlias(..) | Def::TyForeign(..) => { self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); } Def::Fn(..) | Def::Static(..) | Def::Const(..) | Def::VariantCtor(..) => { @@ -830,4 +849,17 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { visit::walk_trait_item(self, item); self.resolver.current_module = parent; } + + fn visit_token(&mut self, t: Token) { + if let Token::Interpolated(nt) = t { + match nt.0 { + token::NtExpr(ref expr) => { + if let ast::ExprKind::Mac(..) = expr.node { + self.visit_invoc(expr.id); + } + } + _ => {} + } + } + } } diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index a66d1ce0859b7..0fb3d96cd50d4 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -26,7 +26,7 @@ use resolve_imports::ImportDirectiveSubclass; use rustc::{lint, ty}; use rustc::util::nodemap::NodeMap; -use syntax::ast::{self, ViewPathGlob, ViewPathList, ViewPathSimple}; +use syntax::ast; use syntax::visit::{self, Visitor}; use syntax_pos::{Span, MultiSpan, DUMMY_SP}; @@ -35,6 +35,8 @@ struct UnusedImportCheckVisitor<'a, 'b: 'a> { resolver: &'a mut Resolver<'b>, /// All the (so far) unused imports, grouped path list unused_imports: NodeMap>, + base_id: ast::NodeId, + item_span: Span, } // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver. @@ -77,40 +79,41 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> { fn visit_item(&mut self, item: &'a ast::Item) { - visit::walk_item(self, item); + self.item_span = item.span; + // Ignore is_public import statements because there's no way to be sure // whether they're used or not. Also ignore imports with a dummy span // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. - if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) { - return; + if let ast::ItemKind::Use(..) = item.node { + if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) { + return; + } } - match item.node { - ast::ItemKind::Use(ref p) => { - match p.node { - ViewPathSimple(..) => { - self.check_import(item.id, item.id, p.span) - } - - ViewPathList(_, ref list) => { - if list.len() == 0 { - self.unused_imports - .entry(item.id) - .or_insert_with(NodeMap) - .insert(item.id, item.span); - } - for i in list { - self.check_import(item.id, i.node.id, i.span); - } - } - ViewPathGlob(_) => { - self.check_import(item.id, item.id, p.span); - } - } + visit::walk_item(self, item); + } + + fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) { + // Use the base UseTree's NodeId as the item id + // This allows the grouping of all the lints in the same item + if !nested { + self.base_id = id; + } + + if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { + if items.len() == 0 { + self.unused_imports + .entry(self.base_id) + .or_insert_with(NodeMap) + .insert(id, self.item_span); } - _ => {} + } else { + let base_id = self.base_id; + self.check_import(base_id, id, use_tree.span); } + + visit::walk_use_tree(self, use_tree, id); } } @@ -120,7 +123,7 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { _ if directive.used.get() || directive.vis.get() == ty::Visibility::Public || directive.span.source_equal(&DUMMY_SP) => {} - ImportDirectiveSubclass::ExternCrate => { + ImportDirectiveSubclass::ExternCrate(_) => { resolver.maybe_unused_extern_crates.push((directive.id, directive.span)); } ImportDirectiveSubclass::MacroUse => { @@ -135,6 +138,8 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { let mut visitor = UnusedImportCheckVisitor { resolver, unused_imports: NodeMap(), + base_id: ast::DUMMY_NODE_ID, + item_span: DUMMY_SP, }; visit::walk_crate(&mut visitor, krate); diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 9193ac0fcd66e..564626ac39885 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -43,7 +43,7 @@ parameter if so. "##, E0154: r##" -## Note: this error code is no longer emitted by the compiler. +#### Note: this error code is no longer emitted by the compiler. Imports (`use` statements) are not allowed after non-item statements, such as variable declarations and expression statements. @@ -79,7 +79,7 @@ https://doc.rust-lang.org/reference.html#statements "##, E0251: r##" -## Note: this error code is no longer emitted by the compiler. +#### Note: this error code is no longer emitted by the compiler. Two items of the same name cannot be imported without rebinding one of the items under a new local name. @@ -268,7 +268,7 @@ fn main() { "##, E0256: r##" -## Note: this error code is no longer emitted by the compiler. +#### Note: this error code is no longer emitted by the compiler. You can't import a type or module when the name of the item being imported is the same as another type or submodule defined in the module. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 36cd69f91b9c3..49c452cddb2cd 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -24,6 +24,7 @@ extern crate rustc_errors as errors; extern crate arena; #[macro_use] extern crate rustc; +extern crate rustc_data_structures; use self::Namespace::*; use self::TypeParameters::*; @@ -59,7 +60,7 @@ use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue}; use syntax_pos::{Span, DUMMY_SP, MultiSpan}; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, DiagnosticId}; use std::cell::{Cell, RefCell}; use std::cmp; @@ -137,7 +138,7 @@ enum ResolutionError<'a> { /// error E0416: identifier is bound more than once in the same pattern IdentifierBoundMoreThanOnceInSamePattern(&'a str), /// error E0426: use of undeclared label - UndeclaredLabel(&'a str), + UndeclaredLabel(&'a str, Option), /// error E0429: `self` imports are only allowed within a { } list SelfImportsOnlyAllowedWithin, /// error E0430: `self` import can only appear once in the list @@ -223,7 +224,11 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver, let target_sp = binding_error.target.iter().map(|x| *x).collect::>(); let msp = MultiSpan::from_spans(target_sp.clone()); let msg = format!("variable `{}` is not bound in all patterns", binding_error.name); - let mut err = resolver.session.struct_span_err_with_code(msp, &msg, "E0408"); + let mut err = resolver.session.struct_span_err_with_code( + msp, + &msg, + DiagnosticId::Error("E0408".into()), + ); for sp in target_sp { err.span_label(sp, format!("pattern doesn't bind `{}`", binding_error.name)); } @@ -263,13 +268,17 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver, err.span_label(span, "used in a pattern more than once"); err } - ResolutionError::UndeclaredLabel(name) => { + ResolutionError::UndeclaredLabel(name, lev_candidate) => { let mut err = struct_span_err!(resolver.session, span, E0426, "use of undeclared label `{}`", name); - err.span_label(span, format!("undeclared label `{}`", name)); + if let Some(lev_candidate) = lev_candidate { + err.span_label(span, format!("did you mean `{}`?", lev_candidate)); + } else { + err.span_label(span, format!("undeclared label `{}`", name)); + } err } ResolutionError::SelfImportsOnlyAllowedWithin => { @@ -368,12 +377,6 @@ enum PatternSource { } impl PatternSource { - fn is_refutable(self) -> bool { - match self { - PatternSource::Match | PatternSource::IfLet | PatternSource::WhileLet => true, - PatternSource::Let | PatternSource::For | PatternSource::FnParam => false, - } - } fn descr(self) -> &'static str { match self { PatternSource::Match => "match binding", @@ -464,7 +467,8 @@ impl<'a> PathSource<'a> { PathSource::Type => match def { Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Trait(..) | Def::TyAlias(..) | Def::AssociatedTy(..) | - Def::PrimTy(..) | Def::TyParam(..) | Def::SelfTy(..) => true, + Def::PrimTy(..) | Def::TyParam(..) | Def::SelfTy(..) | + Def::TyForeign(..) => true, _ => false, }, PathSource::Trait => match def { @@ -584,6 +588,18 @@ struct UsePlacementFinder { found_use: bool, } +impl UsePlacementFinder { + fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { + let mut finder = UsePlacementFinder { + target_module, + span: None, + found_use: false, + }; + visit::walk_crate(&mut finder, krate); + (finder.span, finder.found_use) + } +} + impl<'tcx> Visitor<'tcx> for UsePlacementFinder { fn visit_mod( &mut self, @@ -703,6 +719,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { HasTypeParameters(generics, ItemRibKind) } ForeignItemKind::Static(..) => NoTypeParameters, + ForeignItemKind::Ty => NoTypeParameters, }; self.with_type_parameter_rib(type_parameters, |this| { visit::walk_foreign_item(this, foreign_item); @@ -714,13 +731,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { _: Span, node_id: NodeId) { let rib_kind = match function_kind { - FnKind::ItemFn(_, generics, ..) => { - self.visit_generics(generics); + FnKind::ItemFn(..) => { ItemRibKind } - FnKind::Method(_, sig, _, _) => { - self.visit_generics(&sig.generics); - MethodRibKind(!sig.decl.has_self()) + FnKind::Method(_, _, _, _) => { + TraitOrImplItemRibKind } FnKind::Closure(_) => ClosureRibKind(node_id), }; @@ -808,12 +823,10 @@ enum RibKind<'a> { ClosureRibKind(NodeId /* func id */), // We passed through an impl or trait and are now in one of its - // methods. Allow references to ty params that impl or trait + // methods or associated types. Allow references to ty params that impl or trait // binds. Disallow any other upvars (including other ty params that are // upvars). - // - // The boolean value represents the fact that this method is static or not. - MethodRibKind(bool), + TraitOrImplItemRibKind, // We passed through an item scope. Disallow upvars. ItemRibKind, @@ -1109,7 +1122,7 @@ impl<'a> NameBinding<'a> { match self.kind { NameBindingKind::Import { directive: &ImportDirective { - subclass: ImportDirectiveSubclass::ExternCrate, .. + subclass: ImportDirectiveSubclass::ExternCrate(_), .. }, .. } => true, _ => false, @@ -1123,6 +1136,15 @@ impl<'a> NameBinding<'a> { } } + fn is_renamed_extern_crate(&self) -> bool { + if let NameBindingKind::Import { directive, ..} = self.kind { + if let ImportDirectiveSubclass::ExternCrate(Some(_)) = directive.subclass { + return true; + } + } + false + } + fn is_glob_import(&self) -> bool { match self.kind { NameBindingKind::Import { directive, .. } => directive.is_glob(), @@ -1266,6 +1288,8 @@ pub struct Resolver<'a> { ambiguity_errors: Vec>, /// `use` injections are delayed for better placement and deduplication use_injections: Vec>, + /// `use` injections for proc macros wrongly imported with #[macro_use] + proc_mac_errors: Vec, gated_errors: FxHashSet, disallowed_shadowing: Vec<&'a LegacyBinding<'a>>, @@ -1413,7 +1437,7 @@ impl<'a> Resolver<'a> { let mut definitions = Definitions::new(); DefCollector::new(&mut definitions, Mark::root()) - .collect_root(crate_name, &session.local_crate_disambiguator().as_str()); + .collect_root(crate_name, session.local_crate_disambiguator()); let mut invocations = FxHashMap(); invocations.insert(Mark::root(), @@ -1474,6 +1498,7 @@ impl<'a> Resolver<'a> { privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), use_injections: Vec::new(), + proc_mac_errors: Vec::new(), gated_errors: FxHashSet(), disallowed_shadowing: Vec::new(), @@ -1533,6 +1558,15 @@ impl<'a> Resolver<'a> { } } + fn macro_def(&self, mut ctxt: SyntaxContext) -> DefId { + loop { + match self.macro_defs.get(&ctxt.outer()) { + Some(&def_id) => return def_id, + None => ctxt.remove_mark(), + }; + } + } + /// Entry point to crate resolution. pub fn resolve_crate(&mut self, krate: &Crate) { ImportResolver { resolver: self }.finalize_imports(); @@ -1636,7 +1670,7 @@ impl<'a> Resolver<'a> { module = match self.ribs[ns][i].kind { ModuleRibKind(module) => module, - MacroDefinition(def) if def == self.macro_defs[&ident.ctxt.outer()] => { + MacroDefinition(def) if def == self.macro_def(ident.ctxt) => { // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. ident.ctxt.remove_mark(); @@ -1790,16 +1824,20 @@ impl<'a> Resolver<'a> { } } - /// Searches the current set of local scopes for labels. + /// Searches the current set of local scopes for labels. Returns the first non-None label that + /// is returned by the given predicate function + /// /// Stops after meeting a closure. - fn search_label(&self, mut ident: Ident) -> Option { + fn search_label(&self, mut ident: Ident, pred: P) -> Option + where P: Fn(&Rib, Ident) -> Option + { for rib in self.label_ribs.iter().rev() { match rib.kind { NormalRibKind => {} // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. MacroDefinition(def) => { - if def == self.macro_defs[&ident.ctxt.outer()] { + if def == self.macro_def(ident.ctxt) { ident.ctxt.remove_mark(); } } @@ -1808,9 +1846,9 @@ impl<'a> Resolver<'a> { return None; } } - let result = rib.bindings.get(&ident).cloned(); - if result.is_some() { - return result; + let r = pred(rib, ident); + if r.is_some() { + return r; } } None @@ -1833,7 +1871,7 @@ impl<'a> Resolver<'a> { |this| visit::walk_item(this, item)); } - ItemKind::DefaultImpl(_, ref trait_ref) => { + ItemKind::AutoImpl(_, ref trait_ref) => { self.with_optional_trait_ref(Some(trait_ref), |this, _| { // Resolve type arguments in trait path visit::walk_trait_ref(this, trait_ref); @@ -1846,7 +1884,7 @@ impl<'a> Resolver<'a> { item.id, impl_items), - ItemKind::Trait(_, ref generics, ref bounds, ref trait_items) => { + ItemKind::Trait(.., ref generics, ref bounds, ref trait_items) => { // Create a new rib for the trait-wide type parameters. self.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| { let local_def_id = this.definitions.local_def_id(item.id); @@ -1857,34 +1895,33 @@ impl<'a> Resolver<'a> { for trait_item in trait_items { this.check_proc_macro_attrs(&trait_item.attrs); - match trait_item.node { - TraitItemKind::Const(ref ty, ref default) => { - this.visit_ty(ty); - - // Only impose the restrictions of - // ConstRibKind for an actual constant - // expression in a provided default. - if let Some(ref expr) = *default{ - this.with_constant_rib(|this| { - this.visit_expr(expr); - }); + let type_parameters = HasTypeParameters(&trait_item.generics, + TraitOrImplItemRibKind); + this.with_type_parameter_rib(type_parameters, |this| { + match trait_item.node { + TraitItemKind::Const(ref ty, ref default) => { + this.visit_ty(ty); + + // Only impose the restrictions of + // ConstRibKind for an actual constant + // expression in a provided default. + if let Some(ref expr) = *default{ + this.with_constant_rib(|this| { + this.visit_expr(expr); + }); + } } - } - TraitItemKind::Method(ref sig, _) => { - let type_parameters = - HasTypeParameters(&sig.generics, - MethodRibKind(!sig.decl.has_self())); - this.with_type_parameter_rib(type_parameters, |this| { + TraitItemKind::Method(_, _) => { visit::walk_trait_item(this, trait_item) - }); - } - TraitItemKind::Type(..) => { - this.with_type_parameter_rib(NoTypeParameters, |this| { + } + TraitItemKind::Type(..) => { visit::walk_trait_item(this, trait_item) - }); - } - TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"), - }; + } + TraitItemKind::Macro(_) => { + panic!("unexpanded macro in resolve!") + } + }; + }); } }); }); @@ -1906,17 +1943,15 @@ impl<'a> Resolver<'a> { }); } - ItemKind::Use(ref view_path) => { - match view_path.node { - ast::ViewPathList(ref prefix, ref items) if items.is_empty() => { - // Resolve prefix of an import with empty braces (issue #28388). - self.smart_resolve_path(item.id, None, prefix, PathSource::ImportPrefix); - } - _ => {} - } + ItemKind::Use(ref use_tree) => { + let path = Path { + segments: vec![], + span: use_tree.span, + }; + self.resolve_use_tree(item, use_tree, &path); } - ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(_)=> { + ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(_) => { // do nothing, these are just around to be encoded } @@ -1924,6 +1959,32 @@ impl<'a> Resolver<'a> { } } + fn resolve_use_tree(&mut self, item: &Item, use_tree: &ast::UseTree, prefix: &Path) { + match use_tree.kind { + ast::UseTreeKind::Nested(ref items) => { + let path = Path { + segments: prefix.segments + .iter() + .chain(use_tree.prefix.segments.iter()) + .cloned() + .collect(), + span: prefix.span.to(use_tree.prefix.span), + }; + + if items.len() == 0 { + // Resolve prefix of an import with empty braces (issue #28388). + self.smart_resolve_path(item.id, None, &path, PathSource::ImportPrefix); + } else { + for &(ref tree, _) in items { + self.resolve_use_tree(item, tree, &path); + } + } + } + ast::UseTreeKind::Simple(_) => {}, + ast::UseTreeKind::Glob => {}, + } + } + fn with_type_parameter_rib<'b, F>(&'b mut self, type_parameters: TypeParameters<'a, 'b>, f: F) where F: FnOnce(&mut Resolver) { @@ -2068,46 +2129,48 @@ impl<'a> Resolver<'a> { for impl_item in impl_items { this.check_proc_macro_attrs(&impl_item.attrs); this.resolve_visibility(&impl_item.vis); - match impl_item.node { - ImplItemKind::Const(..) => { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item(impl_item.ident, - ValueNS, - impl_item.span, - |n, s| ResolutionError::ConstNotMemberOfTrait(n, s)); - visit::walk_impl_item(this, impl_item); - } - ImplItemKind::Method(ref sig, _) => { - // If this is a trait impl, ensure the method - // exists in trait - this.check_trait_item(impl_item.ident, - ValueNS, - impl_item.span, - |n, s| ResolutionError::MethodNotMemberOfTrait(n, s)); - - // We also need a new scope for the method- - // specific type parameters. - let type_parameters = - HasTypeParameters(&sig.generics, - MethodRibKind(!sig.decl.has_self())); - this.with_type_parameter_rib(type_parameters, |this| { - visit::walk_impl_item(this, impl_item); - }); - } - ImplItemKind::Type(ref ty) => { - // If this is a trait impl, ensure the type - // exists in trait - this.check_trait_item(impl_item.ident, - TypeNS, - impl_item.span, - |n, s| ResolutionError::TypeNotMemberOfTrait(n, s)); - this.visit_ty(ty); + // We also need a new scope for the impl item type parameters. + let type_parameters = HasTypeParameters(&impl_item.generics, + TraitOrImplItemRibKind); + this.with_type_parameter_rib(type_parameters, |this| { + use self::ResolutionError::*; + match impl_item.node { + ImplItemKind::Const(..) => { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item(impl_item.ident, + ValueNS, + impl_item.span, + |n, s| ConstNotMemberOfTrait(n, s)); + this.with_constant_rib(|this| + visit::walk_impl_item(this, impl_item) + ); + } + ImplItemKind::Method(_, _) => { + // If this is a trait impl, ensure the method + // exists in trait + this.check_trait_item(impl_item.ident, + ValueNS, + impl_item.span, + |n, s| MethodNotMemberOfTrait(n, s)); + + visit::walk_impl_item(this, impl_item); + } + ImplItemKind::Type(ref ty) => { + // If this is a trait impl, ensure the type + // exists in trait + this.check_trait_item(impl_item.ident, + TypeNS, + impl_item.span, + |n, s| TypeNotMemberOfTrait(n, s)); + + this.visit_ty(ty); + } + ImplItemKind::Macro(_) => + panic!("unexpanded macro in resolve!"), } - ImplItemKind::Macro(_) => - panic!("unexpanded macro in resolve!"), - } + }); } }); }); @@ -2364,20 +2427,24 @@ impl<'a> Resolver<'a> { false, pat.span) .and_then(LexicalScopeBinding::item); let resolution = binding.map(NameBinding::def).and_then(|def| { - let ivmode = BindingMode::ByValue(Mutability::Immutable); - let always_binding = !pat_src.is_refutable() || opt_pat.is_some() || - bmode != ivmode; + let is_syntactic_ambiguity = opt_pat.is_none() && + bmode == BindingMode::ByValue(Mutability::Immutable); match def { Def::StructCtor(_, CtorKind::Const) | Def::VariantCtor(_, CtorKind::Const) | - Def::Const(..) if !always_binding => { - // A unit struct/variant or constant pattern. + Def::Const(..) if is_syntactic_ambiguity => { + // Disambiguate in favor of a unit struct/variant + // or constant pattern. self.record_use(ident.node, ValueNS, binding.unwrap(), ident.span); Some(PathResolution::new(def)) } Def::StructCtor(..) | Def::VariantCtor(..) | Def::Const(..) | Def::Static(..) => { - // A fresh binding that shadows something unacceptable. + // This is unambiguously a fresh binding, either syntactically + // (e.g. `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves + // to something unusable as a pattern (e.g. constructor function), + // but we still conservatively report an error, see + // issues/33118#issuecomment-233962221 for one reason why. resolve_error( self, ident.span, @@ -2386,7 +2453,7 @@ impl<'a> Resolver<'a> { ); None } - Def::Local(..) | Def::Upvar(..) | Def::Fn(..) | Def::Err => { + Def::Fn(..) | Def::Err => { // These entities are explicitly allowed // to be shadowed by fresh bindings. None @@ -2480,18 +2547,19 @@ impl<'a> Resolver<'a> { (format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), format!("not found in {}", mod_str), item_span) }; + let code = DiagnosticId::Error(code.into()); let mut err = this.session.struct_span_err_with_code(base_span, &base_msg, code); // Emit special messages for unresolved `Self` and `self`. if is_self_type(path, ns) { __diagnostic_used!(E0411); - err.code("E0411".into()); + err.code(DiagnosticId::Error("E0411".into())); err.span_label(span, "`Self` is only available in traits and impls"); return (err, Vec::new()); } if is_self_value(path, ns) { __diagnostic_used!(E0424); - err.code("E0424".into()); + err.code(DiagnosticId::Error("E0424".into())); err.span_label(span, format!("`self` value is only available in \ methods with `self` parameter")); return (err, Vec::new()); @@ -2576,6 +2644,22 @@ impl<'a> Resolver<'a> { } _ => {} }, + (Def::Enum(..), PathSource::TupleStruct) + | (Def::Enum(..), PathSource::Expr(..)) => { + if let Some(variants) = this.collect_enum_variants(def) { + err.note(&format!("did you mean to use one \ + of the following variants?\n{}", + variants.iter() + .map(|suggestion| path_names_to_string(suggestion)) + .map(|suggestion| format!("- `{}`", suggestion)) + .collect::>() + .join("\n"))); + + } else { + err.note("did you mean to use one of the enum's variants?"); + } + return (err, candidates); + }, _ if ns == ValueNS && is_struct_like(def) => { if let Def::Struct(def_id) = def { if let Some((ctor_def, ctor_vis)) @@ -2587,7 +2671,7 @@ impl<'a> Resolver<'a> { } } err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?", - path_str)); + path_str)); return (err, candidates); } _ => {} @@ -2842,12 +2926,13 @@ impl<'a> Resolver<'a> { debug!("resolve_path ident {} {:?}", i, ident); let is_last = i == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let name = ident.node.name; - if i == 0 && ns == TypeNS && ident.node.name == keywords::SelfValue.name() { + if i == 0 && ns == TypeNS && name == keywords::SelfValue.name() { let mut ctxt = ident.node.ctxt.modern(); module = Some(self.resolve_self(&mut ctxt, self.current_module)); continue - } else if allow_super && ns == TypeNS && ident.node.name == keywords::Super.name() { + } else if allow_super && ns == TypeNS && name == keywords::Super.name() { let mut ctxt = ident.node.ctxt.modern(); let self_module = match i { 0 => self.resolve_self(&mut ctxt, self.current_module), @@ -2863,12 +2948,41 @@ impl<'a> Resolver<'a> { } allow_super = false; - if i == 0 && ns == TypeNS && ident.node.name == keywords::CrateRoot.name() { - module = Some(self.resolve_crate_root(ident.node.ctxt.modern())); - continue - } else if i == 0 && ns == TypeNS && ident.node.name == keywords::DollarCrate.name() { - module = Some(self.resolve_crate_root(ident.node.ctxt)); - continue + if ns == TypeNS { + if (i == 0 && name == keywords::CrateRoot.name()) || + (i == 1 && name == keywords::Crate.name() && + path[0].node.name == keywords::CrateRoot.name()) { + // `::a::b` or `::crate::a::b` + module = Some(self.resolve_crate_root(ident.node.ctxt.modern())); + continue + } else if i == 0 && name == keywords::DollarCrate.name() { + // `$crate::a::b` + module = Some(self.resolve_crate_root(ident.node.ctxt)); + continue + } + } + + // Report special messages for path segment keywords in wrong positions. + if name == keywords::CrateRoot.name() && i != 0 || + name == keywords::DollarCrate.name() && i != 0 || + name == keywords::SelfValue.name() && i != 0 || + name == keywords::SelfType.name() && i != 0 || + name == keywords::Super.name() && i != 0 || + name == keywords::Crate.name() && i != 1 && + path[0].node.name != keywords::CrateRoot.name() { + let name_str = if name == keywords::CrateRoot.name() { + format!("crate root") + } else { + format!("`{}`", name) + }; + let msg = if i == 1 && path[0].node.name == keywords::CrateRoot.name() { + format!("global paths cannot start with {}", name_str) + } else if i == 0 && name == keywords::Crate.name() { + format!("{} can only be used in absolute paths", name_str) + } else { + format!("{} in paths can only be used in start position", name_str) + }; + return PathResult::Failed(ident.span, msg, false); } let binding = if let Some(module) = module { @@ -2919,7 +3033,7 @@ impl<'a> Resolver<'a> { let msg = if module.and_then(ModuleData::def) == self.graph_root.def() { let is_mod = |def| match def { Def::Mod(..) => true, _ => false }; let mut candidates = - self.lookup_import_candidates(ident.node.name, TypeNS, is_mod); + self.lookup_import_candidates(name, TypeNS, is_mod); candidates.sort_by_key(|c| (c.path.segments.len(), c.path.to_string())); if let Some(candidate) = candidates.get(0) { format!("Did you mean `{}`?", candidate.path) @@ -2992,7 +3106,7 @@ impl<'a> Resolver<'a> { seen.insert(node_id, depth); } } - ItemRibKind | MethodRibKind(_) => { + ItemRibKind | TraitOrImplItemRibKind => { // This was an attempt to access an upvar inside a // named function item. This is not allowed, so we // report an error. @@ -3016,7 +3130,7 @@ impl<'a> Resolver<'a> { Def::TyParam(..) | Def::SelfTy(..) => { for rib in ribs { match rib.kind { - NormalRibKind | MethodRibKind(_) | ClosureRibKind(..) | + NormalRibKind | TraitOrImplItemRibKind | ClosureRibKind(..) | ModuleRibKind(..) | MacroDefinition(..) | ForwardTyParamBanRibKind | ConstantItemRibKind => { // Nothing to do. Continue. @@ -3202,12 +3316,20 @@ impl<'a> Resolver<'a> { } ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => { - match self.search_label(label.node) { + match self.search_label(label.node, |rib, id| rib.bindings.get(&id).cloned()) { None => { + // Search again for close matches... + // Picks the first label that is "close enough", which is not necessarily + // the closest match + let close_match = self.search_label(label.node, |rib, ident| { + let names = rib.bindings.iter().map(|(id, _)| &id.name); + find_best_match_for_name(names, &*ident.name.as_str(), None) + }); self.record_def(expr.id, err_path_resolution()); resolve_error(self, label.span, - ResolutionError::UndeclaredLabel(&label.node.name.as_str())); + ResolutionError::UndeclaredLabel(&label.node.name.as_str(), + close_match)); } Some(def @ Def::Label(_)) => { // Since this def is a label, it is never read. @@ -3464,6 +3586,72 @@ impl<'a> Resolver<'a> { candidates } + fn find_module(&mut self, + module_def: Def) + -> Option<(Module<'a>, ImportSuggestion)> + { + let mut result = None; + let mut worklist = Vec::new(); + let mut seen_modules = FxHashSet(); + worklist.push((self.graph_root, Vec::new())); + + while let Some((in_module, path_segments)) = worklist.pop() { + // abort if the module is already found + if let Some(_) = result { break; } + + self.populate_module_if_necessary(in_module); + + in_module.for_each_child_stable(|ident, _, name_binding| { + // abort if the module is already found + if let Some(_) = result { + return (); + } + if let Some(module) = name_binding.module() { + // form the path + let mut path_segments = path_segments.clone(); + path_segments.push(ast::PathSegment::from_ident(ident, name_binding.span)); + if module.def() == Some(module_def) { + let path = Path { + span: name_binding.span, + segments: path_segments, + }; + result = Some((module, ImportSuggestion { path: path })); + } else { + // add the module to the lookup + if seen_modules.insert(module.def_id().unwrap()) { + worklist.push((module, path_segments)); + } + } + } + }); + } + + result + } + + fn collect_enum_variants(&mut self, enum_def: Def) -> Option> { + if let Def::Enum(..) = enum_def {} else { + panic!("Non-enum def passed to collect_enum_variants: {:?}", enum_def) + } + + self.find_module(enum_def).map(|(enum_module, enum_import_suggestion)| { + self.populate_module_if_necessary(enum_module); + + let mut variants = Vec::new(); + enum_module.for_each_child_stable(|ident, _, name_binding| { + if let Def::Variant(..) = name_binding.def() { + let mut segms = enum_import_suggestion.path.segments.clone(); + segms.push(ast::PathSegment::from_ident(ident, name_binding.span)); + variants.push(Path { + span: name_binding.span, + segments: segms, + }); + } + }); + variants + }) + } + fn record_def(&mut self, node_id: NodeId, resolution: PathResolution) { debug!("(recording def) recording {:?} for {}", resolution, node_id); if let Some(prev_res) = self.def_map.insert(node_id, resolution) { @@ -3508,6 +3696,7 @@ impl<'a> Resolver<'a> { fn report_errors(&mut self, krate: &Crate) { self.report_shadowing_errors(); self.report_with_use_injections(krate); + self.report_proc_macro_import(krate); let mut reported_spans = FxHashSet(); for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors { @@ -3557,14 +3746,9 @@ impl<'a> Resolver<'a> { fn report_with_use_injections(&mut self, krate: &Crate) { for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) { - let mut finder = UsePlacementFinder { - target_module: node_id, - span: None, - found_use: false, - }; - visit::walk_crate(&mut finder, krate); + let (span, found_use) = UsePlacementFinder::check(krate, node_id); if !candidates.is_empty() { - show_candidates(&mut err, finder.span, &candidates, better, finder.found_use); + show_candidates(&mut err, span, &candidates, better, found_use); } err.emit(); } @@ -3588,12 +3772,12 @@ impl<'a> Resolver<'a> { } } - fn report_conflict(&mut self, + fn report_conflict<'b>(&mut self, parent: Module, ident: Ident, ns: Namespace, - new_binding: &NameBinding, - old_binding: &NameBinding) { + new_binding: &NameBinding<'b>, + old_binding: &NameBinding<'b>) { // Error on the second of two conflicting names if old_binding.span.lo() > new_binding.span.lo() { return self.report_conflict(parent, ident, ns, old_binding, new_binding); @@ -3665,6 +3849,27 @@ impl<'a> Resolver<'a> { old_noun, old_kind, name)); } + // See https://github.com/rust-lang/rust/issues/32354 + if old_binding.is_import() || new_binding.is_import() { + let binding = if new_binding.is_import() { + new_binding + } else { + old_binding + }; + + let cm = self.session.codemap(); + let rename_msg = "You can use `as` to change the binding name of the import"; + + if let (Ok(snippet), false) = (cm.span_to_snippet(binding.span), + binding.is_renamed_extern_crate()) { + err.span_suggestion(binding.span, + rename_msg, + format!("{} as Other{}", snippet, name)); + } else { + err.span_label(binding.span, rename_msg); + } + } + err.emit(); self.name_already_seen.insert(name, span); } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 064032b888499..3d1d7c0c48a1e 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -13,6 +13,7 @@ use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult}; use Namespace::{self, MacroNS}; use build_reduced_graph::BuildReducedGraphVisitor; use resolve_imports::ImportResolver; +use rustc_data_structures::indexed_vec::Idx; use rustc::hir::def_id::{DefId, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefIndex}; use rustc::hir::def::{Def, Export}; use rustc::hir::map::{self, DefCollector}; @@ -82,6 +83,14 @@ pub struct LegacyBinding<'a> { pub span: Span, } +pub struct ProcMacError { + crate_name: Symbol, + name: Symbol, + module: ast::NodeId, + use_span: Span, + warn_msg: &'static str, +} + #[derive(Copy, Clone)] pub enum MacroBinding<'a> { Legacy(&'a LegacyBinding<'a>), @@ -778,12 +787,37 @@ impl<'a> Resolver<'a> { _ => return, }; - let crate_name = self.cstore.crate_name_untracked(krate); + let def_id = self.current_module.normal_ancestor_id; + let node_id = self.definitions.as_local_node_id(def_id).unwrap(); + + self.proc_mac_errors.push(ProcMacError { + crate_name: self.cstore.crate_name_untracked(krate), + name, + module: node_id, + use_span, + warn_msg, + }); + } + + pub fn report_proc_macro_import(&mut self, krate: &ast::Crate) { + for err in self.proc_mac_errors.drain(..) { + let (span, found_use) = ::UsePlacementFinder::check(krate, err.module); - self.session.struct_span_err(use_span, warn_msg) - .help(&format!("instead, import the procedural macro like any other item: \ - `use {}::{};`", crate_name, name)) - .emit(); + if let Some(span) = span { + let found_use = if found_use { "" } else { "\n" }; + self.session.struct_span_err(err.use_span, err.warn_msg) + .span_suggestion( + span, + "instead, import the procedural macro like any other item", + format!("use {}::{};{}", err.crate_name, err.name, found_use), + ).emit(); + } else { + self.session.struct_span_err(err.use_span, err.warn_msg) + .help(&format!("instead, import the procedural macro like any other item: \ + `use {}::{};`", err.crate_name, err.name)) + .emit(); + } + } } fn gate_legacy_custom_derive(&mut self, name: Symbol, span: Span) { diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index b85bf18ea800c..d72253e5a8a48 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -23,7 +23,7 @@ use rustc::hir::def_id::DefId; use rustc::hir::def::*; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use syntax::ast::{Ident, SpannedIdent, NodeId}; +use syntax::ast::{Ident, Name, SpannedIdent, NodeId}; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; use syntax::ext::hygiene::Mark; use syntax::parse::token; @@ -48,7 +48,7 @@ pub enum ImportDirectiveSubclass<'a> { max_vis: Cell, // The visibility of the greatest reexport. // n.b. `max_vis` is only used in `finalize_import` to check for reexport errors. }, - ExternCrate, + ExternCrate(Option), MacroUse, } @@ -606,10 +606,16 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let module_result = self.resolve_path(&module_path, None, true, span); let module = match module_result { PathResult::Module(module) => module, - PathResult::Failed(span, msg, _) => { + PathResult::Failed(span, msg, false) => { + resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); + return None; + } + PathResult::Failed(span, msg, true) => { let (mut self_path, mut self_result) = (module_path.clone(), None); if !self_path.is_empty() && - !token::Ident(self_path[0].node).is_path_segment_keyword() + !token::Ident(self_path[0].node).is_path_segment_keyword() && + !(self_path.len() > 1 && + token::Ident(self_path[1].node).is_path_segment_keyword()) { self_path[0].node.name = keywords::SelfValue.name(); self_result = Some(self.resolve_path(&self_path, None, false, span)); @@ -923,7 +929,7 @@ fn import_directive_subclass_to_string(subclass: &ImportDirectiveSubclass) -> St match *subclass { SingleImport { source, .. } => source.to_string(), GlobImport { .. } => "*".to_string(), - ExternCrate => "".to_string(), + ExternCrate(_) => "".to_string(), MacroUse => "#[macro_use]".to_string(), } } diff --git a/src/librustc_save_analysis/Cargo.toml b/src/librustc_save_analysis/Cargo.toml index aa249af363f41..6c8c9b7328662 100644 --- a/src/librustc_save_analysis/Cargo.toml +++ b/src/librustc_save_analysis/Cargo.toml @@ -15,7 +15,7 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_typeck = { path = "../librustc_typeck" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rls-data = "0.10" +rls-data = "0.13" rls-span = "0.4" # FIXME(#40527) should move rustc serialize out of tree rustc-serialize = "0.3" diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 3e730cf836523..602c70f9a1f4b 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -32,22 +32,22 @@ use rustc_data_structures::fx::FxHashSet; use std::path::Path; -use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}; +use syntax::ast::{self, Attribute, NodeId, PatKind, CRATE_NODE_ID}; use syntax::parse::token; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; -use syntax::print::pprust::{path_to_string, ty_to_string, bounds_to_string, generics_to_string}; +use syntax::print::pprust::{bounds_to_string, generics_to_string, path_to_string, ty_to_string}; use syntax::ptr::P; -use syntax::codemap::Spanned; +use syntax::codemap::{Spanned, DUMMY_SP}; use syntax_pos::*; -use {escape, generated_code, SaveContext, PathCollector, lower_attributes}; -use json_dumper::{JsonDumper, DumpOutput}; +use {escape, generated_code, lower_attributes, PathCollector, SaveContext}; +use json_dumper::{Access, DumpOutput, JsonDumper}; use span_utils::SpanUtils; use sig; -use rls_data::{CratePreludeData, Import, ImportKind, SpanData, Ref, RefKind, - Def, DefKind, Relation, RelationKind}; +use rls_data::{CratePreludeData, Def, DefKind, GlobalCrateId, Import, ImportKind, Ref, RefKind, + Relation, RelationKind, SpanData}; macro_rules! down_cast_data { ($id:ident, $kind:ident, $sp:expr) => { @@ -59,6 +59,15 @@ macro_rules! down_cast_data { }; } +macro_rules! access_from { + ($save_ctxt:expr, $item:expr) => { + Access { + public: $item.vis == ast::Visibility::Public, + reachable: $save_ctxt.analysis.access_levels.is_reachable($item.id), + } + } +} + pub struct DumpVisitor<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> { save_ctxt: SaveContext<'l, 'tcx>, tcx: TyCtxt<'l, 'tcx, 'tcx>, @@ -77,9 +86,10 @@ pub struct DumpVisitor<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> { } impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { - pub fn new(save_ctxt: SaveContext<'l, 'tcx>, - dumper: &'ll mut JsonDumper) - -> DumpVisitor<'l, 'tcx, 'll, O> { + pub fn new( + save_ctxt: SaveContext<'l, 'tcx>, + dumper: &'ll mut JsonDumper, + ) -> DumpVisitor<'l, 'tcx, 'll, O> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); DumpVisitor { tcx: save_ctxt.tcx, @@ -93,7 +103,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } fn nest_scope(&mut self, scope_id: NodeId, f: F) - where F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, O>) + where + F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, O>), { let parent_scope = self.cur_scope; self.cur_scope = scope_id; @@ -102,7 +113,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } fn nest_tables(&mut self, item_id: NodeId, f: F) - where F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, O>) + where + F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, O>), { let item_def_id = self.tcx.hir.local_def_id(item_id); if self.tcx.has_typeck_tables(item_def_id) { @@ -131,7 +143,14 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { }); let data = CratePreludeData { - crate_name: name.into(), + crate_id: GlobalCrateId { + name: name.into(), + disambiguator: self.tcx + .sess + .local_crate_disambiguator() + .to_fingerprint() + .as_value(), + }, crate_root: crate_root.unwrap_or("".to_owned()), external_crates: self.save_ctxt.get_external_crates(), span: self.span_from_span(krate.span), @@ -206,10 +225,10 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { if len <= 1 { return; } - let sub_paths = &sub_paths[.. (len-1)]; + let sub_paths = &sub_paths[..(len - 1)]; // write the trait part of the sub-path - let (ref span, _) = sub_paths[len-2]; + let (ref span, _) = sub_paths[len - 2]; let span = self.span_from_span(*span); self.dumper.dump_ref(Ref { kind: RefKind::Type, @@ -221,7 +240,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { if len <= 2 { return; } - let sub_paths = &sub_paths[..len-2]; + let sub_paths = &sub_paths[..len - 2]; for &(ref span, _) in sub_paths { let span = self.span_from_span(*span); self.dumper.dump_ref(Ref { @@ -239,11 +258,13 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } - fn process_def_kind(&mut self, - ref_id: NodeId, - span: Span, - sub_span: Option, - def_id: DefId) { + fn process_def_kind( + &mut self, + ref_id: NodeId, + span: Span, + sub_span: Option, + def_id: DefId, + ) { if self.span.filter_generated(sub_span, span) { return; } @@ -263,6 +284,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { HirDef::Union(..) | HirDef::Enum(..) | HirDef::TyAlias(..) | + HirDef::TyForeign(..) | HirDef::Trait(_) => { let span = self.span_from_span(sub_span.expect("No span found for type ref")); self.dumper.dump_ref(Ref { @@ -304,9 +326,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { HirDef::PrimTy(_) | HirDef::GlobalAsm(_) | HirDef::Err => { - span_bug!(span, - "process_def_kind for unexpected item: {:?}", - def); + span_bug!(span, "process_def_kind for unexpected item: {:?}", def); } } } @@ -317,61 +337,74 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { let mut collector = PathCollector::new(); collector.visit_pat(&arg.pat); let span_utils = self.span.clone(); - for &(id, ref p, ..) in &collector.collected_paths { + + for (id, i, sp, ..) in collector.collected_idents { let hir_id = self.tcx.hir.node_to_hir_id(id); let typ = match self.save_ctxt.tables.node_id_to_type_opt(hir_id) { Some(s) => s.to_string(), None => continue, }; - // get the span only for the name of the variable (I hope the path is only ever a - // variable name, but who knows?) - let sub_span = span_utils.span_for_last_ident(p.span); - if !self.span.filter_generated(sub_span, p.span) { + let sub_span = span_utils.span_for_last_ident(sp); + if !self.span.filter_generated(sub_span, sp) { let id = ::id_from_node_id(id, &self.save_ctxt); let span = self.span_from_span(sub_span.expect("No span found for variable")); - self.dumper.dump_def(false, Def { - kind: DefKind::Local, - id, - span, - name: path_to_string(p), - qualname: format!("{}::{}", qualname, path_to_string(p)), - value: typ, - parent: None, - children: vec![], - decl_id: None, - docs: String::new(), - sig: None, - attributes:vec![], - }); + self.dumper.dump_def( + &Access { + public: false, + reachable: false, + }, + Def { + kind: DefKind::Local, + id, + span, + name: i.to_string(), + qualname: format!("{}::{}", qualname, i.to_string()), + value: typ, + parent: None, + children: vec![], + decl_id: None, + docs: String::new(), + sig: None, + attributes: vec![], + }, + ); } } } } - fn process_method(&mut self, - sig: &'l ast::MethodSig, - body: Option<&'l ast::Block>, - id: ast::NodeId, - name: ast::Ident, - vis: ast::Visibility, - span: Span) { + fn process_method( + &mut self, + sig: &'l ast::MethodSig, + body: Option<&'l ast::Block>, + id: ast::NodeId, + name: ast::Ident, + generics: &'l ast::Generics, + vis: ast::Visibility, + span: Span, + ) { debug!("process_method: {}:{}", id, name); if let Some(mut method_data) = self.save_ctxt.get_method_data(id, name.name, span) { - - let sig_str = ::make_signature(&sig.decl, &sig.generics); + let sig_str = ::make_signature(&sig.decl, &generics); if body.is_some() { - self.nest_tables(id, |v| { - v.process_formals(&sig.decl.inputs, &method_data.qualname) - }); + self.nest_tables( + id, + |v| v.process_formals(&sig.decl.inputs, &method_data.qualname), + ); } - self.process_generic_params(&sig.generics, span, &method_data.qualname, id); + self.process_generic_params(&generics, span, &method_data.qualname, id); method_data.value = sig_str; - method_data.sig = sig::method_signature(id, name, sig, &self.save_ctxt); - self.dumper.dump_def(vis == ast::Visibility::Public, method_data); + method_data.sig = sig::method_signature(id, name, generics, sig, &self.save_ctxt); + self.dumper.dump_def( + &Access { + public: vis == ast::Visibility::Public, + reachable: self.save_ctxt.analysis.access_levels.is_reachable(id), + }, + method_data); } // walk arg and return types @@ -389,68 +422,70 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } - fn process_trait_ref(&mut self, trait_ref: &'l ast::TraitRef) { - let trait_ref_data = self.save_ctxt.get_trait_ref_data(trait_ref); - if let Some(trait_ref_data) = trait_ref_data { - self.dumper.dump_ref(trait_ref_data); - } - self.process_path(trait_ref.ref_id, &trait_ref.path); - } - fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) { let field_data = self.save_ctxt.get_field_data(field, parent_id); if let Some(field_data) = field_data { - self.dumper.dump_def(field.vis == ast::Visibility::Public, field_data); + self.dumper.dump_def(&access_from!(self.save_ctxt, field), field_data); } } // Dump generic params bindings, then visit_generics - fn process_generic_params(&mut self, - generics: &'l ast::Generics, - full_span: Span, - prefix: &str, - id: NodeId) { + fn process_generic_params( + &mut self, + generics: &'l ast::Generics, + full_span: Span, + prefix: &str, + id: NodeId, + ) { for param in &generics.ty_params { let param_ss = param.span; let name = escape(self.span.snippet(param_ss)); // Append $id to name to make sure each one is unique - let qualname = format!("{}::{}${}", - prefix, - name, - id); + let qualname = format!("{}::{}${}", prefix, name, id); if !self.span.filter_generated(Some(param_ss), full_span) { let id = ::id_from_node_id(param.id, &self.save_ctxt); let span = self.span_from_span(param_ss); - self.dumper.dump_def(false, Def { - kind: DefKind::Type, - id, - span, - name, - qualname, - value: String::new(), - parent: None, - children: vec![], - decl_id: None, - docs: String::new(), - sig: None, - attributes: vec![], - }); + self.dumper.dump_def( + &Access { + public: false, + reachable: false, + }, + Def { + kind: DefKind::Type, + id, + span, + name, + qualname, + value: String::new(), + parent: None, + children: vec![], + decl_id: None, + docs: String::new(), + sig: None, + attributes: vec![], + }, + ); } } self.visit_generics(generics); } - fn process_fn(&mut self, - item: &'l ast::Item, - decl: &'l ast::FnDecl, - ty_params: &'l ast::Generics, - body: &'l ast::Block) { + fn process_fn( + &mut self, + item: &'l ast::Item, + decl: &'l ast::FnDecl, + ty_params: &'l ast::Generics, + body: &'l ast::Block, + ) { if let Some(fn_data) = self.save_ctxt.get_item_data(item) { down_cast_data!(fn_data, DefData, item.span); - self.nest_tables(item.id, |v| v.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); - self.dumper.dump_def(item.vis == ast::Visibility::Public, fn_data); + self.dumper.dump_def(&access_from!(self.save_ctxt, item), fn_data); } for arg in &decl.inputs { @@ -464,52 +499,61 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { self.nest_tables(item.id, |v| v.nest_scope(item.id, |v| v.visit_block(&body))); } - fn process_static_or_const_item(&mut self, - item: &'l ast::Item, - typ: &'l ast::Ty, - expr: &'l ast::Expr) { + fn process_static_or_const_item( + &mut self, + item: &'l ast::Item, + typ: &'l ast::Ty, + expr: &'l ast::Expr, + ) { self.nest_tables(item.id, |v| { if let Some(var_data) = v.save_ctxt.get_item_data(item) { down_cast_data!(var_data, DefData, item.span); - v.dumper.dump_def(item.vis == ast::Visibility::Public, var_data); + v.dumper.dump_def(&access_from!(v.save_ctxt, item), var_data); } v.visit_ty(&typ); v.visit_expr(expr); }); } - fn process_assoc_const(&mut self, - id: ast::NodeId, - name: ast::Name, - span: Span, - typ: &'l ast::Ty, - expr: Option<&'l ast::Expr>, - parent_id: DefId, - vis: ast::Visibility, - attrs: &'l [Attribute]) { + fn process_assoc_const( + &mut self, + id: ast::NodeId, + name: ast::Name, + span: Span, + typ: &'l ast::Ty, + expr: Option<&'l ast::Expr>, + parent_id: DefId, + vis: ast::Visibility, + attrs: &'l [Attribute], + ) { let qualname = format!("::{}", self.tcx.node_path_str(id)); let sub_span = self.span.sub_span_after_keyword(span, keywords::Const); if !self.span.filter_generated(sub_span, span) { let sig = sig::assoc_const_signature(id, name, typ, expr, &self.save_ctxt); - let id = ::id_from_node_id(id, &self.save_ctxt); let span = self.span_from_span(sub_span.expect("No span found for variable")); - self.dumper.dump_def(vis == ast::Visibility::Public, Def { - kind: DefKind::Const, - id, - span, - name: name.to_string(), - qualname, - value: ty_to_string(&typ), - parent: Some(::id_from_def_id(parent_id)), - children: vec![], - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(attrs), - sig, - attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt), - }); + self.dumper.dump_def( + &Access { + public: vis == ast::Visibility::Public, + reachable: self.save_ctxt.analysis.access_levels.is_reachable(id), + }, + Def { + kind: DefKind::Const, + id: ::id_from_node_id(id, &self.save_ctxt), + span, + name: name.to_string(), + qualname, + value: ty_to_string(&typ), + parent: Some(::id_from_def_id(parent_id)), + children: vec![], + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(attrs), + sig, + attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt), + }, + ); } // walk type and init value @@ -520,52 +564,72 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } // FIXME tuple structs should generate tuple-specific data. - fn process_struct(&mut self, - item: &'l ast::Item, - def: &'l ast::VariantData, - ty_params: &'l ast::Generics) { + fn process_struct( + &mut self, + item: &'l ast::Item, + def: &'l ast::VariantData, + ty_params: &'l ast::Generics, + ) { + debug!("process_struct {:?} {:?}", item, item.span); let name = item.ident.to_string(); let qualname = format!("::{}", self.tcx.node_path_str(item.id)); - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct); - let (value, fields) = - if let ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) = item.node - { - let include_priv_fields = !self.save_ctxt.config.pub_only; - let fields_str = fields - .iter() - .enumerate() - .filter_map(|(i, f)| { - if include_priv_fields || f.vis == ast::Visibility::Public { - f.ident.map(|i| i.to_string()).or_else(|| Some(i.to_string())) - } else { - None - } - }) - .collect::>() - .join(", "); - let value = format!("{} {{ {} }}", name, fields_str); - (value, fields.iter().map(|f| ::id_from_node_id(f.id, &self.save_ctxt)).collect()) - } else { - (String::new(), vec![]) + let (kind, keyword) = match item.node { + ast::ItemKind::Struct(_, _) => (DefKind::Struct, keywords::Struct), + ast::ItemKind::Union(_, _) => (DefKind::Union, keywords::Union), + _ => unreachable!(), + }; + + let sub_span = self.span.sub_span_after_keyword(item.span, keyword); + let (value, fields) = match item.node { + ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) | + ast::ItemKind::Union(ast::VariantData::Struct(ref fields, _), _) => { + let include_priv_fields = !self.save_ctxt.config.pub_only; + let fields_str = fields + .iter() + .enumerate() + .filter_map(|(i, f)| { + if include_priv_fields || f.vis == ast::Visibility::Public { + f.ident + .map(|i| i.to_string()) + .or_else(|| Some(i.to_string())) + } else { + None + } + }) + .collect::>() + .join(", "); + let value = format!("{} {{ {} }}", name, fields_str); + ( + value, + fields + .iter() + .map(|f| ::id_from_node_id(f.id, &self.save_ctxt)) + .collect(), + ) + } + _ => (String::new(), vec![]), }; if !self.span.filter_generated(sub_span, item.span) { let span = self.span_from_span(sub_span.expect("No span found for struct")); - self.dumper.dump_def(item.vis == ast::Visibility::Public, Def { - kind: DefKind::Struct, - id: ::id_from_node_id(item.id, &self.save_ctxt), - span, - name, - qualname: qualname.clone(), - value, - parent: None, - children: fields, - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), - sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), - }); + self.dumper.dump_def( + &access_from!(self.save_ctxt, item), + Def { + kind, + id: ::id_from_node_id(item.id, &self.save_ctxt), + span, + name, + qualname: qualname.clone(), + value, + parent: None, + children: fields, + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&item.attrs), + sig: sig::item_signature(item, &self.save_ctxt), + attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), + }, + ); } for field in def.fields() { @@ -576,10 +640,12 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { self.process_generic_params(ty_params, item.span, &qualname, item.id); } - fn process_enum(&mut self, - item: &'l ast::Item, - enum_definition: &'l ast::EnumDef, - ty_params: &'l ast::Generics) { + fn process_enum( + &mut self, + item: &'l ast::Item, + enum_definition: &'l ast::EnumDef, + ty_params: &'l ast::Generics, + ) { let enum_data = self.save_ctxt.get_item_data(item); let enum_data = match enum_data { None => return, @@ -587,6 +653,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { }; down_cast_data!(enum_data, DefData, item.span); + let access = access_from!(self.save_ctxt, item); + for variant in &enum_definition.variants { let name = variant.node.name.name.to_string(); let mut qualname = enum_data.qualname.clone(); @@ -596,34 +664,41 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { match variant.node.data { ast::VariantData::Struct(ref fields, _) => { let sub_span = self.span.span_for_first_ident(variant.span); - let fields_str = fields.iter() - .enumerate() - .map(|(i, f)| f.ident.map(|i| i.to_string()) - .unwrap_or(i.to_string())) - .collect::>() - .join(", "); + let fields_str = fields + .iter() + .enumerate() + .map(|(i, f)| { + f.ident.map(|i| i.to_string()).unwrap_or(i.to_string()) + }) + .collect::>() + .join(", "); let value = format!("{}::{} {{ {} }}", enum_data.name, name, fields_str); if !self.span.filter_generated(sub_span, variant.span) { - let span = self.span_from_span( - sub_span.expect("No span found for struct variant")); + let span = self + .span_from_span(sub_span.expect("No span found for struct variant")); let id = ::id_from_node_id(variant.node.data.id(), &self.save_ctxt); let parent = Some(::id_from_node_id(item.id, &self.save_ctxt)); - self.dumper.dump_def(item.vis == ast::Visibility::Public, Def { - kind: DefKind::Struct, - id, - span, - name, - qualname, - value, - parent, - children: vec![], - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&variant.node.attrs), - sig: sig::variant_signature(variant, &self.save_ctxt), - attributes: lower_attributes(variant.node.attrs.clone(), - &self.save_ctxt), - }); + self.dumper.dump_def( + &access, + Def { + kind: DefKind::StructVariant, + id, + span, + name, + qualname, + value, + parent, + children: vec![], + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&variant.node.attrs), + sig: sig::variant_signature(variant, &self.save_ctxt), + attributes: lower_attributes( + variant.node.attrs.clone(), + &self.save_ctxt, + ), + }, + ); } } ref v => { @@ -631,10 +706,11 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { let mut value = format!("{}::{}", enum_data.name, name); if let &ast::VariantData::Tuple(ref fields, _) = v { value.push('('); - value.push_str(&fields.iter() - .map(|f| ty_to_string(&f.ty)) - .collect::>() - .join(", ")); + value.push_str(&fields + .iter() + .map(|f| ty_to_string(&f.ty)) + .collect::>() + .join(", ")); value.push(')'); } if !self.span.filter_generated(sub_span, variant.span) { @@ -643,21 +719,26 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { let id = ::id_from_node_id(variant.node.data.id(), &self.save_ctxt); let parent = Some(::id_from_node_id(item.id, &self.save_ctxt)); - self.dumper.dump_def(item.vis == ast::Visibility::Public, Def { - kind: DefKind::Tuple, - id, - span, - name, - qualname, - value, - parent, - children: vec![], - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&variant.node.attrs), - sig: sig::variant_signature(variant, &self.save_ctxt), - attributes: lower_attributes(variant.node.attrs.clone(), - &self.save_ctxt), - }); + self.dumper.dump_def( + &access, + Def { + kind: DefKind::TupleVariant, + id, + span, + name, + qualname, + value, + parent, + children: vec![], + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&variant.node.attrs), + sig: sig::variant_signature(variant, &self.save_ctxt), + attributes: lower_attributes( + variant.node.attrs.clone(), + &self.save_ctxt, + ), + }, + ); } } } @@ -669,15 +750,17 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } self.process_generic_params(ty_params, item.span, &enum_data.qualname, item.id); - self.dumper.dump_def(item.vis == ast::Visibility::Public, enum_data); + self.dumper.dump_def(&access, enum_data); } - fn process_impl(&mut self, - item: &'l ast::Item, - type_parameters: &'l ast::Generics, - trait_ref: &'l Option, - typ: &'l ast::Ty, - impl_items: &'l [ast::ImplItem]) { + fn process_impl( + &mut self, + item: &'l ast::Item, + type_parameters: &'l ast::Generics, + trait_ref: &'l Option, + typ: &'l ast::Ty, + impl_items: &'l [ast::ImplItem], + ) { if let Some(impl_data) = self.save_ctxt.get_item_data(item) { down_cast_data!(impl_data, RelationData, item.span); self.dumper.dump_relation(impl_data); @@ -693,11 +776,13 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } - fn process_trait(&mut self, - item: &'l ast::Item, - generics: &'l ast::Generics, - trait_refs: &'l ast::TyParamBounds, - methods: &'l [ast::TraitItem]) { + fn process_trait( + &mut self, + item: &'l ast::Item, + generics: &'l ast::Generics, + trait_refs: &'l ast::TyParamBounds, + methods: &'l [ast::TraitItem], + ) { let name = item.ident.to_string(); let qualname = format!("::{}", self.tcx.node_path_str(item.id)); let mut val = name.clone(); @@ -712,30 +797,33 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { if !self.span.filter_generated(sub_span, item.span) { let id = ::id_from_node_id(item.id, &self.save_ctxt); let span = self.span_from_span(sub_span.expect("No span found for trait")); - let children = - methods.iter().map(|i| ::id_from_node_id(i.id, &self.save_ctxt)).collect(); - self.dumper.dump_def(item.vis == ast::Visibility::Public, Def { - kind: DefKind::Trait, - id, - span, - name, - qualname: qualname.clone(), - value: val, - parent: None, - children, - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), - sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), - }); + let children = methods + .iter() + .map(|i| ::id_from_node_id(i.id, &self.save_ctxt)) + .collect(); + self.dumper.dump_def( + &access_from!(self.save_ctxt, item), + Def { + kind: DefKind::Trait, + id, + span, + name, + qualname: qualname.clone(), + value: val, + parent: None, + children, + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&item.attrs), + sig: sig::item_signature(item, &self.save_ctxt), + attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), + }, + ); } // super-traits for super_bound in trait_refs.iter() { let trait_ref = match *super_bound { - ast::TraitTyParamBound(ref trait_ref, _) => { - trait_ref - } + ast::TraitTyParamBound(ref trait_ref, _) => trait_ref, ast::RegionTyParamBound(..) => { continue; } @@ -777,24 +865,42 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { fn process_mod(&mut self, item: &ast::Item) { if let Some(mod_data) = self.save_ctxt.get_item_data(item) { down_cast_data!(mod_data, DefData, item.span); - self.dumper.dump_def(item.vis == ast::Visibility::Public, mod_data); + self.dumper.dump_def(&access_from!(self.save_ctxt, item), mod_data); } } - fn process_path(&mut self, id: NodeId, path: &ast::Path) { + fn dump_path_ref(&mut self, id: NodeId, path: &ast::Path) { let path_data = self.save_ctxt.get_path_data(id, path); - if generated_code(path.span) && path_data.is_none() { - return; + if let Some(path_data) = path_data { + self.dumper.dump_ref(path_data); } + } - let path_data = match path_data { - Some(pd) => pd, - None => { - return; + fn process_path(&mut self, id: NodeId, path: &'l ast::Path) { + debug!("process_path {:?}", path); + if generated_code(path.span) { + return; + } + self.dump_path_ref(id, path); + + // Type parameters + for seg in &path.segments { + if let Some(ref params) = seg.parameters { + match **params { + ast::PathParameters::AngleBracketed(ref data) => for t in &data.types { + self.visit_ty(t); + }, + ast::PathParameters::Parenthesized(ref data) => { + for t in &data.inputs { + self.visit_ty(t); + } + if let Some(ref t) = data.output { + self.visit_ty(t); + } + } + } } - }; - - self.dumper.dump_ref(path_data); + } // Modules or types in the path prefix. match self.save_ctxt.get_path_def(id) { @@ -821,12 +927,14 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } - fn process_struct_lit(&mut self, - ex: &'l ast::Expr, - path: &'l ast::Path, - fields: &'l [ast::Field], - variant: &'l ty::VariantDef, - base: &'l Option>) { + fn process_struct_lit( + &mut self, + ex: &'l ast::Expr, + path: &'l ast::Path, + fields: &'l [ast::Field], + variant: &'l ty::VariantDef, + base: &'l Option>, + ) { self.write_sub_paths_truncated(path); if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { @@ -836,8 +944,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } for field in fields { - if let Some(field_data) = self.save_ctxt - .get_field_ref_data(field, variant) { + if let Some(field_data) = self.save_ctxt.get_field_ref_data(field, variant) { self.dumper.dump_ref(field_data); } @@ -848,7 +955,13 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { walk_list!(self, visit_expr, base); } - fn process_method_call(&mut self, ex: &'l ast::Expr, args: &'l [P]) { + fn process_method_call( + &mut self, + ex: &'l ast::Expr, + seg: &'l ast::PathSegment, + args: &'l [P], + ) { + debug!("process_method_call {:?} {:?}", ex, ex.span); if let Some(mcd) = self.save_ctxt.get_expr_data(ex) { down_cast_data!(mcd, RefData, ex.span); if !generated_code(ex.span) { @@ -856,6 +969,15 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { } } + // Explicit types in the turbo-fish. + if let Some(ref params) = seg.parameters { + if let ast::PathParameters::AngleBracketed(ref data) = **params { + for t in &data.types { + self.visit_ty(t); + } + } + } + // walk receiver and args walk_list!(self, visit_expr, args); } @@ -874,7 +996,11 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { }; let variant = adt.variant_of_def(self.save_ctxt.get_path_def(p.id)); - for &Spanned { node: ref field, span } in fields { + for &Spanned { + node: ref field, + span, + } in fields + { let sub_span = self.span.span_for_first_ident(span); if let Some(f) = variant.find_field_named(field.ident.name) { if !self.span.filter_generated(sub_span, span) { @@ -902,7 +1028,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { collector.visit_pat(&p); self.visit_pat(&p); - for &(id, ref p, immut) in &collector.collected_paths { + for (id, i, sp, immut) in collector.collected_idents { let mut value = match immut { ast::Mutability::Immutable => value.to_string(), _ => String::new(), @@ -922,27 +1048,33 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { // Get the span only for the name of the variable (I hope the path // is only ever a variable name, but who knows?). - let sub_span = self.span.span_for_last_ident(p.span); + let sub_span = self.span.span_for_last_ident(sp); // Rust uses the id of the pattern for var lookups, so we'll use it too. - if !self.span.filter_generated(sub_span, p.span) { - let qualname = format!("{}${}", path_to_string(p), id); + if !self.span.filter_generated(sub_span, sp) { + let qualname = format!("{}${}", i.to_string(), id); let id = ::id_from_node_id(id, &self.save_ctxt); let span = self.span_from_span(sub_span.expect("No span found for variable")); - self.dumper.dump_def(false, Def { - kind: DefKind::Local, - id, - span, - name: path_to_string(p), - qualname, - value: typ, - parent: None, - children: vec![], - decl_id: None, - docs: String::new(), - sig: None, - attributes:vec![], - }); + self.dumper.dump_def( + &Access { + public: false, + reachable: false, + }, + Def { + kind: DefKind::Local, + id, + span, + name: i.to_string(), + qualname, + value: typ, + parent: None, + children: vec![], + decl_id: None, + docs: String::new(), + sig: None, + attributes: vec![], + }, + ); } } } @@ -993,51 +1125,65 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { self.process_macro_use(trait_item.span); match trait_item.node { ast::TraitItemKind::Const(ref ty, ref expr) => { - self.process_assoc_const(trait_item.id, - trait_item.ident.name, - trait_item.span, - &ty, - expr.as_ref().map(|e| &**e), - trait_id, - ast::Visibility::Public, - &trait_item.attrs); + self.process_assoc_const( + trait_item.id, + trait_item.ident.name, + trait_item.span, + &ty, + expr.as_ref().map(|e| &**e), + trait_id, + ast::Visibility::Public, + &trait_item.attrs, + ); } ast::TraitItemKind::Method(ref sig, ref body) => { - self.process_method(sig, - body.as_ref().map(|x| &**x), - trait_item.id, - trait_item.ident, - ast::Visibility::Public, - trait_item.span); + self.process_method( + sig, + body.as_ref().map(|x| &**x), + trait_item.id, + trait_item.ident, + &trait_item.generics, + ast::Visibility::Public, + trait_item.span, + ); } ast::TraitItemKind::Type(ref bounds, ref default_ty) => { // FIXME do something with _bounds (for type refs) let name = trait_item.ident.name.to_string(); let qualname = format!("::{}", self.tcx.node_path_str(trait_item.id)); - let sub_span = self.span.sub_span_after_keyword(trait_item.span, keywords::Type); + let sub_span = self.span + .sub_span_after_keyword(trait_item.span, keywords::Type); if !self.span.filter_generated(sub_span, trait_item.span) { let span = self.span_from_span(sub_span.expect("No span found for assoc type")); let id = ::id_from_node_id(trait_item.id, &self.save_ctxt); - self.dumper.dump_def(true, Def { - kind: DefKind::Type, - id, - span, - name, - qualname, - value: self.span.snippet(trait_item.span), - parent: Some(::id_from_def_id(trait_id)), - children: vec![], - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&trait_item.attrs), - sig: sig::assoc_type_signature(trait_item.id, - trait_item.ident, - Some(bounds), - default_ty.as_ref().map(|ty| &**ty), - &self.save_ctxt), - attributes: lower_attributes(trait_item.attrs.clone(), &self.save_ctxt), - }); + self.dumper.dump_def( + &Access { + public: true, + reachable: true, + }, + Def { + kind: DefKind::Type, + id, + span, + name, + qualname, + value: self.span.snippet(trait_item.span), + parent: Some(::id_from_def_id(trait_id)), + children: vec![], + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&trait_item.attrs), + sig: sig::assoc_type_signature( + trait_item.id, + trait_item.ident, + Some(bounds), + default_ty.as_ref().map(|ty| &**ty), + &self.save_ctxt, + ), + attributes: lower_attributes(trait_item.attrs.clone(), &self.save_ctxt), + }, + ); } if let &Some(ref default_ty) = default_ty { @@ -1052,22 +1198,27 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { self.process_macro_use(impl_item.span); match impl_item.node { ast::ImplItemKind::Const(ref ty, ref expr) => { - self.process_assoc_const(impl_item.id, - impl_item.ident.name, - impl_item.span, - &ty, - Some(expr), - impl_id, - impl_item.vis.clone(), - &impl_item.attrs); + self.process_assoc_const( + impl_item.id, + impl_item.ident.name, + impl_item.span, + &ty, + Some(expr), + impl_id, + impl_item.vis.clone(), + &impl_item.attrs, + ); } ast::ImplItemKind::Method(ref sig, ref body) => { - self.process_method(sig, - Some(body), - impl_item.id, - impl_item.ident, - impl_item.vis.clone(), - impl_item.span); + self.process_method( + sig, + Some(body), + impl_item.id, + impl_item.ident, + &impl_item.generics, + impl_item.vis.clone(), + impl_item.span, + ); } ast::ImplItemKind::Type(ref ty) => { // FIXME uses of the assoc type should ideally point to this @@ -1078,6 +1229,106 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { ast::ImplItemKind::Macro(_) => {} } } + + fn process_use_tree(&mut self, + use_tree: &'l ast::UseTree, + id: NodeId, + parent_item: &'l ast::Item, + prefix: &ast::Path) { + let path = &use_tree.prefix; + let access = access_from!(self.save_ctxt, parent_item); + + match use_tree.kind { + ast::UseTreeKind::Simple(ident) => { + let path = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + + let sub_span = self.span.span_for_last_ident(path.span); + let mod_id = match self.lookup_def_id(id) { + Some(def_id) => { + self.process_def_kind(id, path.span, sub_span, def_id); + Some(def_id) + } + None => None, + }; + + // 'use' always introduces an alias, if there is not an explicit + // one, there is an implicit one. + let sub_span = match self.span.sub_span_after_keyword(use_tree.span, + keywords::As) { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; + + if !self.span.filter_generated(sub_span, path.span) { + let span = + self.span_from_span(sub_span.expect("No span found for use")); + self.dumper.import(&access, Import { + kind: ImportKind::Use, + ref_id: mod_id.map(|id| ::id_from_def_id(id)), + span, + name: ident.to_string(), + value: String::new(), + }); + } + self.write_sub_paths_truncated(&path); + } + ast::UseTreeKind::Glob => { + let path = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + + // Make a comma-separated list of names of imported modules. + let mut names = vec![]; + let glob_map = &self.save_ctxt.analysis.glob_map; + let glob_map = glob_map.as_ref().unwrap(); + if glob_map.contains_key(&id) { + for n in glob_map.get(&id).unwrap() { + names.push(n.to_string()); + } + } + + let sub_span = self.span.sub_span_of_token(use_tree.span, + token::BinOp(token::Star)); + if !self.span.filter_generated(sub_span, use_tree.span) { + let span = + self.span_from_span(sub_span.expect("No span found for use glob")); + self.dumper.import(&access, Import { + kind: ImportKind::GlobUse, + ref_id: None, + span, + name: "*".to_owned(), + value: names.join(", "), + }); + } + self.write_sub_paths(&path); + } + ast::UseTreeKind::Nested(ref nested_items) => { + let prefix = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + for &(ref tree, id) in nested_items { + self.process_use_tree(tree, id, parent_item, &prefix); + } + } + } + } } impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, O> { @@ -1091,23 +1342,32 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc let cm = self.tcx.sess.codemap(); let filename = cm.span_to_filename(span); let data_id = ::id_from_node_id(id, &self.save_ctxt); - let children = m.items.iter().map(|i| ::id_from_node_id(i.id, &self.save_ctxt)).collect(); + let children = m.items + .iter() + .map(|i| ::id_from_node_id(i.id, &self.save_ctxt)) + .collect(); let span = self.span_from_span(span); - self.dumper.dump_def(true, Def { - kind: DefKind::Mod, - id: data_id, - name: String::new(), - qualname, - span, - value: filename, - children, - parent: None, - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(attrs), - sig: None, - attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt), - }); + self.dumper.dump_def( + &Access { + public: true, + reachable: true, + }, + Def { + kind: DefKind::Mod, + id: data_id, + name: String::new(), + qualname, + span, + value: filename, + children, + parent: None, + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(attrs), + sig: None, + attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt), + }, + ); self.nest_scope(id, |v| visit::walk_mod(v, m)); } @@ -1115,77 +1375,12 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc use syntax::ast::ItemKind::*; self.process_macro_use(item.span); match item.node { - Use(ref use_item) => { - match use_item.node { - ast::ViewPathSimple(ident, ref path) => { - let sub_span = self.span.span_for_last_ident(path.span); - let mod_id = match self.lookup_def_id(item.id) { - Some(def_id) => { - self.process_def_kind(item.id, path.span, sub_span, def_id); - Some(def_id) - } - None => None, - }; - - // 'use' always introduces an alias, if there is not an explicit - // one, there is an implicit one. - let sub_span = match self.span.sub_span_after_keyword(use_item.span, - keywords::As) { - Some(sub_span) => Some(sub_span), - None => sub_span, - }; - - if !self.span.filter_generated(sub_span, path.span) { - let span = - self.span_from_span(sub_span.expect("No span found for use")); - self.dumper.import(item.vis == ast::Visibility::Public, Import { - kind: ImportKind::Use, - ref_id: mod_id.map(|id| ::id_from_def_id(id)), - span, - name: ident.to_string(), - value: String::new(), - }); - } - self.write_sub_paths_truncated(path); - } - ast::ViewPathGlob(ref path) => { - // Make a comma-separated list of names of imported modules. - let mut names = vec![]; - let glob_map = &self.save_ctxt.analysis.glob_map; - let glob_map = glob_map.as_ref().unwrap(); - if glob_map.contains_key(&item.id) { - for n in glob_map.get(&item.id).unwrap() { - names.push(n.to_string()); - } - } - - let sub_span = self.span - .sub_span_of_token(item.span, token::BinOp(token::Star)); - if !self.span.filter_generated(sub_span, item.span) { - let span = - self.span_from_span(sub_span.expect("No span found for use glob")); - self.dumper.import(item.vis == ast::Visibility::Public, Import { - kind: ImportKind::GlobUse, - ref_id: None, - span, - name: "*".to_owned(), - value: names.join(", "), - }); - } - self.write_sub_paths(path); - } - ast::ViewPathList(ref path, ref list) => { - for plid in list { - let id = plid.node.id; - if let Some(def_id) = self.lookup_def_id(id) { - let span = plid.span; - self.process_def_kind(id, span, Some(span), def_id); - } - } - - self.write_sub_paths(path); - } - } + Use(ref use_tree) => { + let prefix = ast::Path { + segments: vec![], + span: DUMMY_SP, + }; + self.process_use_tree(use_tree, item.id, item, &prefix); } ExternCrate(_) => { let alias_span = self.span.span_for_last_ident(item.span); @@ -1193,32 +1388,36 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc if !self.span.filter_generated(alias_span, item.span) { let span = self.span_from_span(alias_span.expect("No span found for extern crate")); - self.dumper.import(false, Import { - kind: ImportKind::ExternCrate, - ref_id: None, - span, - name: item.ident.to_string(), - value: String::new(), - }); + self.dumper.import( + &Access { + public: false, + reachable: false, + }, + Import { + kind: ImportKind::ExternCrate, + ref_id: None, + span, + name: item.ident.to_string(), + value: String::new(), + }, + ); } } - Fn(ref decl, .., ref ty_params, ref body) => - self.process_fn(item, &decl, ty_params, &body), - Static(ref typ, _, ref expr) => - self.process_static_or_const_item(item, typ, expr), - Const(ref typ, ref expr) => - self.process_static_or_const_item(item, &typ, &expr), - Struct(ref def, ref ty_params) => self.process_struct(item, def, ty_params), + Fn(ref decl, .., ref ty_params, ref body) => { + self.process_fn(item, &decl, ty_params, &body) + } + Static(ref typ, _, ref expr) => self.process_static_or_const_item(item, typ, expr), + Const(ref typ, ref expr) => self.process_static_or_const_item(item, &typ, &expr), + Struct(ref def, ref ty_params) | Union(ref def, ref ty_params) => { + self.process_struct(item, def, ty_params) + } Enum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), - Impl(.., - ref ty_params, - ref trait_ref, - ref typ, - ref impl_items) => { + Impl(.., ref ty_params, ref trait_ref, ref typ, ref impl_items) => { self.process_impl(item, ty_params, trait_ref, &typ, impl_items) } - Trait(_, ref generics, ref trait_refs, ref methods) => - self.process_trait(item, generics, trait_refs, methods), + Trait(_, _, ref generics, ref trait_refs, ref methods) => { + self.process_trait(item, generics, trait_refs, methods) + } Mod(ref m) => { self.process_mod(item); self.nest_scope(item.id, |v| visit::walk_mod(v, m)); @@ -1231,20 +1430,23 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc let span = self.span_from_span(sub_span.expect("No span found for typedef")); let id = ::id_from_node_id(item.id, &self.save_ctxt); - self.dumper.dump_def(item.vis == ast::Visibility::Public, Def { - kind: DefKind::Type, - id, - span, - name: item.ident.to_string(), - qualname: qualname.clone(), - value, - parent: None, - children: vec![], - decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), - sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), - }); + self.dumper.dump_def( + &access_from!(self.save_ctxt, item), + Def { + kind: DefKind::Type, + id, + span, + name: item.ident.to_string(), + qualname: qualname.clone(), + value, + parent: None, + children: vec![], + decl_id: None, + docs: self.save_ctxt.docs_for_attrs(&item.attrs), + sig: sig::item_signature(item, &self.save_ctxt), + attributes: lower_attributes(item.attrs.clone(), &self.save_ctxt), + }, + ); } self.visit_ty(&ty); @@ -1259,7 +1461,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc for param in generics.ty_params.iter() { for bound in param.bounds.iter() { if let ast::TraitTyParamBound(ref trait_ref, _) = *bound { - self.process_trait_ref(&trait_ref.trait_ref); + self.process_path(trait_ref.trait_ref.ref_id, &trait_ref.trait_ref.path) } } if let Some(ref ty) = param.default { @@ -1314,7 +1516,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc let def = self.save_ctxt.get_path_def(hir_expr.id); self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base) } - ast::ExprKind::MethodCall(.., ref args) => self.process_method_call(ex, args), + ast::ExprKind::MethodCall(ref seg, ref args) => self.process_method_call(ex, seg, args), ast::ExprKind::Field(ref sub_ex, _) => { self.visit_expr(&sub_ex); @@ -1331,8 +1533,11 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc let hir_node = match self.save_ctxt.tcx.hir.find(sub_ex.id) { Some(Node::NodeExpr(expr)) => expr, _ => { - debug!("Missing or weird node for sub-expression {} in {:?}", - sub_ex.id, ex); + debug!( + "Missing or weird node for sub-expression {} in {:?}", + sub_ex.id, + ex + ); return; } }; @@ -1357,9 +1562,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc } } ty::TyTuple(..) => {} - _ => span_bug!(ex.span, - "Expected struct or tuple type, found {:?}", - ty), + _ => span_bug!(ex.span, "Expected struct or tuple type, found {:?}", ty), } } ast::ExprKind::Closure(_, ref decl, ref body, _fn_decl_span) => { @@ -1386,15 +1589,15 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc let value = self.span.snippet(subexpression.span); self.process_var_decl(pattern, value); debug!("for loop, walk sub-expr: {:?}", subexpression.node); - visit::walk_expr(self, subexpression); + self.visit_expr(subexpression); visit::walk_block(self, block); } ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => { let value = self.span.snippet(subexpression.span); self.process_var_decl(pattern, value); - visit::walk_expr(self, subexpression); + self.visit_expr(subexpression); visit::walk_block(self, block); - opt_else.as_ref().map(|el| visit::walk_expr(self, el)); + opt_else.as_ref().map(|el| self.visit_expr(el)); } ast::ExprKind::Repeat(ref element, ref count) => { self.visit_expr(element); @@ -1402,15 +1605,16 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc } // In particular, we take this branch for call and path expressions, // where we'll index the idents involved just by continuing to walk. - _ => { - visit::walk_expr(self, ex) - } + _ => visit::walk_expr(self, ex), } } fn visit_mac(&mut self, mac: &'l ast::Mac) { // These shouldn't exist in the AST at this point, log a span bug. - span_bug!(mac.span, "macro invocation should have been expanded out of AST"); + span_bug!( + mac.span, + "macro invocation should have been expanded out of AST" + ); } fn visit_pat(&mut self, p: &'l ast::Pat) { @@ -1426,63 +1630,70 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc self.visit_pat(&pattern); } - // This is to get around borrow checking, because we need mut self to call process_path. - let mut paths_to_process = vec![]; - // process collected paths - for &(id, ref p, immut) in &collector.collected_paths { + for (id, i, sp, immut) in collector.collected_idents { match self.save_ctxt.get_path_def(id) { HirDef::Local(id) => { let mut value = if immut == ast::Mutability::Immutable { - self.span.snippet(p.span).to_string() + self.span.snippet(sp).to_string() } else { "".to_string() }; let hir_id = self.tcx.hir.node_to_hir_id(id); let typ = self.save_ctxt - .tables - .node_id_to_type_opt(hir_id) - .map(|t| t.to_string()) - .unwrap_or(String::new()); + .tables + .node_id_to_type_opt(hir_id) + .map(|t| t.to_string()) + .unwrap_or(String::new()); value.push_str(": "); value.push_str(&typ); - assert!(p.segments.len() == 1, - "qualified path for local variable def in arm"); - if !self.span.filter_generated(Some(p.span), p.span) { - let qualname = format!("{}${}", path_to_string(p), id); + if !self.span.filter_generated(Some(sp), sp) { + let qualname = format!("{}${}", i.to_string(), id); let id = ::id_from_node_id(id, &self.save_ctxt); - let span = self.span_from_span(p.span); - - self.dumper.dump_def(false, Def { - kind: DefKind::Local, - id, - span, - name: path_to_string(p), - qualname, - value: typ, - parent: None, - children: vec![], - decl_id: None, - docs: String::new(), - sig: None, - attributes:vec![], - }); + let span = self.span_from_span(sp); + + self.dumper.dump_def( + &Access { + public: false, + reachable: false, + }, + Def { + kind: DefKind::Local, + id, + span, + name: i.to_string(), + qualname, + value: typ, + parent: None, + children: vec![], + decl_id: None, + docs: String::new(), + sig: None, + attributes: vec![], + }, + ); } } - HirDef::StructCtor(..) | HirDef::VariantCtor(..) | - HirDef::Const(..) | HirDef::AssociatedConst(..) | - HirDef::Struct(..) | HirDef::Variant(..) | - HirDef::TyAlias(..) | HirDef::AssociatedTy(..) | + HirDef::StructCtor(..) | + HirDef::VariantCtor(..) | + HirDef::Const(..) | + HirDef::AssociatedConst(..) | + HirDef::Struct(..) | + HirDef::Variant(..) | + HirDef::TyAlias(..) | + HirDef::AssociatedTy(..) | HirDef::SelfTy(..) => { - paths_to_process.push((id, p.clone())) + self.dump_path_ref(id, &ast::Path::from_ident(sp, i)); } - def => error!("unexpected definition kind when processing collected paths: {:?}", - def), + def => error!( + "unexpected definition kind when processing collected idents: {:?}", + def + ), } } - for &(id, ref path) in &paths_to_process { + for (id, ref path) in collector.collected_paths { self.process_path(id, path); } walk_list!(self, visit_expr, &arm.guard); @@ -1500,7 +1711,10 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc fn visit_local(&mut self, l: &'l ast::Local) { self.process_macro_use(l.span); - let value = l.init.as_ref().map(|i| self.span.snippet(i.span)).unwrap_or(String::new()); + let value = l.init + .as_ref() + .map(|i| self.span.snippet(i.span)) + .unwrap_or(String::new()); self.process_var_decl(&l.pat, value); // Just walk the initialiser and type (don't want to walk the pattern again). @@ -1509,15 +1723,19 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc } fn visit_foreign_item(&mut self, item: &'l ast::ForeignItem) { + let access = access_from!(self.save_ctxt, item); + match item.node { ast::ForeignItemKind::Fn(ref decl, ref generics) => { if let Some(fn_data) = self.save_ctxt.get_extern_item_data(item) { down_cast_data!(fn_data, DefData, item.span); - self.nest_tables(item.id, |v| v.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(generics, item.span, &fn_data.qualname, item.id); - self.dumper.dump_def(item.vis == ast::Visibility::Public, fn_data); + self.dumper.dump_def(&access, fn_data); } for arg in &decl.inputs { @@ -1531,11 +1749,17 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc ast::ForeignItemKind::Static(ref ty, _) => { if let Some(var_data) = self.save_ctxt.get_extern_item_data(item) { down_cast_data!(var_data, DefData, item.span); - self.dumper.dump_def(item.vis == ast::Visibility::Public, var_data); + self.dumper.dump_def(&access, var_data); } self.visit_ty(ty); } + ast::ForeignItemKind::Ty => { + if let Some(var_data) = self.save_ctxt.get_extern_item_data(item) { + down_cast_data!(var_data, DefData, item.span); + self.dumper.dump_def(&access, var_data); + } + } } } } diff --git a/src/librustc_save_analysis/json_dumper.rs b/src/librustc_save_analysis/json_dumper.rs index 8dd191f4375ed..2b35a4123836b 100644 --- a/src/librustc_save_analysis/json_dumper.rs +++ b/src/librustc_save_analysis/json_dumper.rs @@ -12,11 +12,17 @@ use std::io::Write; use rustc_serialize::json::as_json; -use rls_data::{self, Analysis, Import, Def, DefKind, Ref, RefKind, MacroRef, - Relation, CratePreludeData}; +use rls_data::{self, Analysis, CratePreludeData, Def, DefKind, Import, MacroRef, Ref, RefKind, + Relation}; use rls_data::config::Config; use rls_span::{Column, Row}; +#[derive(Debug)] +pub struct Access { + pub reachable: bool, + pub public: bool, +} + pub struct JsonDumper { result: Analysis, config: Config, @@ -54,15 +60,16 @@ impl<'b, W: Write> JsonDumper> { JsonDumper { output: WriteOutput { output: writer }, config: config.clone(), - result: Analysis::new(config) + result: Analysis::new(config), } } } impl<'b> JsonDumper> { - pub fn with_callback(callback: &'b mut FnMut(&Analysis), - config: Config) - -> JsonDumper> { + pub fn with_callback( + callback: &'b mut FnMut(&Analysis), + config: Config, + ) -> JsonDumper> { JsonDumper { output: CallbackOutput { callback: callback }, config: config.clone(), @@ -83,33 +90,35 @@ impl<'b, O: DumpOutput + 'b> JsonDumper { } pub fn macro_use(&mut self, data: MacroRef) { - if self.config.pub_only { + if self.config.pub_only || self.config.reachable_only { return; } self.result.macro_refs.push(data); } - pub fn import(&mut self, public: bool, import: Import) { - if !public && self.config.pub_only { + pub fn import(&mut self, access: &Access, import: Import) { + if !access.public && self.config.pub_only + || !access.reachable && self.config.reachable_only { return; } self.result.imports.push(import); } pub fn dump_ref(&mut self, data: Ref) { - if self.config.pub_only { + if self.config.pub_only || self.config.reachable_only { return; } self.result.refs.push(data); } - pub fn dump_def(&mut self, public: bool, mut data: Def) { - if !public && self.config.pub_only { + pub fn dump_def(&mut self, access: &Access, mut data: Def) { + if !access.public && self.config.pub_only + || !access.reachable && self.config.reachable_only { return; } if data.kind == DefKind::Mod && data.span.file_name.to_str().unwrap() != data.value { - // If the module is an out-of-line defintion, then we'll make the - // definition the first character in the module's file and turn the + // If the module is an out-of-line definition, then we'll make the + // definition the first character in the module's file and turn // the declaration into a reference to it. let rf = Ref { kind: RefKind::Mod, diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 1c6007966afa3..fddafed11f314 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -9,20 +9,22 @@ // except according to those terms. #![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/")] + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] - #![feature(custom_attribute)] #![allow(unused_attributes)] -#[macro_use] extern crate rustc; +#[macro_use] +extern crate rustc; -#[macro_use] extern crate log; -#[macro_use] extern crate syntax; +#[macro_use] +extern crate log; extern crate rustc_data_structures; extern crate rustc_serialize; extern crate rustc_typeck; +#[macro_use] +extern crate syntax; extern crate syntax_pos; extern crate rls_data; @@ -38,7 +40,7 @@ mod sig; use rustc::hir; use rustc::hir::def::Def as HirDef; use rustc::hir::map::{Node, NodeItem}; -use rustc::hir::def_id::{LOCAL_CRATE, DefId}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::session::config::CrateType::CrateTypeExecutable; use rustc::ty::{self, TyCtxt}; use rustc_typeck::hir_ty_to_ty; @@ -48,13 +50,13 @@ use std::env; use std::fs::File; use std::path::{Path, PathBuf}; -use syntax::ast::{self, NodeId, PatKind, Attribute}; +use syntax::ast::{self, Attribute, NodeId, PatKind}; use syntax::parse::lexer::comments::strip_doc_comment_decoration; use syntax::parse::token; use syntax::print::pprust; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; -use syntax::print::pprust::{ty_to_string, arg_to_string}; +use syntax::print::pprust::{arg_to_string, ty_to_string}; use syntax::codemap::MacroAttribute; use syntax_pos::*; @@ -62,8 +64,8 @@ use json_dumper::JsonDumper; use dump_visitor::DumpVisitor; use span_utils::SpanUtils; -use rls_data::{Ref, RefKind, SpanData, MacroRef, Def, DefKind, Relation, RelationKind, - ExternalCrateData}; +use rls_data::{Def, DefKind, ExternalCrateData, GlobalCrateId, MacroRef, Ref, RefKind, Relation, + RelationKind, SpanData}; use rls_data::config::Config; @@ -82,13 +84,9 @@ pub enum Data { RelationData(Relation), } -macro_rules! option_try( - ($e:expr) => (match $e { Some(e) => e, None => return None }) -); - impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { fn span_from_span(&self, span: Span) -> SpanData { - use rls_span::{Row, Column}; + use rls_span::{Column, Row}; let cm = self.tcx.sess.codemap(); let start = cm.lookup_char_pos(span.lo()); @@ -119,9 +117,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }; let lo_loc = self.span_utils.sess.codemap().lookup_char_pos(span.lo()); result.push(ExternalCrateData { - name: self.tcx.crate_name(n).to_string(), - num: n.as_u32(), file_name: SpanUtils::make_path_string(&lo_loc.file.name), + num: n.as_u32(), + id: GlobalCrateId { + name: self.tcx.crate_name(n).to_string(), + disambiguator: self.tcx.crate_disambiguator(n).to_fingerprint().as_value(), + }, }); } @@ -132,7 +133,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let qualname = format!("::{}", self.tcx.node_path_str(item.id)); match item.node { ast::ForeignItemKind::Fn(ref decl, ref generics) => { - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + let sub_span = self.span_utils + .sub_span_after_keyword(item.span, keywords::Fn); filter!(self.span_utils, sub_span, item.span, None); Some(Data::DefData(Def { @@ -173,6 +175,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { attributes: lower_attributes(item.attrs.clone(), self), })) } + // FIXME(plietar): needs a new DefKind in rls-data + ast::ForeignItemKind::Ty => None, } } @@ -180,7 +184,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { match item.node { ast::ItemKind::Fn(ref decl, .., ref generics, _) => { let qualname = format!("::{}", self.tcx.node_path_str(item.id)); - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + let sub_span = self.span_utils + .sub_span_after_keyword(item.span, keywords::Fn); filter!(self.span_utils, sub_span, item.span, None); Some(Data::DefData(Def { kind: DefKind::Function, @@ -228,7 +233,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } ast::ItemKind::Const(ref typ, _) => { let qualname = format!("::{}", self.tcx.node_path_str(item.id)); - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); + let sub_span = self.span_utils + .sub_span_after_keyword(item.span, keywords::Const); filter!(self.span_utils, sub_span, item.span, None); let id = id_from_node_id(item.id, self); @@ -255,7 +261,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let cm = self.tcx.sess.codemap(); let filename = cm.span_to_filename(m.inner); - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); + let sub_span = self.span_utils + .sub_span_after_keyword(item.span, keywords::Mod); filter!(self.span_utils, sub_span, item.span, None); Some(Data::DefData(Def { @@ -266,7 +273,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { span: self.span_from_span(sub_span.unwrap()), value: filename, parent: None, - children: m.items.iter().map(|i| id_from_node_id(i.id, self)).collect(), + children: m.items + .iter() + .map(|i| id_from_node_id(i.id, self)) + .collect(), decl_id: None, docs: self.docs_for_attrs(&item.attrs), sig: sig::item_signature(item, self), @@ -276,12 +286,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { ast::ItemKind::Enum(ref def, _) => { let name = item.ident.to_string(); let qualname = format!("::{}", self.tcx.node_path_str(item.id)); - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum); + let sub_span = self.span_utils + .sub_span_after_keyword(item.span, keywords::Enum); filter!(self.span_utils, sub_span, item.span, None); - let variants_str = def.variants.iter() - .map(|v| v.node.name.to_string()) - .collect::>() - .join(", "); + let variants_str = def.variants + .iter() + .map(|v| v.node.name.to_string()) + .collect::>() + .join(", "); let value = format!("{}::{{{}}}", name, variants_str); Some(Data::DefData(Def { kind: DefKind::Enum, @@ -292,9 +304,9 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { value, parent: None, children: def.variants - .iter() - .map(|v| id_from_node_id(v.node.data.id(), self)) - .collect(), + .iter() + .map(|v| id_from_node_id(v.node.data.id(), self)) + .collect(), decl_id: None, docs: self.docs_for_attrs(&item.attrs), sig: sig::item_signature(item, self), @@ -311,15 +323,18 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { filter!(self.span_utils, sub_span, typ.span, None); let type_data = self.lookup_ref_id(typ.id); - type_data.map(|type_data| Data::RelationData(Relation { - kind: RelationKind::Impl, - span: self.span_from_span(sub_span.unwrap()), - from: id_from_def_id(type_data), - to: trait_ref.as_ref() - .and_then(|t| self.lookup_ref_id(t.ref_id)) - .map(id_from_def_id) - .unwrap_or(null_id()), - })) + type_data.map(|type_data| { + Data::RelationData(Relation { + kind: RelationKind::Impl, + span: self.span_from_span(sub_span.unwrap()), + from: id_from_def_id(type_data), + to: trait_ref + .as_ref() + .and_then(|t| self.lookup_ref_id(t.ref_id)) + .map(id_from_def_id) + .unwrap_or(null_id()), + }) + }) } else { None } @@ -331,14 +346,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } - pub fn get_field_data(&self, - field: &ast::StructField, - scope: NodeId) - -> Option { + pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option { if let Some(ident) = field.ident { let name = ident.to_string(); let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident); - let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon); + let sub_span = self.span_utils + .sub_span_before_token(field.span, token::Colon); filter!(self.span_utils, sub_span, field.span, None); let def_id = self.tcx.hir.local_def_id(field.id); let typ = self.tcx.type_of(def_id).to_string(); @@ -368,18 +381,13 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { // FIXME would be nice to take a MethodItem here, but the ast provides both // trait and impl flavours, so the caller must do the disassembly. - pub fn get_method_data(&self, - id: ast::NodeId, - name: ast::Name, - span: Span) - -> Option { + pub fn get_method_data(&self, id: ast::NodeId, name: ast::Name, span: Span) -> Option { // The qualname for a method is the trait name or name of the struct in an impl in // which the method is declared in, followed by the method's name. let (qualname, parent_scope, decl_id, docs, attributes) = - match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) { - Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) { - Some(Node::NodeItem(item)) => { - match item.node { + match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) { + Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) { + Some(Node::NodeItem(item)) => match item.node { hir::ItemImpl(.., ref ty, _) => { let mut result = String::from("<"); result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id)); @@ -389,7 +397,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { if let Some(def_id) = trait_id { result.push_str(" as "); result.push_str(&self.tcx.item_path_str(def_id)); - self.tcx.associated_items(def_id) + self.tcx + .associated_items(def_id) .find(|item| item.name == name) .map(|item| decl_id = Some(item.def_id)); } else { @@ -401,53 +410,61 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } result.push_str(">"); - (result, trait_id, decl_id, - self.docs_for_attrs(&item.attrs), - item.attrs.to_vec()) + ( + result, + trait_id, + decl_id, + self.docs_for_attrs(&item.attrs), + item.attrs.to_vec(), + ) } _ => { - span_bug!(span, - "Container {:?} for method {} not an impl?", - impl_id, - id); + span_bug!( + span, + "Container {:?} for method {} not an impl?", + impl_id, + id + ); } + }, + r => { + span_bug!( + span, + "Container {:?} for method {} is not a node item {:?}", + impl_id, + id, + r + ); } - } - r => { - span_bug!(span, - "Container {:?} for method {} is not a node item {:?}", - impl_id, - id, - r); - } - }, - None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) { - Some(def_id) => { - match self.tcx.hir.get_if_local(def_id) { - Some(Node::NodeItem(item)) => { - (format!("::{}", self.tcx.item_path_str(def_id)), - Some(def_id), None, - self.docs_for_attrs(&item.attrs), - item.attrs.to_vec()) - } + }, + None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) { + Some(def_id) => match self.tcx.hir.get_if_local(def_id) { + Some(Node::NodeItem(item)) => ( + format!("::{}", self.tcx.item_path_str(def_id)), + Some(def_id), + None, + self.docs_for_attrs(&item.attrs), + item.attrs.to_vec(), + ), r => { - span_bug!(span, - "Could not find container {:?} for \ - method {}, got {:?}", - def_id, - id, - r); + span_bug!( + span, + "Could not find container {:?} for \ + method {}, got {:?}", + def_id, + id, + r + ); } + }, + None => { + debug!("Could not find container for method {} at {:?}", id, span); + // This is not necessarily a bug, if there was a compilation error, + // the tables we need might not exist. + return None; } - } - None => { - debug!("Could not find container for method {} at {:?}", id, span); - // This is not necessarily a bug, if there was a compilation error, the tables - // we need might not exist. - return None; - } - }, - }; + }, + }; let qualname = format!("{}::{}", qualname, name); @@ -471,9 +488,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }) } - pub fn get_trait_ref_data(&self, - trait_ref: &ast::TraitRef) - -> Option { + pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option { self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| { let span = trait_ref.path.span; if generated_code(span) { @@ -501,8 +516,11 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let hir_node = match self.tcx.hir.find(sub_ex.id) { Some(Node::NodeExpr(expr)) => expr, _ => { - debug!("Missing or weird node for sub-expression {} in {:?}", - sub_ex.id, expr); + debug!( + "Missing or weird node for sub-expression {} in {:?}", + sub_ex.id, + expr + ); return None; } }; @@ -544,20 +562,29 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } } - ast::ExprKind::MethodCall(..) => { + ast::ExprKind::MethodCall(ref seg, ..) => { let expr_hir_id = self.tcx.hir.definitions().node_to_hir_id(expr.id); - let method_id = self.tables.type_dependent_defs()[expr_hir_id].def_id(); + let method_id = match self.tables.type_dependent_defs().get(expr_hir_id) { + Some(id) => id.def_id(), + None => { + debug!("Could not resolve method id for {:?}", expr); + return None; + } + }; 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)), }; - let sub_span = self.span_utils.sub_span_for_meth_name(expr.span); - filter!(self.span_utils, sub_span, expr.span, None); - let span = self.span_from_span(sub_span.unwrap()); + let sub_span = seg.span; + filter!(self.span_utils, Some(sub_span), expr.span, None); + let span = self.span_from_span(sub_span); Some(Data::RefData(Ref { kind: RefKind::Function, span, - ref_id: def_id.or(decl_id).map(|id| id_from_def_id(id)).unwrap_or(null_id()), + ref_id: def_id + .or(decl_id) + .map(|id| id_from_def_id(id)) + .unwrap_or(null_id()), })) } ast::ExprKind::Path(_, ref path) => { @@ -574,51 +601,89 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { match self.tcx.hir.get(id) { Node::NodeTraitRef(tr) => tr.path.def, - Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) | + Node::NodeItem(&hir::Item { + node: hir::ItemUse(ref path, _), + .. + }) | Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def, - Node::NodeExpr(&hir::Expr { node: hir::ExprPath(ref qpath), .. }) | - Node::NodeExpr(&hir::Expr { node: hir::ExprStruct(ref qpath, ..), .. }) | - 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, ..), .. }) => { + Node::NodeExpr(&hir::Expr { + node: hir::ExprStruct(ref qpath, ..), + .. + }) | + Node::NodeExpr(&hir::Expr { + node: hir::ExprPath(ref qpath), + .. + }) | + 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, ..), + .. + }) => { let hir_id = self.tcx.hir.node_to_hir_id(id); self.tables.qpath_def(qpath, hir_id) } Node::NodeBinding(&hir::Pat { - node: hir::PatKind::Binding(_, canonical_id, ..), .. + node: hir::PatKind::Binding(_, canonical_id, ..), + .. }) => HirDef::Local(canonical_id), - Node::NodeTy(ty) => { - if let hir::Ty { node: hir::TyPath(ref qpath), .. } = *ty { - match *qpath { - hir::QPath::Resolved(_, ref path) => path.def, - hir::QPath::TypeRelative(..) => { - let ty = hir_ty_to_ty(self.tcx, ty); - if let ty::TyProjection(proj) = ty.sty { - return HirDef::AssociatedTy(proj.item_def_id); - } - HirDef::Err + Node::NodeTy(ty) => if let hir::Ty { + node: hir::TyPath(ref qpath), + .. + } = *ty + { + match *qpath { + hir::QPath::Resolved(_, ref path) => path.def, + hir::QPath::TypeRelative(..) => { + let ty = hir_ty_to_ty(self.tcx, ty); + if let ty::TyProjection(proj) = ty.sty { + return HirDef::AssociatedTy(proj.item_def_id); } + HirDef::Err } - } else { - HirDef::Err } - } + } else { + HirDef::Err + }, - _ => HirDef::Err + _ => HirDef::Err, } } pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option { + // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`. + fn fn_type(path: &ast::Path) -> bool { + if path.segments.len() != 1 { + return false; + } + if let Some(ref params) = path.segments[0].parameters { + if let ast::PathParameters::Parenthesized(_) = **params { + return true; + } + } + false + } + + if path.segments.is_empty() { + return None; + } + let def = self.get_path_def(id); - let sub_span = self.span_utils.span_for_last_ident(path.span); - filter!(self.span_utils, sub_span, path.span, None); + let last_seg = &path.segments[path.segments.len() - 1]; + let sub_span = last_seg.span; + filter!(self.span_utils, Some(sub_span), path.span, None); match def { - HirDef::Upvar(id, ..) | - HirDef::Local(id) => { - let span = self.span_from_span(sub_span.unwrap()); + HirDef::Upvar(id, ..) | HirDef::Local(id) => { + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Variable, span, @@ -628,42 +693,65 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { HirDef::Static(..) | HirDef::Const(..) | HirDef::AssociatedConst(..) | - HirDef::StructCtor(..) | HirDef::VariantCtor(..) => { - let span = self.span_from_span(sub_span.unwrap()); + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def.def_id()), }) } + HirDef::Trait(def_id) if fn_type(path) => { + // Function type bounds are desugared in the parser, so we have to + // special case them here. + let fn_span = self.span_utils.span_for_first_ident(path.span); + fn_span.map(|span| { + Ref { + kind: RefKind::Type, + span: self.span_from_span(span), + ref_id: id_from_def_id(def_id), + } + }) + } HirDef::Struct(def_id) | HirDef::Variant(def_id, ..) | HirDef::Union(def_id) | HirDef::Enum(def_id) | HirDef::TyAlias(def_id) | + HirDef::TyForeign(def_id) | HirDef::AssociatedTy(def_id) | HirDef::Trait(def_id) | HirDef::TyParam(def_id) => { - let span = self.span_from_span(sub_span.unwrap()); + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id), }) } + HirDef::StructCtor(def_id, _) => { + // This is a reference to a tuple struct where the def_id points + // to an invisible constructor function. That is not a very useful + // def, so adjust to point to the tuple struct itself. + let span = self.span_from_span(sub_span); + let parent_def_id = self.tcx.parent_def_id(def_id).unwrap(); + Some(Ref { + kind: RefKind::Type, + span, + ref_id: id_from_def_id(parent_def_id), + }) + } HirDef::Method(decl_id) => { - let sub_span = self.span_utils.sub_span_for_meth_name(path.span); - filter!(self.span_utils, sub_span, path.span, None); let def_id = if decl_id.is_local() { let ti = self.tcx.associated_item(decl_id); - self.tcx.associated_items(ti.container.id()) + self.tcx + .associated_items(ti.container.id()) .find(|item| item.name == ti.name && item.defaultness.has_value()) .map(|item| item.def_id) } else { None }; - let span = self.span_from_span(sub_span.unwrap()); + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Function, span, @@ -671,7 +759,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }) } HirDef::Fn(def_id) => { - let span = self.span_from_span(sub_span.unwrap()); + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Function, span, @@ -679,7 +767,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }) } HirDef::Mod(def_id) => { - let span = self.span_from_span(sub_span.unwrap()); + let span = self.span_from_span(sub_span); Some(Ref { kind: RefKind::Mod, span, @@ -695,10 +783,11 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } - pub fn get_field_ref_data(&self, - field_ref: &ast::Field, - variant: &ty::VariantDef) - -> Option { + pub fn get_field_ref_data( + &self, + field_ref: &ast::Field, + variant: &ty::VariantDef, + ) -> Option { let f = variant.field_named(field_ref.ident.node.name); // We don't really need a sub-span here, but no harm done let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span); @@ -725,8 +814,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { // macro uses. let callsite = span.source_callsite(); let callsite_span = self.span_from_span(callsite); - let callee = option_try!(span.source_callee()); - let callee_span = option_try!(callee.span); + let callee = span.source_callee()?; + let callee_span = callee.span?; // Ignore attribute macros, their spans are usually mangled if let MacroAttribute(_) = callee.format { @@ -736,7 +825,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { // If the callee is an imported macro from an external crate, need to get // the source span and name from the session, as their spans are localized // when read in, and no longer correspond to the source. - if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) { + if let Some(mac) = self.tcx + .sess + .imported_macro_spans + .borrow() + .get(&callee_span) + { let &(ref mac_name, mac_span) = mac; let mac_span = self.span_from_span(mac_span); return Some(MacroRef { @@ -791,21 +885,29 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { let mut sig = "fn ".to_owned(); if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { sig.push('<'); - sig.push_str(&generics.lifetimes.iter() - .map(|l| l.lifetime.ident.name.to_string()) - .collect::>() - .join(", ")); + sig.push_str(&generics + .lifetimes + .iter() + .map(|l| l.lifetime.ident.name.to_string()) + .collect::>() + .join(", ")); if !generics.lifetimes.is_empty() { sig.push_str(", "); } - sig.push_str(&generics.ty_params.iter() - .map(|l| l.ident.to_string()) - .collect::>() - .join(", ")); + sig.push_str(&generics + .ty_params + .iter() + .map(|l| l.ident.to_string()) + .collect::>() + .join(", ")); sig.push_str("> "); } sig.push('('); - sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::>().join(", ")); + sig.push_str(&decl.inputs + .iter() + .map(arg_to_string) + .collect::>() + .join(", ")); sig.push(')'); match decl.output { ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"), @@ -815,35 +917,38 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { sig } -// An AST visitor for collecting paths from patterns. -struct PathCollector { - // The Row field identifies the kind of pattern. - collected_paths: Vec<(NodeId, ast::Path, ast::Mutability)>, +// An AST visitor for collecting paths (e.g., the names of structs) and formal +// variables (idents) from patterns. +struct PathCollector<'l> { + collected_paths: Vec<(NodeId, &'l ast::Path)>, + collected_idents: Vec<(NodeId, ast::Ident, Span, ast::Mutability)>, } -impl PathCollector { - fn new() -> PathCollector { - PathCollector { collected_paths: vec![] } +impl<'l> PathCollector<'l> { + fn new() -> PathCollector<'l> { + PathCollector { + collected_paths: vec![], + collected_idents: vec![], + } } } -impl<'a> Visitor<'a> for PathCollector { - fn visit_pat(&mut self, p: &ast::Pat) { +impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> { + fn visit_pat(&mut self, p: &'a ast::Pat) { match p.node { PatKind::Struct(ref path, ..) => { - self.collected_paths.push((p.id, path.clone(), - ast::Mutability::Mutable)); + self.collected_paths.push((p.id, path)); } - PatKind::TupleStruct(ref path, ..) | - PatKind::Path(_, ref path) => { - self.collected_paths.push((p.id, path.clone(), - ast::Mutability::Mutable)); + PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => { + self.collected_paths.push((p.id, path)); } PatKind::Ident(bm, ref path1, _) => { - debug!("PathCollector, visit ident in pat {}: {:?} {:?}", - path1.node, - p.span, - path1.span); + debug!( + "PathCollector, visit ident in pat {}: {:?} {:?}", + path1.node, + p.span, + path1.span + ); let immut = match bm { // Even if the ref is mut, you can't change the ref, only // the data pointed at, so showing the initialising expression @@ -851,9 +956,8 @@ impl<'a> Visitor<'a> for PathCollector { ast::BindingMode::ByRef(_) => ast::Mutability::Immutable, ast::BindingMode::ByValue(mt) => mt, }; - // collect path for either visit_local or visit_arm - let path = ast::Path::from_ident(path1.span, path1.node); - self.collected_paths.push((p.id, path, immut)); + self.collected_idents + .push((p.id, path1.node, path1.span, immut)); } _ => {} } @@ -863,23 +967,25 @@ impl<'a> Visitor<'a> for PathCollector { /// Defines what to do with the results of saving the analysis. pub trait SaveHandler { - fn save<'l, 'tcx>(&mut self, - save_ctxt: SaveContext<'l, 'tcx>, - krate: &ast::Crate, - cratename: &str); + fn save<'l, 'tcx>( + &mut self, + save_ctxt: SaveContext<'l, 'tcx>, + krate: &ast::Crate, + cratename: &str, + ); } /// Dump the save-analysis results to a file. pub struct DumpHandler<'a> { odir: Option<&'a Path>, - cratename: String + cratename: String, } impl<'a> DumpHandler<'a> { pub fn new(odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> { DumpHandler { odir, - cratename: cratename.to_owned() + cratename: cratename.to_owned(), } } @@ -897,8 +1003,10 @@ impl<'a> DumpHandler<'a> { error!("Could not create directory {}: {}", root_path.display(), e); } - let executable = - sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable); + let executable = sess.crate_types + .borrow() + .iter() + .any(|ct| *ct == CrateTypeExecutable); let mut out_name = if executable { "".to_owned() } else { @@ -915,19 +1023,21 @@ impl<'a> DumpHandler<'a> { info!("Writing output to {}", file_name.display()); - let output_file = File::create(&file_name).unwrap_or_else(|e| { - sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)) - }); + let output_file = File::create(&file_name).unwrap_or_else( + |e| sess.fatal(&format!("Could not open {}: {}", file_name.display(), e)), + ); output_file } } impl<'a> SaveHandler for DumpHandler<'a> { - fn save<'l, 'tcx>(&mut self, - save_ctxt: SaveContext<'l, 'tcx>, - krate: &ast::Crate, - cratename: &str) { + fn save<'l, 'tcx>( + &mut self, + save_ctxt: SaveContext<'l, 'tcx>, + krate: &ast::Crate, + cratename: &str, + ) { let output = &mut self.output_file(&save_ctxt); let mut dumper = JsonDumper::new(output, save_ctxt.config.clone()); let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); @@ -943,10 +1053,12 @@ pub struct CallbackHandler<'b> { } impl<'b> SaveHandler for CallbackHandler<'b> { - fn save<'l, 'tcx>(&mut self, - save_ctxt: SaveContext<'l, 'tcx>, - krate: &ast::Crate, - cratename: &str) { + fn save<'l, 'tcx>( + &mut self, + save_ctxt: SaveContext<'l, 'tcx>, + krate: &ast::Crate, + cratename: &str, + ) { // We're using the JsonDumper here because it has the format of the // save-analysis results that we will pass to the callback. IOW, we are // using the JsonDumper to collect the save-analysis results, but not @@ -960,12 +1072,14 @@ impl<'b> SaveHandler for CallbackHandler<'b> { } } -pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>, - krate: &ast::Crate, - analysis: &'l ty::CrateAnalysis, - cratename: &str, - config: Option, - mut handler: H) { +pub fn process_crate<'l, 'tcx, H: SaveHandler>( + tcx: TyCtxt<'l, 'tcx, 'tcx>, + krate: &ast::Crate, + analysis: &'l ty::CrateAnalysis, + cratename: &str, + config: Option, + mut handler: H, +) { let _ignore = tcx.dep_graph.in_ignore(); assert!(analysis.glob_map.is_some()); @@ -988,10 +1102,8 @@ fn find_config(supplied: Option) -> Config { return config; } match env::var_os("RUST_SAVE_ANALYSIS_CONFIG") { - Some(config_string) => { - rustc_serialize::json::decode(config_string.to_str().unwrap()) - .expect("Could not deserialize save-analysis config") - }, + Some(config_string) => rustc_serialize::json::decode(config_string.to_str().unwrap()) + .expect("Could not deserialize save-analysis config"), None => Config::default(), } } diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index c7e00245d6350..b244876226c48 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -35,9 +35,9 @@ // // FIXME where clauses need implementing, defs/refs in generics are mostly missing. -use {SaveContext, id_from_def_id, id_from_node_id}; +use {id_from_def_id, id_from_node_id, SaveContext}; -use rls_data::{Signature, SigElement}; +use rls_data::{SigElement, Signature}; use rustc::hir::def::Def; use syntax::ast::{self, NodeId}; @@ -75,35 +75,39 @@ pub fn variant_signature(variant: &ast::Variant, scx: &SaveContext) -> Option Option { +pub fn method_signature( + id: NodeId, + ident: ast::Ident, + generics: &ast::Generics, + m: &ast::MethodSig, + scx: &SaveContext, +) -> Option { if !scx.config.signatures { return None; } - make_method_signature(id, ident, m, scx).ok() + make_method_signature(id, ident, generics, m, scx).ok() } -pub fn assoc_const_signature(id: NodeId, - ident: ast::Name, - ty: &ast::Ty, - default: Option<&ast::Expr>, - scx: &SaveContext) - -> Option { +pub fn assoc_const_signature( + id: NodeId, + ident: ast::Name, + ty: &ast::Ty, + default: Option<&ast::Expr>, + scx: &SaveContext, +) -> Option { if !scx.config.signatures { return None; } make_assoc_const_signature(id, ident, ty, default, scx).ok() } -pub fn assoc_type_signature(id: NodeId, - ident: ast::Ident, - bounds: Option<&ast::TyParamBounds>, - default: Option<&ast::Ty>, - scx: &SaveContext) - -> Option { +pub fn assoc_type_signature( + id: NodeId, + ident: ast::Ident, + bounds: Option<&ast::TyParamBounds>, + default: Option<&ast::Ty>, + scx: &SaveContext, +) -> Option { if !scx.config.signatures { return None; } @@ -116,11 +120,12 @@ trait Sig { fn make(&self, offset: usize, id: Option, scx: &SaveContext) -> Result; } -fn extend_sig(mut sig: Signature, - text: String, - defs: Vec, - refs: Vec) - -> Signature { +fn extend_sig( + mut sig: Signature, + text: String, + defs: Vec, + refs: Vec, +) -> Signature { sig.text = text; sig.defs.extend(defs.into_iter()); sig.refs.extend(refs.into_iter()); @@ -141,8 +146,12 @@ fn merge_sigs(text: String, sigs: Vec) -> Signature { let (defs, refs): (Vec<_>, Vec<_>) = sigs.into_iter().map(|s| (s.defs, s.refs)).unzip(); - result.defs.extend(defs.into_iter().flat_map(|ds| ds.into_iter())); - result.refs.extend(refs.into_iter().flat_map(|rs| rs.into_iter())); + result + .defs + .extend(defs.into_iter().flat_map(|ds| ds.into_iter())); + result + .refs + .extend(refs.into_iter().flat_map(|rs| rs.into_iter())); result } @@ -187,9 +196,7 @@ impl Sig for ast::Ty { let text = format!("{}{}", prefix, nested.text); Ok(replace_text(nested, text)) } - ast::TyKind::Never => { - Ok(text_sig("!".to_owned())) - }, + ast::TyKind::Never => Ok(text_sig("!".to_owned())), ast::TyKind::Tup(ref ts) => { let mut text = "(".to_owned(); let mut defs = vec![]; @@ -214,8 +221,11 @@ impl Sig for ast::Ty { if !f.lifetimes.is_empty() { // FIXME defs, bounds on lifetimes text.push_str("for<"); - text.push_str(&f.lifetimes.iter().map(|l| - l.lifetime.ident.to_string()).collect::>().join(", ")); + text.push_str(&f.lifetimes + .iter() + .map(|l| l.lifetime.ident.to_string()) + .collect::>() + .join(", ")); text.push('>'); } @@ -250,9 +260,7 @@ impl Sig for ast::Ty { Ok(Signature { text, defs, refs }) } - ast::TyKind::Path(None, ref path) => { - path.make(offset, id, scx) - } + ast::TyKind::Path(None, ref path) => path.make(offset, id, scx), ast::TyKind::Path(Some(ref qself), ref path) => { let nested_ty = qself.ty.make(offset + 1, id, scx)?; let prefix = if qself.position == 0 { @@ -288,7 +296,7 @@ impl Sig for ast::Ty { }) } } - ast::TyKind::TraitObject(ref bounds) => { + ast::TyKind::TraitObject(ref bounds, ..) => { // FIXME recurse into bounds let nested = pprust::bounds_to_string(bounds); Ok(text_sig(nested)) @@ -324,11 +332,13 @@ impl Sig for ast::Item { text.push_str("mut "); } let name = self.ident.to_string(); - let defs = vec![SigElement { - id: id_from_node_id(self.id, scx), - start: offset + text.len(), - end: offset + text.len() + name.len(), - }]; + let defs = vec![ + SigElement { + id: id_from_node_id(self.id, scx), + start: offset + text.len(), + end: offset + text.len() + name.len(), + }, + ]; text.push_str(&name); text.push_str(": "); @@ -345,11 +355,13 @@ impl Sig for ast::Item { ast::ItemKind::Const(ref ty, ref expr) => { let mut text = "const ".to_owned(); let name = self.ident.to_string(); - let defs = vec![SigElement { - id: id_from_node_id(self.id, scx), - start: offset + text.len(), - end: offset + text.len() + name.len(), - }]; + let defs = vec![ + SigElement { + id: id_from_node_id(self.id, scx), + start: offset + text.len(), + end: offset + text.len() + name.len(), + }, + ]; text.push_str(&name); text.push_str(": "); @@ -378,12 +390,7 @@ impl Sig for ast::Item { } text.push_str("fn "); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push('('); for i in &decl.inputs { @@ -412,11 +419,13 @@ impl Sig for ast::Item { ast::ItemKind::Mod(ref _mod) => { let mut text = "mod ".to_owned(); let name = self.ident.to_string(); - let defs = vec![SigElement { - id: id_from_node_id(self.id, scx), - start: offset + text.len(), - end: offset + text.len() + name.len(), - }]; + let defs = vec![ + SigElement { + id: id_from_node_id(self.id, scx), + start: offset + text.len(), + end: offset + text.len() + name.len(), + }, + ]; text.push_str(&name); // Could be either `mod foo;` or `mod foo { ... }`, but we'll just puck one. text.push(';'); @@ -429,12 +438,7 @@ impl Sig for ast::Item { } ast::ItemKind::Ty(ref ty, ref generics) => { let text = "type ".to_owned(); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push_str(" = "); let ty = ty.make(offset + sig.text.len(), id, scx)?; @@ -445,49 +449,34 @@ impl Sig for ast::Item { } ast::ItemKind::Enum(_, ref generics) => { let text = "enum ".to_owned(); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push_str(" {}"); Ok(sig) } ast::ItemKind::Struct(_, ref generics) => { let text = "struct ".to_owned(); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push_str(" {}"); Ok(sig) } ast::ItemKind::Union(_, ref generics) => { let text = "union ".to_owned(); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push_str(" {}"); Ok(sig) } - ast::ItemKind::Trait(unsafety, ref generics, ref bounds, _) => { + ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, _) => { let mut text = String::new(); + + if is_auto == ast::IsAuto::Yes { + text.push_str("auto "); + } + if unsafety == ast::Unsafety::Unsafe { text.push_str("unsafe "); } text.push_str("trait "); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; if !bounds.is_empty() { sig.text.push_str(": "); @@ -498,7 +487,7 @@ impl Sig for ast::Item { Ok(sig) } - ast::ItemKind::DefaultImpl(unsafety, ref trait_ref) => { + ast::ItemKind::AutoImpl(unsafety, ref trait_ref) => { let mut text = String::new(); if unsafety == ast::Unsafety::Unsafe { text.push_str("unsafe "); @@ -509,13 +498,15 @@ impl Sig for ast::Item { text.push_str(" for .. {}"); Ok(replace_text(trait_sig, text)) } - ast::ItemKind::Impl(unsafety, - polarity, - defaultness, - ref generics, - ref opt_trait, - ref ty, - _) => { + ast::ItemKind::Impl( + unsafety, + polarity, + defaultness, + ref generics, + ref opt_trait, + ref ty, + _, + ) => { let mut text = String::new(); if let ast::Defaultness::Default = defaultness { text.push_str("default "); @@ -556,8 +547,7 @@ impl Sig for ast::Item { ast::ItemKind::ExternCrate(_) => Err("extern crate"), // FIXME should implement this (e.g., pub use). ast::ItemKind::Use(_) => Err("import"), - ast::ItemKind::Mac(..) | - ast::ItemKind::MacroDef(_) => Err("Macro"), + ast::ItemKind::Mac(..) | ast::ItemKind::MacroDef(_) => Err("Macro"), } } } @@ -567,19 +557,14 @@ impl Sig for ast::Path { let def = scx.get_path_def(id.ok_or("Missing id for Path")?); let (name, start, end) = match def { - Def::Label(..) | - Def::PrimTy(..) | - Def::SelfTy(..) | - Def::Err => { + Def::Label(..) | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => { return Ok(Signature { text: pprust::path_to_string(self), defs: vec![], refs: vec![], }) } - Def::AssociatedConst(..) | - Def::Variant(..) | - Def::VariantCtor(..) => { + Def::AssociatedConst(..) | Def::Variant(..) | Def::VariantCtor(..) => { let len = self.segments.len(); if len < 2 { return Err("Bad path"); @@ -629,9 +614,11 @@ impl Sig for ast::Generics { if !l.bounds.is_empty() { l_text.push_str(": "); - let bounds = l.bounds.iter().map(|l| { - l.ident.to_string() - }).collect::>().join(" + "); + let bounds = l.bounds + .iter() + .map(|l| l.ident.to_string()) + .collect::>() + .join(" + "); l_text.push_str(&bounds); // FIXME add lifetime bounds refs. } @@ -656,7 +643,11 @@ impl Sig for ast::Generics { } text.push('>'); - Ok(Signature {text, defs, refs: vec![] }) + Ok(Signature { + text, + defs, + refs: vec![], + }) } } @@ -704,11 +695,7 @@ impl Sig for ast::Variant_ { refs.extend(field_sig.refs.into_iter()); } text.push('}'); - Ok(Signature { - text, - defs, - refs, - }) + Ok(Signature { text, defs, refs }) } ast::VariantData::Tuple(ref fields, id) => { let name_def = SigElement { @@ -727,11 +714,7 @@ impl Sig for ast::Variant_ { refs.extend(field_sig.refs.into_iter()); } text.push(')'); - Ok(Signature { - text, - defs, - refs, - }) + Ok(Signature { text, defs, refs }) } ast::VariantData::Unit(id) => { let name_def = SigElement { @@ -757,12 +740,7 @@ impl Sig for ast::ForeignItem { let mut text = String::new(); text.push_str("fn "); - let mut sig = name_and_generics(text, - offset, - generics, - self.id, - self.ident, - scx)?; + let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push('('); for i in &decl.inputs { @@ -794,11 +772,13 @@ impl Sig for ast::ForeignItem { text.push_str("mut "); } let name = self.ident.to_string(); - let defs = vec![SigElement { - id: id_from_node_id(self.id, scx), - start: offset + text.len(), - end: offset + text.len() + name.len(), - }]; + let defs = vec![ + SigElement { + id: id_from_node_id(self.id, scx), + start: offset + text.len(), + end: offset + text.len() + name.len(), + }, + ]; text.push_str(&name); text.push_str(": "); @@ -807,17 +787,37 @@ impl Sig for ast::ForeignItem { Ok(extend_sig(ty_sig, text, defs, vec![])) } + ast::ForeignItemKind::Ty => { + let mut text = "type ".to_owned(); + let name = self.ident.to_string(); + let defs = vec![ + SigElement { + id: id_from_node_id(self.id, scx), + start: offset + text.len(), + end: offset + text.len() + name.len(), + }, + ]; + text.push_str(&name); + text.push(';'); + + Ok(Signature { + text: text, + defs: defs, + refs: vec![], + }) + } } } } -fn name_and_generics(mut text: String, - offset: usize, - generics: &ast::Generics, - id: NodeId, - name: ast::Ident, - scx: &SaveContext) - -> Result { +fn name_and_generics( + mut text: String, + offset: usize, + generics: &ast::Generics, + id: NodeId, + name: ast::Ident, + scx: &SaveContext, +) -> Result { let name = name.to_string(); let def = SigElement { id: id_from_node_id(id, scx), @@ -832,19 +832,22 @@ fn name_and_generics(mut text: String, } -fn make_assoc_type_signature(id: NodeId, - ident: ast::Ident, - bounds: Option<&ast::TyParamBounds>, - default: Option<&ast::Ty>, - scx: &SaveContext) - -> Result { +fn make_assoc_type_signature( + id: NodeId, + ident: ast::Ident, + bounds: Option<&ast::TyParamBounds>, + default: Option<&ast::Ty>, + scx: &SaveContext, +) -> Result { let mut text = "type ".to_owned(); let name = ident.to_string(); - let mut defs = vec![SigElement { - id: id_from_node_id(id, scx), - start: text.len(), - end: text.len() + name.len(), - }]; + let mut defs = vec![ + SigElement { + id: id_from_node_id(id, scx), + start: text.len(), + end: text.len() + name.len(), + }, + ]; let mut refs = vec![]; text.push_str(&name); if let Some(bounds) = bounds { @@ -863,19 +866,22 @@ fn make_assoc_type_signature(id: NodeId, Ok(Signature { text, defs, refs }) } -fn make_assoc_const_signature(id: NodeId, - ident: ast::Name, - ty: &ast::Ty, - default: Option<&ast::Expr>, - scx: &SaveContext) - -> Result { +fn make_assoc_const_signature( + id: NodeId, + ident: ast::Name, + ty: &ast::Ty, + default: Option<&ast::Expr>, + scx: &SaveContext, +) -> Result { let mut text = "const ".to_owned(); let name = ident.to_string(); - let mut defs = vec![SigElement { - id: id_from_node_id(id, scx), - start: text.len(), - end: text.len() + name.len(), - }]; + let mut defs = vec![ + SigElement { + id: id_from_node_id(id, scx), + start: text.len(), + end: text.len() + name.len(), + }, + ]; let mut refs = vec![]; text.push_str(&name); text.push_str(": "); @@ -893,11 +899,13 @@ fn make_assoc_const_signature(id: NodeId, Ok(Signature { text, defs, refs }) } -fn make_method_signature(id: NodeId, - ident: ast::Ident, - m: &ast::MethodSig, - scx: &SaveContext) - -> Result { +fn make_method_signature( + id: NodeId, + ident: ast::Ident, + generics: &ast::Generics, + m: &ast::MethodSig, + scx: &SaveContext, +) -> Result { // FIXME code dup with function signature let mut text = String::new(); if m.constness.node == ast::Constness::Const { @@ -913,12 +921,7 @@ fn make_method_signature(id: NodeId, } text.push_str("fn "); - let mut sig = name_and_generics(text, - 0, - &m.generics, - id, - ident, - scx)?; + let mut sig = name_and_generics(text, 0, generics, id, ident, scx)?; sig.text.push('('); for i in &m.decl.inputs { diff --git a/src/librustc_save_analysis/span_utils.rs b/src/librustc_save_analysis/span_utils.rs index b9d82b8e2512a..ff1a8541e06a6 100644 --- a/src/librustc_save_analysis/span_utils.rs +++ b/src/librustc_save_analysis/span_utils.rs @@ -42,7 +42,11 @@ impl<'a> SpanUtils<'a> { if path.is_absolute() { path.clone().display().to_string() } else { - env::current_dir().unwrap().join(&path).display().to_string() + env::current_dir() + .unwrap() + .join(&path) + .display() + .to_string() } } @@ -66,7 +70,7 @@ impl<'a> SpanUtils<'a> { loop { let ts = toks.real_token(); if ts.tok == token::Eof { - return result + return result; } if bracket_count == 0 && (ts.tok.is_ident() || ts.tok.is_keyword(keywords::SelfValue)) { result = Some(ts.sp); @@ -103,47 +107,6 @@ impl<'a> SpanUtils<'a> { } } - // Return the span for the last ident before a `(` or `<` or '::<' and outside any - // any brackets, or the last span. - pub fn sub_span_for_meth_name(&self, span: Span) -> Option { - let mut toks = self.retokenise_span(span); - let mut prev = toks.real_token(); - let mut result = None; - let mut bracket_count = 0; - let mut prev_span = None; - while prev.tok != token::Eof { - prev_span = None; - let mut next = toks.real_token(); - - if (next.tok == token::OpenDelim(token::Paren) || next.tok == token::Lt) && - bracket_count == 0 && prev.tok.is_ident() { - result = Some(prev.sp); - } - - if bracket_count == 0 && next.tok == token::ModSep { - let old = prev; - prev = next; - next = toks.real_token(); - if next.tok == token::Lt && old.tok.is_ident() { - result = Some(old.sp); - } - } - - bracket_count += match prev.tok { - token::OpenDelim(token::Paren) | token::Lt => 1, - token::CloseDelim(token::Paren) | token::Gt => -1, - token::BinOp(token::Shr) => -2, - _ => 0, - }; - - if prev.tok.is_ident() && bracket_count == 0 { - prev_span = Some(prev.sp); - } - prev = next; - } - result.or(prev_span) - } - // Return the span for the last ident before a `<` and outside any // angle brackets, or the last span. pub fn sub_span_for_type_name(&self, span: Span) -> Option { @@ -163,10 +126,9 @@ impl<'a> SpanUtils<'a> { loop { let next = toks.real_token(); - if (next.tok == token::Lt || next.tok == token::Colon) && - angle_count == 0 && - bracket_count == 0 && - prev.tok.is_ident() { + if (next.tok == token::Lt || next.tok == token::Colon) && angle_count == 0 + && bracket_count == 0 && prev.tok.is_ident() + { result = Some(prev.sp); } @@ -193,12 +155,14 @@ impl<'a> SpanUtils<'a> { } if angle_count != 0 || bracket_count != 0 { let loc = self.sess.codemap().lookup_char_pos(span.lo()); - span_bug!(span, - "Mis-counted brackets when breaking path? Parsing '{}' \ - in {}, line {}", - self.snippet(span), - loc.file.name, - loc.line); + span_bug!( + span, + "Mis-counted brackets when breaking path? Parsing '{}' \ + in {}, line {}", + self.snippet(span), + loc.file.name, + loc.line + ); } if result.is_none() && prev.tok.is_ident() && angle_count == 0 { return Some(prev.sp); @@ -252,7 +216,7 @@ impl<'a> SpanUtils<'a> { if f(ts.tok) { let ts = toks.real_token(); if ts.tok == token::Eof { - return None + return None; } else { return Some(ts.sp); } @@ -319,7 +283,12 @@ impl<'a> SpanUtils<'a> { }; //If the span comes from a fake filemap, filter it. - if !self.sess.codemap().lookup_char_pos(parent.lo()).file.is_real_file() { + if !self.sess + .codemap() + .lookup_char_pos(parent.lo()) + .file + .is_real_file() + { return true; } @@ -330,7 +299,7 @@ impl<'a> SpanUtils<'a> { } macro_rules! filter { - ($util: expr, $span: ident, $parent: expr, None) => { + ($util: expr, $span: expr, $parent: expr, None) => { if $util.filter_generated($span, $parent) { return None; } diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 482350d04b5a7..d8318ea808221 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -11,15 +11,17 @@ test = false [dependencies] bitflags = "1.0" -num_cpus = "1.0" flate2 = "0.2" jobserver = "0.1.5" log = "0.3" +num_cpus = "1.0" owning_ref = "0.3.3" -rustc-demangle = "0.1.4" rustc = { path = "../librustc" } +rustc-demangle = "0.1.4" rustc_allocator = { path = "../librustc_allocator" } +rustc_apfloat = { path = "../librustc_apfloat" } rustc_back = { path = "../librustc_back" } +rustc_binaryen = { path = "../librustc_binaryen" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } @@ -30,6 +32,7 @@ rustc_trans_utils = { path = "../librustc_trans_utils" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +tempdir = "0.3" [target."cfg(windows)".dependencies] -cc = "1.0" +cc = "1.0.1" diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 2aecc016a5cbd..834558fc16614 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -11,7 +11,7 @@ use llvm::{self, ValueRef, AttributePlace}; use base; use builder::Builder; -use common::{instance_ty, ty_fn_sig, type_is_fat_ptr, C_usize}; +use common::{instance_ty, ty_fn_sig, C_usize}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -30,30 +30,34 @@ use cabi_sparc64; use cabi_nvptx; use cabi_nvptx64; use cabi_hexagon; -use machine::llalign_of_min; +use mir::place::{Alignment, PlaceRef}; +use mir::operand::OperandValue; use type_::Type; -use type_of; +use type_of::{LayoutLlvmExt, PointerKind}; -use rustc::hir; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, Layout, LayoutTyper, TyLayout, Size}; +use rustc::ty::layout::{self, Align, Size, TyLayout}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; use libc::c_uint; -use std::cmp; -use std::iter; +use std::{cmp, iter}; pub use syntax::abi::Abi; pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; -#[derive(Clone, Copy, PartialEq, Debug)] -enum ArgKind { - /// Pass the argument directly using the normal converted - /// LLVM type or by coercing to another specified type - Direct, - /// Pass the argument indirectly via a hidden pointer - Indirect, - /// Ignore the argument (useful for empty struct) +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PassMode { + /// Ignore the argument (useful for empty struct). Ignore, + /// Pass the argument directly. + Direct(ArgAttributes), + /// Pass a pair's elements directly in two arguments. + Pair(ArgAttributes, ArgAttributes), + /// Pass the argument after casting it, to either + /// a single uniform or a pair of registers. + Cast(CastTarget), + /// Pass the argument indirectly via a hidden pointer. + Indirect(ArgAttributes), } // Hack to disable non_upper_case_globals only for the bitflags! and not for the rest @@ -95,42 +99,78 @@ impl ArgAttribute { /// A compact representation of LLVM attributes (at least those relevant for this module) /// that can be manipulated without interacting with LLVM's Attribute machinery. -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct ArgAttributes { regular: ArgAttribute, - dereferenceable_bytes: u64, + pointee_size: Size, + pointee_align: Option } impl ArgAttributes { + fn new() -> Self { + ArgAttributes { + regular: ArgAttribute::default(), + pointee_size: Size::from_bytes(0), + pointee_align: None, + } + } + pub fn set(&mut self, attr: ArgAttribute) -> &mut Self { self.regular = self.regular | attr; self } - pub fn set_dereferenceable(&mut self, bytes: u64) -> &mut Self { - self.dereferenceable_bytes = bytes; - self + pub fn contains(&self, attr: ArgAttribute) -> bool { + self.regular.contains(attr) } pub fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { + let mut regular = self.regular; unsafe { - self.regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); - if self.dereferenceable_bytes != 0 { - llvm::LLVMRustAddDereferenceableAttr(llfn, - idx.as_uint(), - self.dereferenceable_bytes); + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableAttr(llfn, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentAttr(llfn, + idx.as_uint(), + align.abi() as u32); } + regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); } } pub fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef) { + let mut regular = self.regular; unsafe { - self.regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); - if self.dereferenceable_bytes != 0 { - llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, - idx.as_uint(), - self.dereferenceable_bytes); + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullCallSiteAttr(callsite, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentCallSiteAttr(callsite, + idx.as_uint(), + align.abi() as u32); } + regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); } } } @@ -169,7 +209,32 @@ impl Reg { } impl Reg { - fn llvm_type(&self, ccx: &CrateContext) -> Type { + pub fn align(&self, ccx: &CrateContext) -> Align { + let dl = ccx.data_layout(); + match self.kind { + RegKind::Integer => { + match self.size.bits() { + 1 => dl.i1_align, + 2...8 => dl.i8_align, + 9...16 => dl.i16_align, + 17...32 => dl.i32_align, + 33...64 => dl.i64_align, + 65...128 => dl.i128_align, + _ => bug!("unsupported integer: {:?}", self) + } + } + RegKind::Float => { + match self.size.bits() { + 32 => dl.f32_align, + 64 => dl.f64_align, + _ => bug!("unsupported float: {:?}", self) + } + } + RegKind::Vector => dl.vector_align(self.size) + } + } + + pub fn llvm_type(&self, ccx: &CrateContext) -> Type { match self.kind { RegKind::Integer => Type::ix(ccx, self.size.bits()), RegKind::Float => { @@ -188,7 +253,7 @@ impl Reg { /// An argument passed entirely registers with the /// same kind (e.g. HFA / HVA on PPC64 and AArch64). -#[derive(Copy, Clone)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Uniform { pub unit: Reg, @@ -211,7 +276,11 @@ impl From for Uniform { } impl Uniform { - fn llvm_type(&self, ccx: &CrateContext) -> Type { + pub fn align(&self, ccx: &CrateContext) -> Align { + self.unit.align(ccx) + } + + pub fn llvm_type(&self, ccx: &CrateContext) -> Type { let llunit = self.unit.llvm_type(ccx); if self.total <= self.unit.size { @@ -243,106 +312,62 @@ pub trait LayoutExt<'tcx> { impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { fn is_aggregate(&self) -> bool { - match *self.layout { - Layout::Scalar { .. } | - Layout::RawNullablePointer { .. } | - Layout::CEnum { .. } | - Layout::Vector { .. } => false, - - Layout::Array { .. } | - Layout::FatPointer { .. } | - Layout::Univariant { .. } | - Layout::UntaggedUnion { .. } | - Layout::General { .. } | - Layout::StructWrappedNullablePointer { .. } => true + match self.abi { + layout::Abi::Uninhabited | + layout::Abi::Scalar(_) | + layout::Abi::Vector => false, + layout::Abi::ScalarPair(..) | + layout::Abi::Aggregate { .. } => true } } fn homogeneous_aggregate<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option { - match *self.layout { - // The primitives for this algorithm. - Layout::Scalar { value, .. } | - Layout::RawNullablePointer { value, .. } => { - let kind = match value { - layout::Int(_) | + match self.abi { + layout::Abi::Uninhabited => None, + + // The primitive for this algorithm. + layout::Abi::Scalar(ref scalar) => { + let kind = match scalar.value { + layout::Int(..) | layout::Pointer => RegKind::Integer, layout::F32 | layout::F64 => RegKind::Float }; Some(Reg { kind, - size: self.size(ccx) + size: self.size }) } - Layout::CEnum { .. } => { - Some(Reg { - kind: RegKind::Integer, - size: self.size(ccx) - }) - } - - Layout::Vector { .. } => { + layout::Abi::Vector => { Some(Reg { kind: RegKind::Vector, - size: self.size(ccx) + size: self.size }) } - Layout::Array { count, .. } => { - if count > 0 { - self.field(ccx, 0).homogeneous_aggregate(ccx) - } else { - None - } - } - - Layout::Univariant { ref variant, .. } => { - let mut unaligned_offset = Size::from_bytes(0); + layout::Abi::ScalarPair(..) | + layout::Abi::Aggregate { .. } => { + let mut total = Size::from_bytes(0); let mut result = None; - for i in 0..self.field_count() { - if unaligned_offset != variant.offsets[i] { - return None; - } - - let field = self.field(ccx, i); - match (result, field.homogeneous_aggregate(ccx)) { - // The field itself must be a homogeneous aggregate. - (_, None) => return None, - // If this is the first field, record the unit. - (None, Some(unit)) => { - result = Some(unit); - } - // For all following fields, the unit must be the same. - (Some(prev_unit), Some(unit)) => { - if prev_unit != unit { - return None; - } + let is_union = match self.fields { + layout::FieldPlacement::Array { count, .. } => { + if count > 0 { + return self.field(ccx, 0).homogeneous_aggregate(ccx); + } else { + return None; } } + layout::FieldPlacement::Union(_) => true, + layout::FieldPlacement::Arbitrary { .. } => false + }; - // Keep track of the offset (without padding). - let size = field.size(ccx); - match unaligned_offset.checked_add(size, ccx) { - Some(offset) => unaligned_offset = offset, - None => return None + for i in 0..self.fields.count() { + if !is_union && total != self.fields.offset(i) { + return None; } - } - // There needs to be no padding. - if unaligned_offset != self.size(ccx) { - None - } else { - result - } - } - - Layout::UntaggedUnion { .. } => { - let mut max = Size::from_bytes(0); - let mut result = None; - - for i in 0..self.field_count() { let field = self.field(ccx, i); match (result, field.homogeneous_aggregate(ccx)) { // The field itself must be a homogeneous aggregate. @@ -360,28 +385,26 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> { } // Keep track of the offset (without padding). - let size = field.size(ccx); - if size > max { - max = size; + let size = field.size; + if is_union { + total = cmp::max(total, size); + } else { + total += size; } } // There needs to be no padding. - if max != self.size(ccx) { + if total != self.size { None } else { result } } - - // Rust-specific types, which we can ignore for C ABIs. - Layout::FatPointer { .. } | - Layout::General { .. } | - Layout::StructWrappedNullablePointer { .. } => None } } } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum CastTarget { Uniform(Uniform), Pair(Reg, Reg) @@ -400,7 +423,28 @@ impl From for CastTarget { } impl CastTarget { - fn llvm_type(&self, ccx: &CrateContext) -> Type { + pub fn size(&self, ccx: &CrateContext) -> Size { + match *self { + CastTarget::Uniform(u) => u.total, + CastTarget::Pair(a, b) => { + (a.size.abi_align(a.align(ccx)) + b.size) + .abi_align(self.align(ccx)) + } + } + } + + pub fn align(&self, ccx: &CrateContext) -> Align { + match *self { + CastTarget::Uniform(u) => u.align(ccx), + CastTarget::Pair(a, b) => { + ccx.data_layout().aggregate_align + .max(a.align(ccx)) + .max(b.align(ccx)) + } + } + } + + pub fn llvm_type(&self, ccx: &CrateContext) -> Type { match *self { CastTarget::Uniform(u) => u.llvm_type(ccx), CastTarget::Pair(a, b) => { @@ -413,131 +457,118 @@ impl CastTarget { } } -/// Information about how a specific C type -/// should be passed to or returned from a function -/// -/// This is borrowed from clang's ABIInfo.h -#[derive(Clone, Copy, Debug)] +/// Information about how to pass an argument to, +/// or return a value from, a function, under some ABI. +#[derive(Debug)] pub struct ArgType<'tcx> { - kind: ArgKind, pub layout: TyLayout<'tcx>, - /// Coerced LLVM Type - pub cast: Option, - /// Dummy argument, which is emitted before the real argument - pub pad: Option, - /// LLVM attributes of argument - pub attrs: ArgAttributes + + /// Dummy argument, which is emitted before the real argument. + pub pad: Option, + + pub mode: PassMode, } impl<'a, 'tcx> ArgType<'tcx> { fn new(layout: TyLayout<'tcx>) -> ArgType<'tcx> { ArgType { - kind: ArgKind::Direct, layout, - cast: None, pad: None, - attrs: ArgAttributes::default() + mode: PassMode::Direct(ArgAttributes::new()), } } - pub fn make_indirect(&mut self, ccx: &CrateContext<'a, 'tcx>) { - assert_eq!(self.kind, ArgKind::Direct); - - // Wipe old attributes, likely not valid through indirection. - self.attrs = ArgAttributes::default(); + pub fn make_indirect(&mut self) { + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); - let llarg_sz = self.layout.size(ccx).bytes(); + // Start with fresh attributes for the pointer. + let mut attrs = ArgAttributes::new(); // For non-immediate arguments the callee gets its own copy of // the value on the stack, so there are no aliases. It's also // program-invisible so can't possibly capture - self.attrs.set(ArgAttribute::NoAlias) - .set(ArgAttribute::NoCapture) - .set_dereferenceable(llarg_sz); - - self.kind = ArgKind::Indirect; + attrs.set(ArgAttribute::NoAlias) + .set(ArgAttribute::NoCapture) + .set(ArgAttribute::NonNull); + attrs.pointee_size = self.layout.size; + // FIXME(eddyb) We should be doing this, but at least on + // i686-pc-windows-msvc, it results in wrong stack offsets. + // attrs.pointee_align = Some(self.layout.align); + + self.mode = PassMode::Indirect(attrs); } - pub fn ignore(&mut self) { - assert_eq!(self.kind, ArgKind::Direct); - self.kind = ArgKind::Ignore; + pub fn make_indirect_byval(&mut self) { + self.make_indirect(); + match self.mode { + PassMode::Indirect(ref mut attrs) => { + attrs.set(ArgAttribute::ByVal); + } + _ => bug!() + } } pub fn extend_integer_width_to(&mut self, bits: u64) { // Only integers have signedness - let (i, signed) = match *self.layout { - Layout::Scalar { value, .. } => { - match value { - layout::Int(i) => { - if self.layout.ty.is_integral() { - (i, self.layout.ty.is_signed()) + if let layout::Abi::Scalar(ref scalar) = self.layout.abi { + if let layout::Int(i, signed) = scalar.value { + if i.size().bits() < bits { + if let PassMode::Direct(ref mut attrs) = self.mode { + attrs.set(if signed { + ArgAttribute::SExt } else { - return; - } + ArgAttribute::ZExt + }); } - _ => return } } - - // Rust enum types that map onto C enums also need to follow - // the target ABI zero-/sign-extension rules. - Layout::CEnum { discr, signed, .. } => (discr, signed), - - _ => return - }; - - if i.size().bits() < bits { - self.attrs.set(if signed { - ArgAttribute::SExt - } else { - ArgAttribute::ZExt - }); } } - pub fn cast_to>(&mut self, ccx: &CrateContext, target: T) { - self.cast = Some(target.into().llvm_type(ccx)); + pub fn cast_to>(&mut self, target: T) { + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); + self.mode = PassMode::Cast(target.into()); } - pub fn pad_with(&mut self, ccx: &CrateContext, reg: Reg) { - self.pad = Some(reg.llvm_type(ccx)); + pub fn pad_with(&mut self, reg: Reg) { + self.pad = Some(reg); } pub fn is_indirect(&self) -> bool { - self.kind == ArgKind::Indirect + match self.mode { + PassMode::Indirect(_) => true, + _ => false + } } pub fn is_ignore(&self) -> bool { - self.kind == ArgKind::Ignore + self.mode == PassMode::Ignore } - /// Get the LLVM type for an lvalue of the original Rust type of + /// Get the LLVM type for an place of the original Rust type of /// this argument/return, i.e. the result of `type_of::type_of`. pub fn memory_ty(&self, ccx: &CrateContext<'a, 'tcx>) -> Type { - type_of::type_of(ccx, self.layout.ty) + self.layout.llvm_type(ccx) } /// Store a direct/indirect value described by this ArgType into a - /// lvalue for the original Rust type of this argument/return. + /// place 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: &Builder<'a, 'tcx>, mut val: ValueRef, dst: ValueRef) { + pub fn store(&self, bcx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>) { if self.is_ignore() { return; } let ccx = bcx.ccx; if self.is_indirect() { - let llsz = C_usize(ccx, self.layout.size(ccx).bytes()); - let llalign = self.layout.align(ccx).abi(); - base::call_memcpy(bcx, dst, val, llsz, llalign as u32); - } else if let Some(ty) = self.cast { + OperandValue::Ref(val, Alignment::AbiAligned).store(bcx, dst) + } else if let PassMode::Cast(cast) = self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. let can_store_through_cast_ptr = false; if can_store_through_cast_ptr { - let cast_dst = bcx.pointercast(dst, ty.ptr_to()); - let llalign = self.layout.align(ccx).abi(); - bcx.store(val, cast_dst, Some(llalign as u32)); + let cast_dst = bcx.pointercast(dst.llval, cast.llvm_type(ccx).ptr_to()); + bcx.store(val, cast_dst, Some(self.layout.align)); } else { // The actual return type is a struct, but the ABI // adaptation code has cast it into some scalar type. The @@ -554,40 +585,45 @@ impl<'a, 'tcx> ArgType<'tcx> { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = bcx.alloca(ty, "abi_cast", None); - base::Lifetime::Start.call(bcx, llscratch); + let llscratch = bcx.alloca(cast.llvm_type(ccx), "abi_cast", cast.align(ccx)); + let scratch_size = cast.size(ccx); + bcx.lifetime_start(llscratch, scratch_size); // ...where we first store the value... bcx.store(val, llscratch, None); // ...and then memcpy it to the intended destination. base::call_memcpy(bcx, - bcx.pointercast(dst, Type::i8p(ccx)), + bcx.pointercast(dst.llval, Type::i8p(ccx)), bcx.pointercast(llscratch, Type::i8p(ccx)), - C_usize(ccx, self.layout.size(ccx).bytes()), - cmp::min(self.layout.align(ccx).abi() as u32, - llalign_of_min(ccx, ty))); + C_usize(ccx, self.layout.size.bytes()), + self.layout.align.min(cast.align(ccx))); - base::Lifetime::End.call(bcx, llscratch); + bcx.lifetime_end(llscratch, scratch_size); } } else { - if self.layout.ty == ccx.tcx().types.bool { - val = bcx.zext(val, Type::i8(ccx)); - } - bcx.store(val, dst, None); + OperandValue::Immediate(val).store(bcx, dst); } } - pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: ValueRef) { + pub fn store_fn_arg(&self, bcx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>) { if self.pad.is_some() { *idx += 1; } - if self.is_ignore() { - return; + let mut next = || { + let val = llvm::get_param(bcx.llfn(), *idx as c_uint); + *idx += 1; + val + }; + match self.mode { + PassMode::Ignore => {}, + PassMode::Pair(..) => { + OperandValue::Pair(next(), next()).store(bcx, dst); + } + PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { + self.store(bcx, next(), dst); + } } - let val = llvm::get_param(bcx.llfn(), *idx as c_uint); - *idx += 1; - self.store(bcx, val, dst); } } @@ -596,7 +632,7 @@ impl<'a, 'tcx> ArgType<'tcx> { /// /// I will do my best to describe this structure, but these /// comments are reverse-engineered and may be inaccurate. -NDM -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct FnType<'tcx> { /// The LLVM types of each argument. pub args: Vec>, @@ -615,14 +651,14 @@ impl<'a, 'tcx> FnType<'tcx> { let fn_ty = instance_ty(ccx.tcx(), &instance); let sig = ty_fn_sig(ccx, fn_ty); let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); - Self::new(ccx, sig, &[]) + FnType::new(ccx, sig, &[]) } pub fn new(ccx: &CrateContext<'a, 'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> FnType<'tcx> { let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); - fn_ty.adjust_for_abi(ccx, sig); + fn_ty.adjust_for_abi(ccx, sig.abi); fn_ty } @@ -631,8 +667,23 @@ impl<'a, 'tcx> FnType<'tcx> { extra_args: &[Ty<'tcx>]) -> FnType<'tcx> { let mut fn_ty = FnType::unadjusted(ccx, sig, extra_args); // Don't pass the vtable, it's not an argument of the virtual fn. - fn_ty.args[1].ignore(); - fn_ty.adjust_for_abi(ccx, sig); + { + let self_arg = &mut fn_ty.args[0]; + match self_arg.mode { + PassMode::Pair(data_ptr, _) => { + self_arg.mode = PassMode::Direct(data_ptr); + } + _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) + } + + let pointee = self_arg.layout.ty.builtin_deref(true, ty::NoPreference) + .unwrap_or_else(|| { + bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) + }).ty; + let fat_ptr_ty = ccx.tcx().mk_mut_ptr(pointee); + self_arg.layout = ccx.layout_of(fat_ptr_ty).field(ccx, 0); + } + fn_ty.adjust_for_abi(ccx, sig.abi); fn_ty } @@ -697,119 +748,114 @@ impl<'a, 'tcx> FnType<'tcx> { _ => false }; - let arg_of = |ty: Ty<'tcx>, is_return: bool| { - let mut arg = ArgType::new(ccx.layout_of(ty)); - if ty.is_bool() { - arg.attrs.set(ArgAttribute::ZExt); - } else { - if arg.layout.size(ccx).bytes() == 0 { - // For some forsaken reason, x86_64-pc-windows-gnu - // doesn't ignore zero-sized struct arguments. - // The same is true for s390x-unknown-linux-gnu. - if is_return || rust_abi || - (!win_x64_gnu && !linux_s390x) { - arg.ignore(); - } - } + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &layout::Scalar, + layout: TyLayout<'tcx>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; } - arg - }; - let ret_ty = sig.output(); - let mut ret = arg_of(ret_ty, true); - - if !type_is_fat_ptr(ccx, ret_ty) { - // The `noalias` attribute on the return value is useful to a - // function ptr caller. - if ret_ty.is_box() { - // `Box` pointer return values never alias because ownership - // is transferred - ret.attrs.set(ArgAttribute::NoAlias); + // Only pointer types handled below. + if scalar.value != layout::Pointer { + return; } - // We can also mark the return value as `dereferenceable` in certain cases - match ret_ty.sty { - // These are not really pointers but pairs, (pointer, len) - ty::TyRef(_, ty::TypeAndMut { ty, .. }) => { - ret.attrs.set_dereferenceable(ccx.size_of(ty)); - } - ty::TyAdt(def, _) if def.is_box() => { - ret.attrs.set_dereferenceable(ccx.size_of(ret_ty.boxed_ty())); + if scalar.valid_range.start < scalar.valid_range.end { + if scalar.valid_range.start > 0 { + attrs.set(ArgAttribute::NonNull); } - _ => {} } - } - let mut args = Vec::with_capacity(inputs.len() + extra_args.len()); - - // Handle safe Rust thin and fat pointers. - let rust_ptr_attrs = |ty: Ty<'tcx>, arg: &mut ArgType| match ty.sty { - // `Box` pointer parameters never alias because ownership is transferred - ty::TyAdt(def, _) if def.is_box() => { - arg.attrs.set(ArgAttribute::NoAlias); - Some(ty.boxed_ty()) - } + if let Some(pointee) = layout.pointee_info_at(ccx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_size = pointee.size; + attrs.pointee_align = Some(pointee.align); - ty::TyRef(b, mt) => { - use rustc::ty::{BrAnon, ReLateBound}; + // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions + // with align attributes, and those calls later block optimizations. + if !is_return { + attrs.pointee_align = None; + } - // `&mut` pointer parameters never alias other parameters, or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, and can be marked as - // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely - // on memory dependencies rather than pointer equality - let is_freeze = ccx.shared().type_is_freeze(mt.ty); + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | + PointerKind::UniqueBorrowed => !is_return + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } - if mt.mutbl != hir::MutMutable && is_freeze { - arg.attrs.set(ArgAttribute::NoAlias); + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } } + } + }; - if mt.mutbl == hir::MutImmutable && is_freeze { - arg.attrs.set(ArgAttribute::ReadOnly); + let arg_of = |ty: Ty<'tcx>, is_return: bool| { + let mut arg = ArgType::new(ccx.layout_of(ty)); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for s390x-unknown-linux-gnu. + if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { + arg.mode = PassMode::Ignore; } + } - // When a reference in an argument has no named lifetime, it's - // impossible for that reference to escape this function - // (returned or stored beyond the call by a closure). - if let ReLateBound(_, BrAnon(_)) = *b { - arg.attrs.set(ArgAttribute::NoCapture); + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, + a, + arg.layout, + Size::from_bytes(0), + false); + adjust_for_rust_scalar(&mut b_attrs, + b, + arg.layout, + a.value.size(ccx).abi_align(b.value.align(ccx)), + false); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; } - - Some(mt.ty) } - _ => None - }; - for ty in inputs.iter().chain(extra_args.iter()) { - let mut arg = arg_of(ty, false); - - if let ty::layout::FatPointer { .. } = *arg.layout { - let mut data = ArgType::new(arg.layout.field(ccx, 0)); - let mut info = ArgType::new(arg.layout.field(ccx, 1)); - - if let Some(inner) = rust_ptr_attrs(ty, &mut data) { - data.attrs.set(ArgAttribute::NonNull); - if ccx.tcx().struct_tail(inner).is_trait() { - // vtables can be safely marked non-null, readonly - // and noalias. - info.attrs.set(ArgAttribute::NonNull); - info.attrs.set(ArgAttribute::ReadOnly); - info.attrs.set(ArgAttribute::NoAlias); - } + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, + scalar, + arg.layout, + Size::from_bytes(0), + is_return); } - args.push(data); - args.push(info); - } else { - if let Some(inner) = rust_ptr_attrs(ty, &mut arg) { - arg.attrs.set_dereferenceable(ccx.size_of(inner)); - } - args.push(arg); } - } + + arg + }; FnType { - args, - ret, + ret: arg_of(sig.output(), true), + args: inputs.iter().chain(extra_args.iter()).map(|ty| { + arg_of(ty, false) + }).collect(), variadic: sig.variadic, cconv, } @@ -817,63 +863,38 @@ impl<'a, 'tcx> FnType<'tcx> { fn adjust_for_abi(&mut self, ccx: &CrateContext<'a, 'tcx>, - sig: ty::FnSig<'tcx>) { - let abi = sig.abi; + abi: Abi) { if abi == Abi::Unadjusted { return } if abi == Abi::Rust || abi == Abi::RustCall || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { let fixup = |arg: &mut ArgType<'tcx>| { - if !arg.layout.is_aggregate() { - return; - } - - let size = arg.layout.size(ccx); + if arg.is_ignore() { return; } - if let Some(unit) = arg.layout.homogeneous_aggregate(ccx) { - // Replace newtypes with their inner-most type. - if unit.size == size { - // Needs a cast as we've unpacked a newtype. - arg.cast_to(ccx, unit); - return; - } - - // Pairs of floats. - if unit.kind == RegKind::Float { - if unit.size.checked_mul(2, ccx) == Some(size) { - // FIXME(eddyb) This should be using Uniform instead of a pair, - // but the resulting [2 x float/double] breaks emscripten. - // See https://github.com/kripken/emscripten-fastcomp/issues/178. - arg.cast_to(ccx, CastTarget::Pair(unit, unit)); - return; - } - } + match arg.layout.abi { + layout::Abi::Aggregate { .. } => {} + _ => return } + let size = arg.layout.size; if size > layout::Pointer.size(ccx) { - arg.make_indirect(ccx); + arg.make_indirect(); } else { // We want to pass small aggregates as immediates, but using // a LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. - arg.cast_to(ccx, Reg { + arg.cast_to(Reg { kind: RegKind::Integer, size }); } }; - // Fat pointers are returned by-value. - if !self.ret.is_ignore() { - if !type_is_fat_ptr(ccx, sig.output()) { - fixup(&mut self.ret); - } - } + fixup(&mut self.ret); for arg in &mut self.args { - if arg.is_ignore() { continue; } fixup(arg); } - if self.ret.is_indirect() { - self.ret.attrs.set(ArgAttribute::StructRet); + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); } return; } @@ -890,7 +911,7 @@ impl<'a, 'tcx> FnType<'tcx> { "x86_64" => if abi == Abi::SysV64 { cabi_x86_64::compute_abi_info(ccx, self); } else if abi == Abi::Win64 || ccx.sess().target.target.options.is_like_windows { - cabi_x86_win64::compute_abi_info(ccx, self); + cabi_x86_win64::compute_abi_info(self); } else { cabi_x86_64::compute_abi_info(ccx, self); }, @@ -903,51 +924,52 @@ impl<'a, 'tcx> FnType<'tcx> { "s390x" => cabi_s390x::compute_abi_info(ccx, self), "asmjs" => cabi_asmjs::compute_abi_info(ccx, self), "wasm32" => cabi_asmjs::compute_abi_info(ccx, self), - "msp430" => cabi_msp430::compute_abi_info(ccx, self), + "msp430" => cabi_msp430::compute_abi_info(self), "sparc" => cabi_sparc::compute_abi_info(ccx, self), "sparc64" => cabi_sparc64::compute_abi_info(ccx, self), - "nvptx" => cabi_nvptx::compute_abi_info(ccx, self), - "nvptx64" => cabi_nvptx64::compute_abi_info(ccx, self), - "hexagon" => cabi_hexagon::compute_abi_info(ccx, self), + "nvptx" => cabi_nvptx::compute_abi_info(self), + "nvptx64" => cabi_nvptx64::compute_abi_info(self), + "hexagon" => cabi_hexagon::compute_abi_info(self), a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)) } - if self.ret.is_indirect() { - self.ret.attrs.set(ArgAttribute::StructRet); + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); } } pub fn llvm_type(&self, ccx: &CrateContext<'a, 'tcx>) -> Type { let mut llargument_tys = Vec::new(); - let llreturn_ty = if self.ret.is_ignore() { - Type::void(ccx) - } else if self.ret.is_indirect() { - llargument_tys.push(self.ret.memory_ty(ccx).ptr_to()); - Type::void(ccx) - } else { - self.ret.cast.unwrap_or_else(|| { - type_of::immediate_type_of(ccx, self.ret.layout.ty) - }) + let llreturn_ty = match self.ret.mode { + PassMode::Ignore => Type::void(ccx), + PassMode::Direct(_) | PassMode::Pair(..) => { + self.ret.layout.immediate_llvm_type(ccx) + } + PassMode::Cast(cast) => cast.llvm_type(ccx), + PassMode::Indirect(_) => { + llargument_tys.push(self.ret.memory_ty(ccx).ptr_to()); + Type::void(ccx) + } }; for arg in &self.args { - if arg.is_ignore() { - continue; - } // add padding if let Some(ty) = arg.pad { - llargument_tys.push(ty); + llargument_tys.push(ty.llvm_type(ccx)); } - let llarg_ty = if arg.is_indirect() { - arg.memory_ty(ccx).ptr_to() - } else { - arg.cast.unwrap_or_else(|| { - type_of::immediate_type_of(ccx, arg.layout.ty) - }) + let llarg_ty = match arg.mode { + PassMode::Ignore => continue, + PassMode::Direct(_) => arg.layout.immediate_llvm_type(ccx), + PassMode::Pair(..) => { + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(ccx, 0)); + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(ccx, 1)); + continue; + } + PassMode::Cast(cast) => cast.llvm_type(ccx), + PassMode::Indirect(_) => arg.memory_ty(ccx).ptr_to(), }; - llargument_tys.push(llarg_ty); } @@ -959,31 +981,61 @@ impl<'a, 'tcx> FnType<'tcx> { } pub fn apply_attrs_llfn(&self, llfn: ValueRef) { - let mut i = if self.ret.is_indirect() { 1 } else { 0 }; - if !self.ret.is_ignore() { - self.ret.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} } - i += 1; for arg in &self.args { - if !arg.is_ignore() { - if arg.pad.is_some() { i += 1; } - arg.attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); - i += 1; + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), } } } pub fn apply_attrs_callsite(&self, callsite: ValueRef) { - let mut i = if self.ret.is_indirect() { 1 } else { 0 }; - if !self.ret.is_ignore() { - self.ret.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} } - i += 1; for arg in &self.args { - if !arg.is_ignore() { - if arg.pad.is_some() { i += 1; } - arg.attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); - i += 1; + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), } } @@ -992,7 +1044,3 @@ impl<'a, 'tcx> FnType<'tcx> { } } } - -pub fn align_up_to(off: u64, a: u64) -> u64 { - (off + a - 1) / a * a -} diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs deleted file mode 100644 index 23a45a7962abf..0000000000000 --- a/src/librustc_trans/adt.rs +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Representation of Algebraic Data Types -//! -//! This module determines how to represent enums, structs, and tuples -//! based on their monomorphized types; it is responsible both for -//! choosing a representation and translating basic operations on -//! values of those types. (Note: exporting the representations for -//! debuggers is handled in debuginfo.rs, not here.) -//! -//! Note that the interface treats everything as a general case of an -//! enum, so structs/tuples/etc. have one pseudo-variant with -//! discriminant 0; i.e., as if they were a univariant enum. -//! -//! Having everything in one place will enable improvements to data -//! structure representation; possibilities include: -//! -//! - User-specified alignment (e.g., cacheline-aligning parts of -//! concurrently accessed data structures); LLVM can't represent this -//! directly, so we'd have to insert padding fields in any structure -//! that might contain one and adjust GEP indices accordingly. See -//! issue #4578. -//! -//! - Store nested enums' discriminants in the same word. Rather, if -//! some variants start with enums, and those enums representations -//! have unused alignment padding between discriminant and body, the -//! outer enum's discriminant can be stored there and those variants -//! can start at offset 0. Kind of fancy, and might need work to -//! make copies of the inner enum type cooperate, but it could help -//! with `Option` or `Result` wrapped around another enum. -//! -//! - Tagged pointers would be neat, but given that any type can be -//! used unboxed and any field can have pointers (including mutable) -//! taken to it, implementing them for Rust seems difficult. - -use std; - -use llvm::{ValueRef, True, IntEQ, IntNE}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, LayoutTyper}; -use common::*; -use builder::Builder; -use base; -use machine; -use monomorphize; -use type_::Type; -use type_of; - -use mir::lvalue::Alignment; - -/// 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. -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() - }, - ty::TyAdt(ref def, ref substs) => { - def.variants[variant_index].fields.iter().map(|f| { - monomorphize::field_ty(cx.tcx(), substs, f) - }).collect::>() - }, - ty::TyTuple(fields, _) => fields.to_vec(), - ty::TyClosure(def_id, substs) => { - if variant_index > 0 { bug!("{} is a closure, which only has one variant", t);} - substs.upvar_tys(def_id, cx.tcx()).collect() - }, - ty::TyGenerator(def_id, substs, _) => { - if variant_index > 0 { bug!("{} is a generator, which only has one variant", t);} - substs.field_tys(def_id, cx.tcx()).map(|t| { - cx.tcx().normalize_associated_type(&t) - }).collect() - }, - _ => bug!("{} is not a type that can have fields.", t) - } -} - -/// LLVM-level types are a little complicated. -/// -/// C-like enums need to be actual ints, not wrapped in a struct, -/// because that changes the ABI on some platforms (see issue #10308). -/// -/// For nominal types, in some cases, we need to use LLVM named structs -/// and fill in the actual contents in a second pass to prevent -/// unbounded recursion; see also the comments in `trans::type_of`. -pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - generic_type_of(cx, t, None) -} - -pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, name: &str) -> Type { - generic_type_of(cx, t, Some(name)) -} - -pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, llty: &mut Type) { - let l = cx.layout_of(t); - debug!("finish_type_of: {} with layout {:#?}", t, l); - match *l { - layout::CEnum { .. } | layout::General { .. } - | layout::UntaggedUnion { .. } | layout::RawNullablePointer { .. } => { } - layout::Univariant { ..} - | layout::StructWrappedNullablePointer { .. } => { - let (nonnull_variant_index, nonnull_variant, packed) = match *l { - layout::Univariant { ref variant, .. } => (0, variant, variant.packed), - layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => - (nndiscr, nonnull, nonnull.packed), - _ => unreachable!() - }; - let fields = compute_fields(cx, t, nonnull_variant_index as usize, true); - llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant), - packed) - }, - _ => bug!("This function cannot handle {} with layout {:#?}", t, l) - } -} - -fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, - name: Option<&str>) -> Type { - let l = cx.layout_of(t); - debug!("adt::generic_type_of t: {:?} name: {:?}", t, name); - match *l { - layout::CEnum { discr, .. } => Type::from_integer(cx, discr), - layout::RawNullablePointer { nndiscr, .. } => { - let (def, substs) = match t.sty { - ty::TyAdt(d, s) => (d, s), - _ => bug!("{} is not an ADT", t) - }; - let nnty = monomorphize::field_ty(cx.tcx(), substs, - &def.variants[nndiscr as usize].fields[0]); - if let layout::Scalar { value: layout::Pointer, .. } = *cx.layout_of(nnty) { - Type::i8p(cx) - } else { - type_of::type_of(cx, nnty) - } - } - layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { - let fields = compute_fields(cx, t, nndiscr as usize, false); - match name { - None => { - Type::struct_(cx, &struct_llfields(cx, &fields, nonnull), - nonnull.packed) - } - Some(name) => { - Type::named_struct(cx, name) - } - } - } - layout::Univariant { ref variant, .. } => { - // Note that this case also handles empty enums. - // Thus the true as the final parameter here. - let fields = compute_fields(cx, t, 0, true); - match name { - None => { - let fields = struct_llfields(cx, &fields, &variant); - Type::struct_(cx, &fields, variant.packed) - } - Some(name) => { - // Hypothesis: named_struct's can never need a - // drop flag. (... needs validation.) - Type::named_struct(cx, name) - } - } - } - layout::UntaggedUnion { ref variants, .. }=> { - // Use alignment-sized ints to fill all the union storage. - let size = variants.stride().bytes(); - let align = variants.align.abi(); - let fill = union_fill(cx, size, align); - match name { - None => { - Type::struct_(cx, &[fill], variants.packed) - } - Some(name) => { - let mut llty = Type::named_struct(cx, name); - llty.set_struct_body(&[fill], variants.packed); - llty - } - } - } - layout::General { discr, size, align, primitive_align, .. } => { - // We need a representation that has: - // * The alignment of the most-aligned field - // * The size of the largest variant (rounded up to that alignment) - // * No alignment padding anywhere any variant has actual data - // (currently matters only for enums small enough to be immediate) - // * The discriminant in an obvious place. - // - // So we start with the discriminant, pad it up to the alignment with - // more of its own type, then use alignment-sized ints to get the rest - // of the size. - let size = size.bytes(); - let align = align.abi(); - let primitive_align = primitive_align.abi(); - assert!(align <= std::u32::MAX as u64); - let discr_ty = Type::from_integer(cx, discr); - let discr_size = discr.size().bytes(); - let padded_discr_size = roundup(discr_size, align as u32); - let variant_part_size = size-padded_discr_size; - let variant_fill = union_fill(cx, variant_part_size, primitive_align); - - assert_eq!(machine::llalign_of_min(cx, variant_fill), primitive_align as u32); - assert_eq!(padded_discr_size % discr_size, 0); // Ensure discr_ty can fill pad evenly - let fields: Vec = - [discr_ty, - Type::array(&discr_ty, (padded_discr_size - discr_size)/discr_size), - variant_fill].iter().cloned().collect(); - match name { - None => { - Type::struct_(cx, &fields, false) - } - Some(name) => { - let mut llty = Type::named_struct(cx, name); - llty.set_struct_body(&fields, false); - llty - } - } - } - _ => bug!("Unsupported type {} represented as {:#?}", t, l) - } -} - -fn union_fill(cx: &CrateContext, size: u64, align: u64) -> Type { - assert_eq!(size%align, 0); - assert_eq!(align.count_ones(), 1, "Alignment must be a power fof 2. Got {}", align); - let align_units = size/align; - let layout_align = layout::Align::from_bytes(align, align).unwrap(); - if let Some(ity) = layout::Integer::for_abi_align(cx, layout_align) { - Type::array(&Type::from_integer(cx, ity), align_units) - } else { - Type::array(&Type::vector(&Type::i32(cx), align/4), - align_units) - } -} - - -// Double index to account for padding (FieldPath already uses `Struct::memory_index`) -fn struct_llfields_path(discrfield: &layout::FieldPath) -> Vec { - discrfield.iter().map(|&i| (i as usize) << 1).collect::>() -} - - -// Lookup `Struct::memory_index` and double it to account for padding -pub fn struct_llfields_index(variant: &layout::Struct, index: usize) -> usize { - (variant.memory_index[index] as usize) << 1 -} - - -pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, field_tys: &Vec>, - variant: &layout::Struct) -> Vec { - debug!("struct_llfields: variant: {:?}", variant); - let mut first_field = true; - let mut min_offset = 0; - let mut result: Vec = Vec::with_capacity(field_tys.len() * 2); - let field_iter = variant.field_index_by_increasing_offset().map(|i| { - (i, field_tys[i as usize], variant.offsets[i as usize].bytes()) }); - for (index, ty, target_offset) in field_iter { - if first_field { - debug!("struct_llfields: {} ty: {} min_offset: {} target_offset: {}", - index, ty, min_offset, target_offset); - first_field = false; - } else { - assert!(target_offset >= min_offset); - let padding_bytes = if variant.packed { 0 } else { target_offset - min_offset }; - result.push(Type::array(&Type::i8(cx), padding_bytes)); - debug!("struct_llfields: {} ty: {} pad_bytes: {} min_offset: {} target_offset: {}", - index, ty, padding_bytes, min_offset, target_offset); - } - let llty = type_of::in_memory_type_of(cx, ty); - result.push(llty); - let layout = cx.layout_of(ty); - let target_size = layout.size(&cx.tcx().data_layout).bytes(); - min_offset = target_offset + target_size; - } - if variant.sized && !field_tys.is_empty() { - if variant.stride().bytes() < min_offset { - bug!("variant: {:?} stride: {} min_offset: {}", variant, variant.stride().bytes(), - min_offset); - } - let padding_bytes = variant.stride().bytes() - min_offset; - debug!("struct_llfields: pad_bytes: {} min_offset: {} min_size: {} stride: {}\n", - padding_bytes, min_offset, variant.min_size.bytes(), variant.stride().bytes()); - result.push(Type::array(&Type::i8(cx), padding_bytes)); - assert!(result.len() == (field_tys.len() * 2)); - } else { - debug!("struct_llfields: min_offset: {} min_size: {} stride: {}\n", - min_offset, variant.min_size.bytes(), variant.stride().bytes()); - } - - result -} - -pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { - match *l { - layout::CEnum { signed, .. }=> signed, - _ => false, - } -} - -/// Obtain the actual discriminant of a value. -pub fn trans_get_discr<'a, 'tcx>( - bcx: &Builder<'a, 'tcx>, - t: Ty<'tcx>, - scrutinee: ValueRef, - alignment: Alignment, - cast_to: Option, - range_assert: bool -) -> ValueRef { - debug!("trans_get_discr t: {:?}", t); - let l = bcx.ccx.layout_of(t); - - let val = match *l { - layout::CEnum { discr, min, max, .. } => { - load_discr(bcx, discr, scrutinee, alignment, min, max, range_assert) - } - layout::General { discr, ref variants, .. } => { - let ptr = bcx.struct_gep(scrutinee, 0); - load_discr(bcx, discr, ptr, alignment, - 0, variants.len() as u64 - 1, - range_assert) - } - layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0), - layout::RawNullablePointer { nndiscr, .. } => { - let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - let discr = bcx.load(scrutinee, alignment.to_align()); - bcx.icmp(cmp, discr, C_null(val_ty(discr))) - } - layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee, alignment) - }, - _ => bug!("{} is not an enum", t) - }; - match cast_to { - None => val, - Some(llty) => bcx.intcast(val, llty, is_discr_signed(&l)) - } -} - -fn struct_wrapped_nullable_bitdiscr( - bcx: &Builder, - nndiscr: u64, - discrfield: &layout::FieldPath, - scrutinee: ValueRef, - alignment: Alignment, -) -> ValueRef { - let path = struct_llfields_path(discrfield); - let llptrptr = bcx.gepi(scrutinee, &path); - let llptr = bcx.load(llptrptr, alignment.to_align()); - let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - bcx.icmp(cmp, llptr, C_null(val_ty(llptr))) -} - -/// Helper for cases where the discriminant is simply loaded. -fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, - alignment: Alignment, min: u64, max: u64, - range_assert: bool) - -> ValueRef { - let llty = Type::from_integer(bcx.ccx, ity); - assert_eq!(val_ty(ptr), llty.ptr_to()); - let bits = ity.size().bits(); - assert!(bits <= 64); - let bits = bits as usize; - let mask = !0u64 >> (64 - bits); - // For a (max) discr of -1, max will be `-1 as usize`, which overflows. - // However, that is fine here (it would still represent the full range), - if max.wrapping_add(1) & mask == min & mask || !range_assert { - // i.e., if the range is everything. The lo==hi case would be - // rejected by the LLVM verifier (it would mean either an - // empty set, which is impossible, or the entire range of the - // type, which is pointless). - bcx.load(ptr, alignment.to_align()) - } else { - // llvm::ConstantRange can deal with ranges that wrap around, - // so an overflow on (max + 1) is fine. - bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True, - alignment.to_align()) - } -} - -/// Set the discriminant for a new value of the given case of the given -/// representation. -pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: u64) { - let l = bcx.ccx.layout_of(t); - match *l { - layout::CEnum{ discr, min, max, .. } => { - assert_discr_in_range(min, max, to); - bcx.store(C_int(Type::from_integer(bcx.ccx, discr), to as i64), - val, None); - } - layout::General{ discr, .. } => { - bcx.store(C_int(Type::from_integer(bcx.ccx, discr), to as i64), - bcx.struct_gep(val, 0), None); - } - layout::Univariant { .. } - | layout::UntaggedUnion { .. } - | layout::Vector { .. } => { - assert_eq!(to, 0); - } - layout::RawNullablePointer { nndiscr, .. } => { - if to != nndiscr { - let llptrty = val_ty(val).element_type(); - bcx.store(C_null(llptrty), val, None); - } - } - layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => { - if to != nndiscr { - if target_sets_discr_via_memset(bcx) { - // Issue #34427: As workaround for LLVM bug on - // ARM, use memset of 0 on whole struct rather - // than storing null to single target field. - let llptr = bcx.pointercast(val, Type::i8(bcx.ccx).ptr_to()); - let fill_byte = C_u8(bcx.ccx, 0); - let size = C_usize(bcx.ccx, nonnull.stride().bytes()); - let align = C_i32(bcx.ccx, nonnull.align.abi() as i32); - base::call_memset(bcx, llptr, fill_byte, size, align, false); - } else { - let path = struct_llfields_path(discrfield); - let llptrptr = bcx.gepi(val, &path); - let llptrty = val_ty(llptrptr).element_type(); - bcx.store(C_null(llptrty), llptrptr, None); - } - } - } - _ => bug!("Cannot handle {} represented as {:#?}", t, l) - } -} - -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" -} - -pub fn assert_discr_in_range(min: D, max: D, discr: D) { - if min <= max { - assert!(min <= discr && discr <= max) - } else { - assert!(min <= discr || discr <= max) - } -} - -// 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 } - -/// Extract a field of a constant value, as appropriate for its -/// representation. -/// -/// (Not to be confused with `common::const_get_elt`, which operates on -/// raw LLVM-level structs and arrays.) -pub fn const_get_field<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, - val: ValueRef, - ix: usize) -> ValueRef { - let l = ccx.layout_of(t); - match *l { - layout::CEnum { .. } => bug!("element access in C-like enum const"), - layout::Univariant { ref variant, .. } => { - const_struct_field(val, variant.memory_index[ix] as usize) - } - layout::Vector { .. } => const_struct_field(val, ix), - layout::UntaggedUnion { .. } => const_struct_field(val, 0), - _ => bug!("{} does not have fields.", t) - } -} - -/// Extract field of struct-like const, skipping our alignment padding. -fn const_struct_field(val: ValueRef, ix: usize) -> ValueRef { - // Get the ix-th non-undef element of the struct. - let mut real_ix = 0; // actual position in the struct - let mut ix = ix; // logical index relative to real_ix - let mut field; - loop { - loop { - field = const_get_elt(val, &[real_ix]); - if !is_undef(field) { - break; - } - real_ix = real_ix + 1; - } - if ix == 0 { - return field; - } - ix = ix - 1; - real_ix = real_ix + 1; - } -} diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs index 92cbd004206e7..ef76fece088fe 100644 --- a/src/librustc_trans/asm.rs +++ b/src/librustc_trans/asm.rs @@ -11,16 +11,15 @@ //! # Translation of inline assembly. use llvm::{self, ValueRef}; -use base; use common::*; -use type_of; use type_::Type; +use type_of::LayoutLlvmExt; use builder::Builder; use rustc::hir; -use rustc::ty::Ty; -use mir::lvalue::Alignment; +use mir::place::PlaceRef; +use mir::operand::OperandValue; use std::ffi::CString; use syntax::ast::AsmDialect; @@ -30,7 +29,7 @@ use libc::{c_uint, c_char}; pub fn trans_inline_asm<'a, 'tcx>( bcx: &Builder<'a, 'tcx>, ia: &hir::InlineAsm, - outputs: Vec<(ValueRef, Ty<'tcx>)>, + outputs: Vec>, mut inputs: Vec ) { let mut ext_constraints = vec![]; @@ -38,20 +37,15 @@ pub fn trans_inline_asm<'a, 'tcx>( // Prepare the output operands let mut indirect_outputs = vec![]; - for (i, (out, &(val, ty))) in ia.outputs.iter().zip(&outputs).enumerate() { - let val = if out.is_rw || out.is_indirect { - Some(base::load_ty(bcx, val, Alignment::Packed, ty)) - } else { - None - }; + for (i, (out, place)) in ia.outputs.iter().zip(&outputs).enumerate() { if out.is_rw { - inputs.push(val.unwrap()); + inputs.push(place.load(bcx).immediate()); ext_constraints.push(i.to_string()); } if out.is_indirect { - indirect_outputs.push(val.unwrap()); + indirect_outputs.push(place.load(bcx).immediate()); } else { - output_types.push(type_of::type_of(bcx.ccx, ty)); + output_types.push(place.layout.llvm_type(bcx.ccx)); } } if !indirect_outputs.is_empty() { @@ -106,9 +100,9 @@ pub fn trans_inline_asm<'a, 'tcx>( // Again, based on how many outputs we have let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); - for (i, (_, &(val, _))) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i) }; - bcx.store(v, val, None); + for (i, (_, &place)) in outputs.enumerate() { + let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i as u64) }; + OperandValue::Immediate(v).store(bcx, place); } // Store mark in a metadata node so we can map LLVM errors diff --git a/src/librustc_trans/assert_module_sources.rs b/src/librustc_trans/assert_module_sources.rs index 6e661a5a8c6a4..c891bd8aaf44f 100644 --- a/src/librustc_trans/assert_module_sources.rs +++ b/src/librustc_trans/assert_module_sources.rs @@ -27,47 +27,32 @@ //! the HIR doesn't change as a result of the annotations, which might //! perturb the reuse results. +use rustc::dep_graph::{DepNode, DepConstructor}; use rustc::ty::TyCtxt; use syntax::ast; - -use {ModuleSource, ModuleTranslation}; - use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED}; const MODULE: &'static str = "module"; const CFG: &'static str = "cfg"; #[derive(Debug, PartialEq, Clone, Copy)] -pub enum Disposition { Reused, Translated } - -impl ModuleTranslation { - pub fn disposition(&self) -> (String, Disposition) { - let disposition = match self.source { - ModuleSource::Preexisting(_) => Disposition::Reused, - ModuleSource::Translated(_) => Disposition::Translated, - }; +enum Disposition { Reused, Translated } - (self.name.clone(), disposition) - } -} - -pub(crate) fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - modules: &[(String, Disposition)]) { +pub(crate) fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let _ignore = tcx.dep_graph.in_ignore(); if tcx.sess.opts.incremental.is_none() { return; } - let ams = AssertModuleSource { tcx: tcx, modules: modules }; + let ams = AssertModuleSource { tcx }; for attr in &tcx.hir.krate().attrs { ams.check_attr(attr); } } struct AssertModuleSource<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - modules: &'a [(String, Disposition)], + tcx: TyCtxt<'a, 'tcx, 'tcx> } impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { @@ -86,32 +71,31 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { } let mname = self.field(attr, MODULE); - let mtrans = self.modules.iter().find(|&&(ref name, _)| name == mname.as_str()); - let mtrans = match mtrans { - Some(m) => m, - None => { - debug!("module name `{}` not found amongst:", mname); - for &(ref name, ref disposition) in self.modules { - debug!("module named `{}` with disposition {:?}", - name, - disposition); - } - self.tcx.sess.span_err( - attr.span, - &format!("no module named `{}`", mname)); - return; - } - }; + let dep_node = DepNode::new(self.tcx, + DepConstructor::CompileCodegenUnit(mname.as_str())); - let mtrans_disposition = mtrans.1; - if disposition != mtrans_disposition { - self.tcx.sess.span_err( - attr.span, - &format!("expected module named `{}` to be {:?} but is {:?}", - mname, - disposition, - mtrans_disposition)); + if let Some(loaded_from_cache) = self.tcx.dep_graph.was_loaded_from_cache(&dep_node) { + match (disposition, loaded_from_cache) { + (Disposition::Reused, false) => { + self.tcx.sess.span_err( + attr.span, + &format!("expected module named `{}` to be Reused but is Translated", + mname)); + } + (Disposition::Translated, true) => { + self.tcx.sess.span_err( + attr.span, + &format!("expected module named `{}` to be Translated but is Reused", + mname)); + } + (Disposition::Reused, true) | + (Disposition::Translated, false) => { + // These are what we would expect. + } + } + } else { + self.tcx.sess.span_err(attr.span, &format!("no module named `{}`", mname)); } } diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index b6ca1460a7d0a..745aa0da82900 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -116,7 +116,7 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe naked(llfn, true); } else if attr.check_name("allocator") { Attribute::NoAlias.apply_llfn( - llvm::AttributePlace::ReturnValue(), llfn); + llvm::AttributePlace::ReturnValue, llfn); } else if attr.check_name("unwind") { unwind(llfn, true); } else if attr.check_name("rustc_allocator_nounwind") { diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs index 0d39db9e10a70..775cf3ac4c934 100644 --- a/src/librustc_trans/back/archive.rs +++ b/src/librustc_trans/back/archive.rs @@ -17,6 +17,7 @@ use std::path::{Path, PathBuf}; use std::ptr; use std::str; +use back::bytecode::RLIB_BYTECODE_EXTENSION; use libc; use llvm::archive_ro::{ArchiveRO, Child}; use llvm::{self, ArchiveKind}; @@ -30,8 +31,7 @@ pub struct ArchiveConfig<'a> { pub lib_search_paths: Vec, } -/// Helper for adding many files to an archive with a single invocation of -/// `ar`. +/// Helper for adding many files to an archive. #[must_use = "must call build() to finish building the archive"] pub struct ArchiveBuilder<'a> { config: ArchiveConfig<'a>, @@ -154,12 +154,9 @@ impl<'a> ArchiveBuilder<'a> { // might be also an extra name suffix let obj_start = format!("{}", name); - // Ignoring all bytecode files, no matter of - // name - let bc_ext = ".bytecode.deflate"; - self.add_archive(rlib, move |fname: &str| { - if fname.ends_with(bc_ext) || fname == METADATA_FILENAME { + // Ignore bytecode/metadata files, no matter the name. + if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { return true } @@ -203,8 +200,8 @@ impl<'a> ArchiveBuilder<'a> { }); } - /// Indicate that the next call to `build` should updates all symbols in - /// the archive (run 'ar s' over it). + /// Indicate that the next call to `build` should update all symbols in + /// the archive (equivalent to running 'ar s' over it). pub fn update_symbols(&mut self) { self.should_update_symbols = true; } diff --git a/src/librustc_trans/back/bytecode.rs b/src/librustc_trans/back/bytecode.rs new file mode 100644 index 0000000000000..55c96322a95c0 --- /dev/null +++ b/src/librustc_trans/back/bytecode.rs @@ -0,0 +1,160 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Management of the encoding of LLVM bytecode into rlibs +//! +//! This module contains the management of encoding LLVM bytecode into rlibs, +//! primarily for the usage in LTO situations. Currently the compiler will +//! unconditionally encode LLVM-IR into rlibs regardless of what's happening +//! elsewhere, so we currently compress the bytecode via deflate to avoid taking +//! up too much space on disk. +//! +//! After compressing the bytecode we then have the rest of the format to +//! basically deal with various bugs in various archive implementations. The +//! format currently is: +//! +//! RLIB LLVM-BYTECODE OBJECT LAYOUT +//! Version 2 +//! Bytes Data +//! 0..10 "RUST_OBJECT" encoded in ASCII +//! 11..14 format version as little-endian u32 +//! 15..19 the length of the module identifier string +//! 20..n the module identifier string +//! n..n+8 size in bytes of deflate compressed LLVM bitcode as +//! little-endian u64 +//! n+9.. compressed LLVM bitcode +//! ? maybe a byte to make this whole thing even length + +use std::io::{Read, Write}; +use std::ptr; +use std::str; + +use flate2::Compression; +use flate2::read::DeflateDecoder; +use flate2::write::DeflateEncoder; + +// This is the "magic number" expected at the beginning of a LLVM bytecode +// object in an rlib. +pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; + +// The version number this compiler will write to bytecode objects in rlibs +pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; + +pub const RLIB_BYTECODE_EXTENSION: &str = "bytecode.encoded"; + +pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { + let mut encoded = Vec::new(); + + // Start off with the magic string + encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); + + // Next up is the version + encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); + + // Next is the LLVM module identifier length + contents + let identifier_len = identifier.len(); + encoded.extend_from_slice(&[ + (identifier_len >> 0) as u8, + (identifier_len >> 8) as u8, + (identifier_len >> 16) as u8, + (identifier_len >> 24) as u8, + ]); + encoded.extend_from_slice(identifier.as_bytes()); + + // Next is the LLVM module deflate compressed, prefixed with its length. We + // don't know its length yet, so fill in 0s + let deflated_size_pos = encoded.len(); + encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + + let before = encoded.len(); + DeflateEncoder::new(&mut encoded, Compression::Fast) + .write_all(bytecode) + .unwrap(); + let after = encoded.len(); + + // Fill in the length we reserved space for before + let bytecode_len = (after - before) as u64; + encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; + encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; + encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; + encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; + encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; + encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; + encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; + encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; + + // If the number of bytes written to the object so far is odd, add a + // padding byte to make it even. This works around a crash bug in LLDB + // (see issue #15950) + if encoded.len() % 2 == 1 { + encoded.push(0); + } + + return encoded +} + +pub struct DecodedBytecode<'a> { + identifier: &'a str, + encoded_bytecode: &'a [u8], +} + +impl<'a> DecodedBytecode<'a> { + pub fn new(data: &'a [u8]) -> Result, String> { + if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { + return Err(format!("magic bytecode prefix not found")) + } + let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; + if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { + return Err(format!("wrong version prefix found in bytecode")) + } + let data = &data[4..]; + if data.len() < 4 { + return Err(format!("bytecode corrupted")) + } + let identifier_len = unsafe { + u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize + }; + let data = &data[4..]; + if data.len() < identifier_len { + return Err(format!("bytecode corrupted")) + } + let identifier = match str::from_utf8(&data[..identifier_len]) { + Ok(s) => s, + Err(_) => return Err(format!("bytecode corrupted")) + }; + let data = &data[identifier_len..]; + if data.len() < 8 { + return Err(format!("bytecode corrupted")) + } + let bytecode_len = unsafe { + u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize + }; + let data = &data[8..]; + if data.len() < bytecode_len { + return Err(format!("bytecode corrupted")) + } + let encoded_bytecode = &data[..bytecode_len]; + + Ok(DecodedBytecode { + identifier, + encoded_bytecode, + }) + } + + pub fn bytecode(&self) -> Vec { + let mut data = Vec::new(); + DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); + return data + } + + pub fn identifier(&self) -> &'a str { + self.identifier + } +} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 39a9ccd8eb9b7..a182d7c6fbe06 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -9,12 +9,14 @@ // except according to those terms. use super::archive::{ArchiveBuilder, ArchiveConfig}; +use super::bytecode::RLIB_BYTECODE_EXTENSION; use super::linker::Linker; use super::command::Command; use super::rpath::RPathConfig; use super::rpath; use metadata::METADATA_FILENAME; use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; +use rustc::session::config::RUST_CGU_EXT; use rustc::session::filesearch; use rustc::session::search_paths::PathKind; use rustc::session::Session; @@ -24,8 +26,8 @@ use {CrateTranslation, CrateInfo}; use rustc::util::common::time; use rustc::util::fs::fix_windows_verbatim_for_gcc; use rustc::hir::def_id::CrateNum; -use rustc_back::tempdir::TempDir; -use rustc_back::{PanicStrategy, RelroLevel}; +use tempdir::TempDir; +use rustc_back::{PanicStrategy, RelroLevel, LinkerFlavor}; use context::get_reloc_model; use llvm; @@ -35,54 +37,18 @@ use std::env; use std::ffi::OsString; use std::fmt; use std::fs::{self, File}; -use std::io::{self, Read, Write, BufWriter}; -use std::mem; +use std::io::{self, Write, BufWriter}; use std::path::{Path, PathBuf}; use std::process::{Output, Stdio}; use std::str; -use flate2::Compression; -use flate2::write::DeflateEncoder; use syntax::attr; /// The LLVM module name containing crate-metadata. This includes a `.` on /// purpose, so it cannot clash with the name of a user-defined module. pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; -/// The name of the crate-metadata object file the compiler generates. Must -/// match up with `METADATA_MODULE_NAME`. -pub const METADATA_OBJ_NAME: &'static str = "crate.metadata.o"; // same as for metadata above, but for allocator shim pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; -pub const ALLOCATOR_OBJ_NAME: &'static str = "crate.allocator.o"; - -// RLIB LLVM-BYTECODE OBJECT LAYOUT -// Version 1 -// Bytes Data -// 0..10 "RUST_OBJECT" encoded in ASCII -// 11..14 format version as little-endian u32 -// 15..22 size in bytes of deflate compressed LLVM bitcode as -// little-endian u64 -// 23.. compressed LLVM bitcode - -// This is the "magic number" expected at the beginning of a LLVM bytecode -// object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; - -// The version number this compiler will write to bytecode objects in rlibs -pub const RLIB_BYTECODE_OBJECT_VERSION: u32 = 1; - -// The offset in bytes the bytecode object format version number can be found at -pub const RLIB_BYTECODE_OBJECT_VERSION_OFFSET: usize = 11; - -// The offset in bytes the size of the compressed bytecode can be found at in -// format version 1 -pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: usize = - RLIB_BYTECODE_OBJECT_VERSION_OFFSET + 4; - -// The offset in bytes the compressed LLVM bytecode can be found at in format -// version 1 -pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize = - RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target, invalid_output_for_target, build_link_meta, out_filename, @@ -201,13 +167,23 @@ pub fn link_binary(sess: &Session, // Remove the temporary object file and metadata if we aren't saving temps if !sess.opts.cg.save_temps { if sess.opts.output_types.should_trans() { - for obj in object_filenames(trans, outputs) { - remove(sess, &obj); + for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { + remove(sess, obj); } } - remove(sess, &outputs.with_extension(METADATA_OBJ_NAME)); - if trans.allocator_module.is_some() { - remove(sess, &outputs.with_extension(ALLOCATOR_OBJ_NAME)); + for obj in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { + remove(sess, obj); + } + if let Some(ref obj) = trans.metadata_module.object { + remove(sess, obj); + } + if let Some(ref allocator) = trans.allocator_module { + if let Some(ref obj) = allocator.object { + remove(sess, obj); + } + if let Some(ref bc) = allocator.bytecode_compressed { + remove(sess, bc); + } } } @@ -269,12 +245,12 @@ pub fn each_linked_rlib(sess: &Session, /// It's unusual for a crate to not participate in LTO. Typically only /// compiler-specific and unstable crates have a reason to not participate in /// LTO. -pub fn ignored_for_lto(info: &CrateInfo, cnum: CrateNum) -> bool { - // `#![no_builtins]` crates don't participate in LTO because the state - // of builtins gets messed up (our crate isn't tagged with no builtins). - // Similarly `#![compiler_builtins]` doesn't participate because we want - // those builtins! - info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum) +pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { + // If our target enables builtin function lowering in LLVM then the + // crates providing these functions don't participate in LTO (e.g. + // no_builtins or compiler builtins crates). + !sess.target.target.options.no_builtins && + (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) } fn link_binary_output(sess: &Session, @@ -282,25 +258,35 @@ fn link_binary_output(sess: &Session, crate_type: config::CrateType, outputs: &OutputFilenames, crate_name: &str) -> Vec { - let objects = object_filenames(trans, outputs); - - for file in &objects { - check_file_is_writeable(file, sess); + for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { + check_file_is_writeable(obj, sess); } - let tmpdir = match TempDir::new("rustc") { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; - let mut out_filenames = vec![]; if outputs.outputs.contains_key(&OutputType::Metadata) { let out_filename = filename_for_metadata(sess, crate_name, outputs); - emit_metadata(sess, trans, &out_filename); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with a `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + let metadata = emit_metadata(sess, trans, &metadata_tmpdir); + if let Err(e) = fs::rename(metadata, &out_filename) { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } out_filenames.push(out_filename); } + let tmpdir = match TempDir::new("rustc") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + if outputs.outputs.should_trans() { let out_filename = out_filename(sess, crate_type, outputs, crate_name); match crate_type { @@ -308,22 +294,14 @@ fn link_binary_output(sess: &Session, link_rlib(sess, trans, RlibFlavor::Normal, - &objects, - outputs, &out_filename, - tmpdir.path()).build(); + &tmpdir).build(); } config::CrateTypeStaticlib => { - link_staticlib(sess, - trans, - outputs, - &objects, - &out_filename, - tmpdir.path()); + link_staticlib(sess, trans, &out_filename, &tmpdir); } _ => { - link_natively(sess, crate_type, &objects, &out_filename, - trans, outputs, tmpdir.path()); + link_natively(sess, crate_type, &out_filename, trans, tmpdir.path()); } } out_filenames.push(out_filename); @@ -336,14 +314,6 @@ fn link_binary_output(sess: &Session, out_filenames } -fn object_filenames(trans: &CrateTranslation, - outputs: &OutputFilenames) - -> Vec { - trans.modules.iter().map(|module| { - outputs.temp_path(OutputType::Object, Some(&module.name)) - }).collect() -} - fn archive_search_paths(sess: &Session) -> Vec { let mut search = Vec::new(); sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { @@ -363,14 +333,23 @@ fn archive_config<'a>(sess: &'a Session, } } -fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, out_filename: &Path) { - let result = fs::File::create(out_filename).and_then(|mut f| { +/// We use a temp directory here to avoid races between concurrent rustc processes, +/// such as builds in the same directory using the same filename for metadata while +/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a +/// directory being searched for `extern crate` (observing an incomplete file). +/// The returned path is the temporary file containing the complete metadata. +fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, tmpdir: &TempDir) + -> PathBuf { + let out_filename = tmpdir.path().join(METADATA_FILENAME); + let result = fs::File::create(&out_filename).and_then(|mut f| { f.write_all(&trans.metadata.raw_data) }); if let Err(e) = result { sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); } + + out_filename } enum RlibFlavor { @@ -387,14 +366,12 @@ enum RlibFlavor { fn link_rlib<'a>(sess: &'a Session, trans: &CrateTranslation, flavor: RlibFlavor, - objects: &[PathBuf], - outputs: &OutputFilenames, out_filename: &Path, - tmpdir: &Path) -> ArchiveBuilder<'a> { - info!("preparing rlib from {:?} to {:?}", objects, out_filename); + tmpdir: &TempDir) -> ArchiveBuilder<'a> { + info!("preparing rlib to {:?}", out_filename); let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); - for obj in objects { + for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { ab.add_file(obj); } @@ -452,67 +429,13 @@ fn link_rlib<'a>(sess: &'a Session, match flavor { RlibFlavor::Normal => { // Instead of putting the metadata in an object file section, rlibs - // contain the metadata in a separate file. We use a temp directory - // here so concurrent builds in the same directory don't try to use - // the same filename for metadata (stomping over one another) - let metadata = tmpdir.join(METADATA_FILENAME); - emit_metadata(sess, trans, &metadata); - ab.add_file(&metadata); + // contain the metadata in a separate file. + ab.add_file(&emit_metadata(sess, trans, tmpdir)); // For LTO purposes, the bytecode of this library is also inserted - // into the archive. If codegen_units > 1, we insert each of the - // bitcode files. - for obj in objects { - // Note that we make sure that the bytecode filename in the - // archive is never exactly 16 bytes long by adding a 16 byte - // extension to it. This is to work around a bug in LLDB that - // would cause it to crash if the name of a file in an archive - // was exactly 16 bytes. - let bc_filename = obj.with_extension("bc"); - let bc_deflated_filename = tmpdir.join({ - obj.with_extension("bytecode.deflate").file_name().unwrap() - }); - - let mut bc_data = Vec::new(); - match fs::File::open(&bc_filename).and_then(|mut f| { - f.read_to_end(&mut bc_data) - }) { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to read bytecode: {}", - e)) - } - - let mut bc_data_deflated = Vec::new(); - DeflateEncoder::new(&mut bc_data_deflated, Compression::Fast) - .write_all(&bc_data).unwrap(); - - let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { - Ok(file) => file, - Err(e) => { - sess.fatal(&format!("failed to create compressed \ - bytecode file: {}", e)) - } - }; - - match write_rlib_bytecode_object_v1(&mut bc_file_deflated, - &bc_data_deflated) { - Ok(()) => {} - Err(e) => { - sess.fatal(&format!("failed to write compressed \ - bytecode: {}", e)); - } - }; - - ab.add_file(&bc_deflated_filename); - - // See the bottom of back::write::run_passes for an explanation - // of when we do and don't keep .#module-name#.bc files around. - let user_wants_numbered_bitcode = - sess.opts.output_types.contains_key(&OutputType::Bitcode) && - sess.opts.cg.codegen_units > 1; - if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { - remove(sess, &bc_filename); - } + // into the archive. + for bytecode in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { + ab.add_file(bytecode); } // After adding all files to the archive, we need to update the @@ -524,8 +447,11 @@ fn link_rlib<'a>(sess: &'a Session, } RlibFlavor::StaticlibBase => { - if trans.allocator_module.is_some() { - ab.add_file(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + let obj = trans.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + ab.add_file(obj); } } } @@ -533,40 +459,6 @@ fn link_rlib<'a>(sess: &'a Session, ab } -fn write_rlib_bytecode_object_v1(writer: &mut Write, - bc_data_deflated: &[u8]) -> io::Result<()> { - let bc_data_deflated_size: u64 = bc_data_deflated.len() as u64; - - writer.write_all(RLIB_BYTECODE_OBJECT_MAGIC)?; - writer.write_all(&[1, 0, 0, 0])?; - writer.write_all(&[ - (bc_data_deflated_size >> 0) as u8, - (bc_data_deflated_size >> 8) as u8, - (bc_data_deflated_size >> 16) as u8, - (bc_data_deflated_size >> 24) as u8, - (bc_data_deflated_size >> 32) as u8, - (bc_data_deflated_size >> 40) as u8, - (bc_data_deflated_size >> 48) as u8, - (bc_data_deflated_size >> 56) as u8, - ])?; - writer.write_all(&bc_data_deflated)?; - - let number_of_bytes_written_so_far = - RLIB_BYTECODE_OBJECT_MAGIC.len() + // magic id - mem::size_of_val(&RLIB_BYTECODE_OBJECT_VERSION) + // version - mem::size_of_val(&bc_data_deflated_size) + // data size field - bc_data_deflated_size as usize; // actual data - - // If the number of bytes written to the object so far is odd, add a - // padding byte to make it even. This works around a crash bug in LLDB - // (see issue #15950) - if number_of_bytes_written_so_far % 2 == 1 { - writer.write_all(&[0])?; - } - - return Ok(()); -} - // Create a static archive // // This is essentially the same thing as an rlib, but it also involves adding @@ -581,15 +473,11 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write, // metadata file). fn link_staticlib(sess: &Session, trans: &CrateTranslation, - outputs: &OutputFilenames, - objects: &[PathBuf], out_filename: &Path, - tempdir: &Path) { + tempdir: &TempDir) { let mut ab = link_rlib(sess, trans, RlibFlavor::StaticlibBase, - objects, - outputs, out_filename, tempdir); let mut all_native_libs = vec![]; @@ -617,7 +505,7 @@ fn link_staticlib(sess: &Session, }); ab.add_rlib(path, &name.as_str(), - sess.lto() && !ignored_for_lto(&trans.crate_info, cnum), + sess.lto() && !ignored_for_lto(sess, &trans.crate_info, cnum), skip_object_files).unwrap(); all_native_libs.extend(trans.crate_info.native_libraries[&cnum].iter().cloned()); @@ -632,31 +520,10 @@ fn link_staticlib(sess: &Session, if !all_native_libs.is_empty() { if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { print_native_static_libs(sess, &all_native_libs); - } else { - // Fallback for backwards compatibility only - print_native_static_libs_legacy(sess, &all_native_libs); } } } -fn print_native_static_libs_legacy(sess: &Session, all_native_libs: &[NativeLibrary]) { - sess.note_without_error("link against the following native artifacts when linking against \ - this static library"); - sess.note_without_error("This list will not be printed by default. \ - Please add --print=native-static-libs if you need this information"); - - for lib in all_native_libs.iter().filter(|l| relevant_lib(sess, l)) { - let name = match lib.kind { - NativeLibraryKind::NativeStaticNobundle | - NativeLibraryKind::NativeUnknown => "library", - NativeLibraryKind::NativeFramework => "framework", - // These are included, no need to print them - NativeLibraryKind::NativeStatic => continue, - }; - sess.note_without_error(&format!("{}: {}", name, lib.name)); - } -} - fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { let lib_args: Vec<_> = all_native_libs.iter() .filter(|l| relevant_lib(sess, l)) @@ -692,14 +559,17 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { // links to all upstream files as well. fn link_natively(sess: &Session, crate_type: config::CrateType, - objects: &[PathBuf], out_filename: &Path, trans: &CrateTranslation, - outputs: &OutputFilenames, tmpdir: &Path) { - info!("preparing {:?} from {:?} to {:?}", crate_type, objects, out_filename); + info!("preparing {:?} to {:?}", crate_type, out_filename); let flavor = sess.linker_flavor(); + // The "binaryen linker" is massively special, so skip everything below. + if flavor == LinkerFlavor::Binaryen { + return link_binaryen(sess, crate_type, out_filename, trans, tmpdir); + } + // The invocations of cc share some flags across platforms let (pname, mut cmd, envs) = get_linker(sess); // This will set PATH on windows @@ -735,7 +605,7 @@ fn link_natively(sess: &Session, { let mut linker = trans.linker_info.to_linker(cmd, &sess); link_args(&mut *linker, sess, crate_type, tmpdir, - objects, out_filename, outputs, trans); + out_filename, trans); cmd = linker.finalize(); } if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { @@ -796,17 +666,18 @@ fn link_natively(sess: &Session, let mut out = output.stderr.clone(); out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); - let msg = "clang: error: unable to execute command: \ - Segmentation fault: 11"; - if !out.contains(msg) { + let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; + let msg_bus = "clang: error: unable to execute command: Bus error: 10"; + if !(out.contains(msg_segv) || out.contains(msg_bus)) { break } - sess.struct_warn("looks like the linker segfaulted when we tried to \ - call it, automatically retrying again") - .note(&format!("{:?}", cmd)) - .note(&out) - .emit(); + warn!( + "looks like the linker segfaulted when we tried to call it, \ + automatically retrying again. cmd = {:?}, out = {}.", + cmd, + out, + ); } match prog { @@ -956,9 +827,7 @@ fn link_args(cmd: &mut Linker, sess: &Session, crate_type: config::CrateType, tmpdir: &Path, - objects: &[PathBuf], out_filename: &Path, - outputs: &OutputFilenames, trans: &CrateTranslation) { // The default library location, we need this to find the runtime. @@ -969,7 +838,7 @@ fn link_args(cmd: &mut Linker, let t = &sess.target.target; cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - for obj in objects { + for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { cmd.add_object(obj); } cmd.output_filename(out_filename); @@ -993,11 +862,16 @@ fn link_args(cmd: &mut Linker, // object file, so we link that in here. if crate_type == config::CrateTypeDylib || crate_type == config::CrateTypeProcMacro { - cmd.add_object(&outputs.with_extension(METADATA_OBJ_NAME)); + if let Some(obj) = trans.metadata_module.object.as_ref() { + cmd.add_object(obj); + } } - if trans.allocator_module.is_some() { - cmd.add_object(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + let obj = trans.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + cmd.add_object(obj); } // Try to strip as much out of the generated object by removing unused @@ -1264,10 +1138,10 @@ fn add_upstream_rust_crates(cmd: &mut Linker, archive.update_symbols(); for f in archive.src_files() { - if f.ends_with("bytecode.deflate") || f == METADATA_FILENAME { - archive.remove_file(&f); - continue - } + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + archive.remove_file(&f); + continue + } } archive.build(); @@ -1324,7 +1198,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) }); - if (!sess.lto() || ignored_for_lto(&trans.crate_info, cnum)) && + if (!sess.lto() || ignored_for_lto(sess, &trans.crate_info, cnum)) && crate_type != config::CrateTypeDylib && !skip_native { cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); @@ -1342,7 +1216,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, let mut any_objects = false; for f in archive.src_files() { - if f.ends_with("bytecode.deflate") || f == METADATA_FILENAME { + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { archive.remove_file(&f); continue } @@ -1350,11 +1224,23 @@ fn add_upstream_rust_crates(cmd: &mut Linker, let canonical = f.replace("-", "_"); let canonical_name = name.replace("-", "_"); + // Look for `.rcgu.o` at the end of the filename to conclude + // that this is a Rust-related object file. + fn looks_like_rust(s: &str) -> bool { + let path = Path::new(s); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + return false + } + let ext2 = path.file_stem() + .and_then(|s| Path::new(s).extension()) + .and_then(|s| s.to_str()); + ext2 == Some(RUST_CGU_EXT) + } + let is_rust_object = - canonical.starts_with(&canonical_name) && { - let num = &f[name.len()..f.len() - 2]; - num.len() > 0 && num[1..].parse::().is_ok() - }; + canonical.starts_with(&canonical_name) && + looks_like_rust(&f); // If we've been requested to skip all native object files // (those not generated by the rust compiler) then we can skip @@ -1365,8 +1251,10 @@ fn add_upstream_rust_crates(cmd: &mut Linker, // file, then we don't need the object file as it's part of the // LTO module. Note that `#![no_builtins]` is excluded from LTO, // though, so we let that object file slide. - let skip_because_lto = sess.lto() && is_rust_object && - !trans.crate_info.is_no_builtins.contains(&cnum); + let skip_because_lto = sess.lto() && + is_rust_object && + (sess.target.target.options.no_builtins || + !trans.crate_info.is_no_builtins.contains(&cnum)); if skip_because_cfg_say_so || skip_because_lto { archive.remove_file(&f); @@ -1481,3 +1369,30 @@ fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { None => true, } } + +/// For now "linking with binaryen" is just "move the one module we generated in +/// the backend to the final output" +/// +/// That is, all the heavy lifting happens during the `back::write` phase. Here +/// we just clean up after that. +/// +/// Note that this is super temporary and "will not survive the night", this is +/// guaranteed to get removed as soon as a linker for wasm exists. This should +/// not be used for anything other than wasm. +fn link_binaryen(sess: &Session, + _crate_type: config::CrateType, + out_filename: &Path, + trans: &CrateTranslation, + _tmpdir: &Path) { + assert!(trans.allocator_module.is_none()); + assert_eq!(trans.modules.len(), 1); + + let object = trans.modules[0].object.as_ref().expect("object must exist"); + let res = fs::hard_link(object, out_filename) + .or_else(|_| fs::copy(object, out_filename).map(|_| ())); + if let Err(e) = res { + sess.fatal(&format!("failed to create `{}`: {}", + out_filename.display(), + e)); + } +} diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 99422bf8c90af..aa29c3cc12058 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -77,6 +77,9 @@ impl LinkerInfo { is_ld: true, }) as Box } + LinkerFlavor::Binaryen => { + panic!("can't instantiate binaryen linker") + } } } } @@ -747,7 +750,7 @@ impl<'a> Linker for EmLinker<'a> { fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { let mut symbols = Vec::new(); - let export_threshold = symbol_export::threshold(tcx); + let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for &(ref name, _, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { if level.is_below_threshold(export_threshold) { symbols.push(name.clone()); diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index aa13e4aa196ee..48c3fd638c36b 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -8,27 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use back::link; -use back::write; +use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; use back::symbol_export; -use rustc::session::config; +use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; +use back::write; use errors::{FatalError, Handler}; -use llvm; use llvm::archive_ro::ArchiveRO; use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm; +use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::exported_symbols::SymbolExportLevel; +use rustc::session::config; use rustc::util::common::time; -use rustc::util::common::path2cstr; -use rustc::hir::def_id::LOCAL_CRATE; -use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; +use time_graph::Timeline; +use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource}; use libc; -use flate2::read::DeflateDecoder; -use std::io::Read; use std::ffi::CString; -use std::path::Path; -use std::ptr::read_unaligned; +use std::slice; +use std::sync::Arc; pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { match crate_type { @@ -42,31 +41,79 @@ pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { } } -pub fn run(cgcx: &CodegenContext, - diag_handler: &Handler, - llmod: ModuleRef, - tm: TargetMachineRef, - config: &ModuleConfig, - temp_no_opt_bc_filename: &Path) -> Result<(), FatalError> { - if cgcx.opts.cg.prefer_dynamic { - diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") - .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ - supported with LTO") - .emit(); - return Err(FatalError) +pub enum LtoModuleTranslation { + Fat { + module: Option, + _serialized_bitcode: Vec, + }, + + Thin(ThinModule), +} + +impl LtoModuleTranslation { + pub fn name(&self) -> &str { + match *self { + LtoModuleTranslation::Fat { .. } => "everything", + LtoModuleTranslation::Thin(ref m) => m.name(), + } } - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ - static library outputs"); - return Err(e) + /// Optimize this module within the given codegen context. + /// + /// This function is unsafe as it'll return a `ModuleTranslation` still + /// points to LLVM data structures owned by this `LtoModuleTranslation`. + /// It's intended that the module returned is immediately code generated and + /// dropped, and then this LTO module is dropped. + pub unsafe fn optimize(&mut self, + cgcx: &CodegenContext, + timeline: &mut Timeline) + -> Result + { + match *self { + LtoModuleTranslation::Fat { ref mut module, .. } => { + let trans = module.take().unwrap(); + let config = cgcx.config(trans.kind); + let llmod = trans.llvm().unwrap().llmod; + let tm = trans.llvm().unwrap().tm; + run_pass_manager(cgcx, tm, llmod, config, false); + timeline.record("fat-done"); + Ok(trans) + } + LtoModuleTranslation::Thin(ref mut thin) => thin.optimize(cgcx, timeline), } } - let export_threshold = - symbol_export::crates_export_threshold(&cgcx.crate_types); + /// A "guage" of how costly it is to optimize this module, used to sort + /// biggest modules first. + pub fn cost(&self) -> u64 { + match *self { + // Only one module with fat LTO, so the cost doesn't matter. + LtoModuleTranslation::Fat { .. } => 0, + LtoModuleTranslation::Thin(ref m) => m.cost(), + } + } +} + +pub enum LTOMode { + WholeCrateGraph, + JustThisCrate, +} + +pub fn run(cgcx: &CodegenContext, + modules: Vec, + mode: LTOMode, + timeline: &mut Timeline) + -> Result, FatalError> +{ + let diag_handler = cgcx.create_diag_handler(); + let export_threshold = match mode { + LTOMode::WholeCrateGraph => { + symbol_export::crates_export_threshold(&cgcx.crate_types) + } + LTOMode::JustThisCrate => { + SymbolExportLevel::Rust + } + }; let symbol_filter = &|&(ref name, _, level): &(String, _, SymbolExportLevel)| { if level.is_below_threshold(export_threshold) { @@ -78,111 +125,305 @@ pub fn run(cgcx: &CodegenContext, } }; - let mut symbol_white_list: Vec = cgcx.exported_symbols[&LOCAL_CRATE] + let mut symbol_white_list = cgcx.exported_symbols[&LOCAL_CRATE] .iter() .filter_map(symbol_filter) - .collect(); - - // For each of our upstream dependencies, find the corresponding rlib and - // load the bitcode from the archive. Then merge it into the current LLVM - // module that we've got. - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - symbol_white_list.extend( - cgcx.exported_symbols[&cnum] - .iter() - .filter_map(symbol_filter)); - - let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let bytecodes = archive.iter().filter_map(|child| { - child.ok().and_then(|c| c.name().map(|name| (name, c))) - }).filter(|&(name, _)| name.ends_with("bytecode.deflate")); - for (name, data) in bytecodes { - let bc_encoded = data.data(); - - let bc_decoded = if is_versioned_bytecode_format(bc_encoded) { - time(cgcx.time_passes, &format!("decode {}", name), || { - // Read the version - let version = extract_bytecode_format_version(bc_encoded); - - if version == 1 { - // The only version existing so far - let data_size = extract_compressed_bytecode_size_v1(bc_encoded); - let compressed_data = &bc_encoded[ - link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET.. - (link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as usize)]; - - let mut inflated = Vec::new(); - let res = DeflateDecoder::new(compressed_data) - .read_to_end(&mut inflated); - if res.is_err() { - let msg = format!("failed to decompress bc of `{}`", - name); - Err(diag_handler.fatal(&msg)) - } else { - Ok(inflated) - } - } else { - Err(diag_handler.fatal(&format!("Unsupported bytecode format version {}", - version))) - } - })? - } else { - time(cgcx.time_passes, &format!("decode {}", name), || { - // the object must be in the old, pre-versioning format, so - // simply inflate everything and let LLVM decide if it can - // make sense of it - let mut inflated = Vec::new(); - let res = DeflateDecoder::new(bc_encoded) - .read_to_end(&mut inflated); - if res.is_err() { - let msg = format!("failed to decompress bc of `{}`", - name); - Err(diag_handler.fatal(&msg)) - } else { - Ok(inflated) + .collect::>(); + timeline.record("whitelist"); + info!("{} symbols to preserve in this crate", symbol_white_list.len()); + + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, find the corresponding rlib and load the bitcode + // from the archive. + // + // We save off all the bytecode and LLVM module ids for later processing + // with either fat or thin LTO + let mut upstream_modules = Vec::new(); + if let LTOMode::WholeCrateGraph = mode { + if cgcx.opts.cg.prefer_dynamic { + diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ + supported with LTO") + .emit(); + return Err(FatalError) + } + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ + static library outputs"); + return Err(e) + } + } + + for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + symbol_white_list.extend( + cgcx.exported_symbols[&cnum] + .iter() + .filter_map(symbol_filter)); + + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); + let bytecodes = archive.iter().filter_map(|child| { + child.ok().and_then(|c| c.name().map(|name| (name, c))) + }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); + for (name, data) in bytecodes { + info!("adding bytecode {}", name); + let bc_encoded = data.data(); + + let (bc, id) = time(cgcx.time_passes, &format!("decode {}", name), || { + match DecodedBytecode::new(bc_encoded) { + Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), + Err(e) => Err(diag_handler.fatal(&e)), } - })? - }; + })?; + let bc = SerializedModule::FromRlib(bc); + upstream_modules.push((bc, CString::new(id).unwrap())); + } + timeline.record(&format!("load: {}", path.display())); + } + } - let ptr = bc_decoded.as_ptr(); - debug!("linking {}", name); - time(cgcx.time_passes, &format!("ll link {}", name), || unsafe { - if llvm::LLVMRustLinkInExternalBitcode(llmod, - ptr as *const libc::c_char, - bc_decoded.len() as libc::size_t) { - Ok(()) - } else { - let msg = format!("failed to load bc of `{}`", name); - Err(write::llvm_err(&diag_handler, msg)) - } - })?; + let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::>(); + match mode { + LTOMode::WholeCrateGraph if !cgcx.thinlto => { + fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) + } + _ => { + thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) } } +} + +fn fat_lto(cgcx: &CodegenContext, + diag_handler: &Handler, + mut modules: Vec, + mut serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + info!("going for a fat lto"); - // Internalize everything but the exported symbols of the current module - let arr: Vec<*const libc::c_char> = symbol_white_list.iter() - .map(|c| c.as_ptr()) - .collect(); - let ptr = arr.as_ptr(); + // Find the "costliest" module and merge everything into that codegen unit. + // All the other modules will be serialized and reparsed into the new + // context, so this hopefully avoids serializing and parsing the largest + // codegen unit. + // + // Additionally use a regular module as the base here to ensure that various + // file copy operations in the backend work correctly. The only other kind + // of module here should be an allocator one, and if your crate is smaller + // than the allocator module then the size doesn't really matter anyway. + let (_, costliest_module) = modules.iter() + .enumerate() + .filter(|&(_, module)| module.kind == ModuleKind::Regular) + .map(|(i, module)| { + let cost = unsafe { + llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) + }; + (cost, i) + }) + .max() + .expect("must be trans'ing at least one module"); + let module = modules.remove(costliest_module); + let llmod = module.llvm().expect("can't lto pre-translated modules").llmod; + info!("using {:?} as a base module", module.llmod_id); + + // For all other modules we translated we'll need to link them into our own + // bitcode. All modules were translated in their own LLVM context, however, + // and we want to move everything to the same LLVM context. Currently the + // way we know of to do that is to serialize them to a string and them parse + // them later. Not great but hey, that's why it's "fat" LTO, right? + for module in modules { + let llvm = module.llvm().expect("can't lto pre-translated modules"); + let buffer = ModuleBuffer::new(llvm.llmod); + let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); + serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); + } + + // For all serialized bitcode files we parse them and link them in as we did + // above, this is all mostly handled in C++. Like above, though, we don't + // know much about the memory management here so we err on the side of being + // save and persist everything with the original module. + let mut serialized_bitcode = Vec::new(); + for (bc_decoded, name) in serialized_modules { + info!("linking {:?}", name); + time(cgcx.time_passes, &format!("ll link {:?}", name), || unsafe { + let data = bc_decoded.data(); + if llvm::LLVMRustLinkInExternalBitcode(llmod, + data.as_ptr() as *const libc::c_char, + data.len() as libc::size_t) { + Ok(()) + } else { + let msg = format!("failed to load bc of {:?}", name); + Err(write::llvm_err(&diag_handler, msg)) + } + })?; + timeline.record(&format!("link {:?}", name)); + serialized_bitcode.push(bc_decoded); + } + cgcx.save_temp_bitcode(&module, "lto.input"); + + // Internalize everything that *isn't* in our whitelist to help strip out + // more modules and such unsafe { + let ptr = symbol_white_list.as_ptr(); llvm::LLVMRustRunRestrictionPass(llmod, ptr as *const *const libc::c_char, - arr.len() as libc::size_t); + symbol_white_list.len() as libc::size_t); + cgcx.save_temp_bitcode(&module, "lto.after-restriction"); } if cgcx.no_landing_pads { unsafe { llvm::LLVMRustMarkAllFunctionsNounwind(llmod); } + cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); } + timeline.record("passes"); - if cgcx.opts.cg.save_temps { - let cstr = path2cstr(temp_no_opt_bc_filename); - unsafe { - llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + Ok(vec![LtoModuleTranslation::Fat { + module: Some(module), + _serialized_bitcode: serialized_bitcode, + }]) +} + +/// Prepare "thin" LTO to get run on these modules. +/// +/// The general structure of ThinLTO is quite different from the structure of +/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into +/// one giant LLVM module, and then we run more optimization passes over this +/// big module after internalizing most symbols. Thin LTO, on the other hand, +/// avoid this large bottleneck through more targeted optimization. +/// +/// At a high level Thin LTO looks like: +/// +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. +/// +/// The summary for each module is intended to be quite cheap, and the global +/// index is relatively quite cheap to create as well. As a result, the goal of +/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more +/// situations. For example one cheap optimization is that we can parallelize +/// all codegen modules, easily making use of all the cores on a machine. +/// +/// With all that in mind, the function here is designed at specifically just +/// calculating the *index* for ThinLTO. This index will then be shared amongst +/// all of the `LtoModuleTranslation` units returned below and destroyed once +/// they all go out of scope. +fn thin_lto(diag_handler: &Handler, + modules: Vec, + serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + unsafe { + info!("going for that thin, thin LTO"); + + let mut thin_buffers = Vec::new(); + let mut module_names = Vec::new(); + let mut thin_modules = Vec::new(); + + // FIXME: right now, like with fat LTO, we serialize all in-memory + // modules before working with them and ThinLTO. We really + // shouldn't do this, however, and instead figure out how to + // extract a summary from an in-memory module and then merge that + // into the global index. It turns out that this loop is by far + // the most expensive portion of this small bit of global + // analysis! + for (i, module) in modules.iter().enumerate() { + info!("local module: {} - {}", i, module.llmod_id); + let llvm = module.llvm().expect("can't lto pretranslated module"); + let name = CString::new(module.llmod_id.clone()).unwrap(); + let buffer = ThinBuffer::new(llvm.llmod); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: buffer.data().as_ptr(), + len: buffer.data().len(), + }); + thin_buffers.push(buffer); + module_names.push(name); + timeline.record(&module.llmod_id); + } + + // FIXME: All upstream crates are deserialized internally in the + // function below to extract their summary and modules. Note that + // unlike the loop above we *must* decode and/or read something + // here as these are all just serialized files on disk. An + // improvement, however, to make here would be to store the + // module summary separately from the actual module itself. Right + // now this is store in one large bitcode file, and the entire + // file is deflate-compressed. We could try to bypass some of the + // decompression by storing the index uncompressed and only + // lazily decompressing the bytecode if necessary. + // + // Note that truly taking advantage of this optimization will + // likely be further down the road. We'd have to implement + // incremental ThinLTO first where we could actually avoid + // looking at upstream modules entirely sometimes (the contents, + // we must always unconditionally look at the index). + let mut serialized = Vec::new(); + for (module, name) in serialized_modules { + info!("foreign module {:?}", name); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: module.data().as_ptr(), + len: module.data().len(), + }); + serialized.push(module); + module_names.push(name); } + + // Delegate to the C++ bindings to create some data here. Once this is a + // tried-and-true interface we may wish to try to upstream some of this + // to LLVM itself, right now we reimplement a lot of what they do + // upstream... + let data = llvm::LLVMRustCreateThinLTOData( + thin_modules.as_ptr(), + thin_modules.len() as u32, + symbol_white_list.as_ptr(), + symbol_white_list.len() as u32, + ); + if data.is_null() { + let msg = format!("failed to prepare thin LTO context"); + return Err(write::llvm_err(&diag_handler, msg)) + } + let data = ThinData(data); + info!("thin LTO data created"); + timeline.record("data"); + + // Throw our data in an `Arc` as we'll be sharing it across threads. We + // also put all memory referenced by the C++ data (buffers, ids, etc) + // into the arc as well. After this we'll create a thin module + // translation per module in this data. + let shared = Arc::new(ThinShared { + data, + thin_buffers, + serialized_modules: serialized, + module_names, + }); + Ok((0..shared.module_names.len()).map(|i| { + LtoModuleTranslation::Thin(ThinModule { + shared: shared.clone(), + idx: i, + }) + }).collect()) } +} +fn run_pass_manager(cgcx: &CodegenContext, + tm: TargetMachineRef, + llmod: ModuleRef, + config: &ModuleConfig, + thin: bool) { // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. // @@ -196,10 +437,33 @@ pub fn run(cgcx: &CodegenContext, assert!(!pass.is_null()); llvm::LLVMRustAddPass(pm, pass); - with_llvm_pmb(llmod, config, &mut |b| { - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, - /* Internalize = */ False, - /* RunInliner = */ True); + // When optimizing for LTO we don't actually pass in `-O0`, but we force + // it to always happen at least with `-O1`. + // + // With ThinLTO we mess around a lot with symbol visibility in a way + // that will actually cause linking failures if we optimize at O0 which + // notable is lacking in dead code elimination. To ensure we at least + // get some optimizations and correctly link we forcibly switch to `-O1` + // to get dead code elimination. + // + // Note that in general this shouldn't matter too much as you typically + // only turn on ThinLTO when you're compiling with optimizations + // otherwise. + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + let opt_level = match opt_level { + llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less, + level => level, + }; + with_llvm_pmb(llmod, config, opt_level, &mut |b| { + if thin { + if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { + panic!("this version of LLVM does not support ThinLTO"); + } + } else { + llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, + /* Internalize = */ False, + /* RunInliner = */ True); + } }); let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); @@ -212,25 +476,207 @@ pub fn run(cgcx: &CodegenContext, llvm::LLVMDisposePassManager(pm); } debug!("lto done"); - Ok(()) } -fn is_versioned_bytecode_format(bc: &[u8]) -> bool { - let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len(); - return bc.len() > magic_id_byte_count && - &bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC; +pub enum SerializedModule { + Local(ModuleBuffer), + FromRlib(Vec), +} + +impl SerializedModule { + fn data(&self) -> &[u8] { + match *self { + SerializedModule::Local(ref m) => m.data(), + SerializedModule::FromRlib(ref m) => m, + } + } +} + +pub struct ModuleBuffer(*mut llvm::ModuleBuffer); + +unsafe impl Send for ModuleBuffer {} +unsafe impl Sync for ModuleBuffer {} + +impl ModuleBuffer { + pub fn new(m: ModuleRef) -> ModuleBuffer { + ModuleBuffer(unsafe { + llvm::LLVMRustModuleBufferCreate(m) + }) + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustModuleBufferPtr(self.0); + let len = llvm::LLVMRustModuleBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ModuleBuffer { + fn drop(&mut self) { + unsafe { llvm::LLVMRustModuleBufferFree(self.0); } + } +} + +pub struct ThinModule { + shared: Arc, + idx: usize, +} + +struct ThinShared { + data: ThinData, + thin_buffers: Vec, + serialized_modules: Vec, + module_names: Vec, +} + +struct ThinData(*mut llvm::ThinLTOData); + +unsafe impl Send for ThinData {} +unsafe impl Sync for ThinData {} + +impl Drop for ThinData { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustFreeThinLTOData(self.0); + } + } +} + +pub struct ThinBuffer(*mut llvm::ThinLTOBuffer); + +unsafe impl Send for ThinBuffer {} +unsafe impl Sync for ThinBuffer {} + +impl ThinBuffer { + pub fn new(m: ModuleRef) -> ThinBuffer { + unsafe { + let buffer = llvm::LLVMRustThinLTOBufferCreate(m); + ThinBuffer(buffer) + } + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } } -fn extract_bytecode_format_version(bc: &[u8]) -> u32 { - let pos = link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET; - let byte_data = &bc[pos..pos + 4]; - let data = unsafe { read_unaligned(byte_data.as_ptr() as *const u32) }; - u32::from_le(data) +impl Drop for ThinBuffer { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustThinLTOBufferFree(self.0); + } + } } -fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 { - let pos = link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET; - let byte_data = &bc[pos..pos + 8]; - let data = unsafe { read_unaligned(byte_data.as_ptr() as *const u64) }; - u64::from_le(data) +impl ThinModule { + fn name(&self) -> &str { + self.shared.module_names[self.idx].to_str().unwrap() + } + + fn cost(&self) -> u64 { + // Yes, that's correct, we're using the size of the bytecode as an + // indicator for how costly this codegen unit is. + self.data().len() as u64 + } + + fn data(&self) -> &[u8] { + let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); + a.unwrap_or_else(|| { + let len = self.shared.thin_buffers.len(); + self.shared.serialized_modules[self.idx - len].data() + }) + } + + unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) + -> Result + { + let diag_handler = cgcx.create_diag_handler(); + let tm = (cgcx.tm_factory)().map_err(|e| { + write::llvm_err(&diag_handler, e) + })?; + + // Right now the implementation we've got only works over serialized + // modules, so we create a fresh new LLVM context and parse the module + // into that context. One day, however, we may do this for upstream + // crates but for locally translated modules we may be able to reuse + // that LLVM Context and Module. + let llcx = llvm::LLVMContextCreate(); + let llmod = llvm::LLVMRustParseBitcodeForThinLTO( + llcx, + self.data().as_ptr(), + self.data().len(), + self.shared.module_names[self.idx].as_ptr(), + ); + assert!(!llmod.is_null()); + let mtrans = ModuleTranslation { + source: ModuleSource::Translated(ModuleLlvm { + llmod, + llcx, + tm, + }), + llmod_id: self.name().to_string(), + name: self.name().to_string(), + kind: ModuleKind::Regular, + }; + cgcx.save_temp_bitcode(&mtrans, "thin-lto-input"); + + // Like with "fat" LTO, get some better optimizations if landing pads + // are disabled by removing all landing pads. + if cgcx.no_landing_pads { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-nounwind"); + timeline.record("nounwind"); + } + + // Up next comes the per-module local analyses that we do for Thin LTO. + // Each of these functions is basically copied from the LLVM + // implementation and then tailored to suit this implementation. Ideally + // each of these would be supported by upstream LLVM but that's perhaps + // a patch for another day! + // + // You can find some more comments about these functions in the LLVM + // bindings we've got (currently `PassWrapper.cpp`) + if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rename"); + timeline.record("rename"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-resolve"); + timeline.record("resolve"); + if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-internalize"); + timeline.record("internalize"); + if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-import"); + timeline.record("import"); + + // Alright now that we've done everything related to the ThinLTO + // analysis it's time to run some optimizations! Here we use the same + // `run_pass_manager` as the "fat" LTO above except that we tell it to + // populate a thin-specific pass manager, which presumably LLVM treats a + // little differently. + info!("running thin lto passes over {}", mtrans.name); + let config = cgcx.config(mtrans.kind); + run_pass_manager(cgcx, tm, llmod, config, true); + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); + timeline.record("thin-done"); + Ok(mtrans) + } } diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index e1f97e2c923db..fa6fe2e9e93ef 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -21,6 +21,8 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc::util::nodemap::FxHashMap; use rustc_allocator::ALLOCATOR_METHODS; +use rustc_back::LinkerFlavor; +use syntax::attr; pub type ExportedSymbols = FxHashMap< CrateNum, @@ -34,7 +36,7 @@ pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { pub fn metadata_symbol_name(tcx: TyCtxt) -> String { format!("rust_metadata_{}_{}", tcx.crate_name(LOCAL_CRATE), - tcx.crate_disambiguator(LOCAL_CRATE)) + tcx.crate_disambiguator(LOCAL_CRATE).to_fingerprint().to_hex()) } fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { @@ -59,7 +61,7 @@ pub fn crates_export_threshold(crate_types: &[config::CrateType]) } } -pub fn provide_local(providers: &mut Providers) { +pub fn provide(providers: &mut Providers) { providers.exported_symbol_ids = |tcx, cnum| { let export_threshold = threshold(tcx); Rc::new(tcx.exported_symbols(cnum) @@ -77,11 +79,7 @@ pub fn provide_local(providers: &mut Providers) { }; providers.is_exported_symbol = |tcx, id| { - // FIXME(#42293) needs red/green to not break a bunch of incremental - // tests - tcx.dep_graph.with_ignore(|| { - tcx.exported_symbol_ids(id.krate).contains(&id) - }) + tcx.exported_symbol_ids(id.krate).contains(&id) }; providers.exported_symbols = |tcx, cnum| { @@ -128,6 +126,12 @@ pub fn provide_local(providers: &mut Providers) { None, SymbolExportLevel::Rust)); } + + // Sort so we get a stable incr. comp. hash. + local_crate.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| { + name1.cmp(name2) + }); + Arc::new(local_crate) }; } @@ -151,12 +155,26 @@ pub fn provide_extern(providers: &mut Providers) { let special_runtime_crate = tcx.is_panic_runtime(cnum) || tcx.is_compiler_builtins(cnum); - let crate_exports = tcx + // Dealing with compiler-builtins and wasm right now is super janky. + // There's no linker! As a result we need all of the compiler-builtins + // exported symbols to make their way through all the way to the end of + // compilation. We want to make sure that LLVM doesn't remove them as + // well because we may or may not need them in the final output + // artifact. For now just force them to always get exported at the C + // layer, and we'll worry about gc'ing them later. + let compiler_builtins_and_binaryen = + tcx.is_compiler_builtins(cnum) && + tcx.sess.linker_flavor() == LinkerFlavor::Binaryen; + + let mut crate_exports: Vec<_> = tcx .exported_symbol_ids(cnum) .iter() .map(|&def_id| { let name = tcx.symbol_name(Instance::mono(tcx, def_id)); - let export_level = if special_runtime_crate { + let export_level = if compiler_builtins_and_binaryen && + tcx.contains_extern_indicator(def_id) { + SymbolExportLevel::C + } else if special_runtime_crate { // We can probably do better here by just ensuring that // it has hidden visibility rather than public // visibility, as this is primarily here to ensure it's @@ -179,12 +197,25 @@ pub fn provide_extern(providers: &mut Providers) { }) .collect(); + // Sort so we get a stable incr. comp. hash. + crate_exports.sort_unstable_by(|&(ref name1, ..), &(ref name2, ..)| { + name1.cmp(name2) + }); + Arc::new(crate_exports) }; } fn export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { - if tcx.contains_extern_indicator(sym_def_id) { + // We export anything that's not mangled at the "C" layer as it probably has + // to do with ABI concerns. We do not, however, apply such treatment to + // special symbols in the standard library for various plumbing between + // core/std/allocators/etc. For example symbols used to hook up allocation + // are not considered for export + let is_extern = tcx.contains_extern_indicator(sym_def_id); + let std_internal = attr::contains_name(&tcx.get_attrs(sym_def_id), + "rustc_std_internal_symbol"); + if is_extern && !std_internal { SymbolExportLevel::C } else { SymbolExportLevel::Rust diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index 306071223fc2c..695950e672785 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -98,8 +98,10 @@ //! DefPaths which are much more robust in the face of changes to the code base. use monomorphize::Instance; +use trans_item::{BaseTransItemExt, InstantiationMode}; use rustc::middle::weak_lang_items; +use rustc::middle::trans::TransItem; use rustc::hir::def_id::DefId; use rustc::hir::map as hir_map; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -150,7 +152,10 @@ pub fn provide(providers: &mut Providers) { fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // the DefId of the item this name is for - def_id: Option, + def_id: DefId, + + // instance this name will be for + instance: Instance<'tcx>, // type of the item, without any generic // parameters substituted; this is @@ -160,7 +165,7 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // values for generic type parameters, // if any. - substs: Option<&'tcx Substs<'tcx>>) + substs: &'tcx Substs<'tcx>) -> u64 { debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); @@ -170,7 +175,7 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // the main symbol name is not necessarily unique; hash in the // compiler's internal def-path, guaranteeing each symbol has a // truly unique path - hasher.hash(def_id.map(|def_id| tcx.def_path_hash(def_id))); + hasher.hash(tcx.def_path_hash(def_id)); // Include the main item-type. Note that, in this case, the // assertions about `needs_subst` may not hold, but this item-type @@ -186,19 +191,36 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } // also include any type parameters (for generic items) - if let Some(substs) = substs { - 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(tcx.crate_name.as_str()); - hasher.hash(tcx.sess.local_crate_disambiguator().as_str()); + assert!(!substs.has_erasable_regions()); + assert!(!substs.needs_subst()); + substs.visit_with(&mut hasher); + + let mut avoid_cross_crate_conflicts = false; + + // 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() { + avoid_cross_crate_conflicts = true; + } + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match TransItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => { + avoid_cross_crate_conflicts = true; } + _ => {} + } + + if avoid_cross_crate_conflicts { + hasher.hash(tcx.crate_name.as_str()); + hasher.hash(tcx.sess.local_crate_disambiguator()); } }); @@ -309,7 +331,7 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance // and should not matter anyhow. let instance_ty = tcx.erase_regions(&instance_ty); - let hash = get_symbol_hash(tcx, Some(def_id), instance_ty, Some(substs)); + let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index ef6bf2504f312..cb883e0349f31 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -8,43 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use back::lto; +use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; +use back::lto::{self, ModuleBuffer, ThinBuffer}; use back::link::{self, get_linker, remove}; use back::linker::LinkerInfo; use back::symbol_export::ExportedSymbols; +use base; +use consts; use rustc_incremental::{save_trans_partition, in_incr_comp_dir}; -use rustc::dep_graph::DepGraph; +use rustc::dep_graph::{DepGraph, WorkProductFileKind}; use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses, AllPasses, Sanitizer}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; -use time_graph::{self, TimeGraph}; +use rustc_back::LinkerFlavor; +use time_graph::{self, TimeGraph, Timeline}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; -use llvm::SMDiagnosticRef; +use llvm::{SMDiagnosticRef, ContextRef}; use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; use CrateInfo; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry}; use rustc::util::fs::{link_or_copy, rename_or_copy_remove}; -use errors::{self, Handler, Level, DiagnosticBuilder, FatalError}; +use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use errors::emitter::{Emitter}; use syntax::attr; use syntax::ext::hygiene::Mark; use syntax_pos::MultiSpan; use syntax_pos::symbol::Symbol; +use type_::Type; use context::{is_pie_binary, get_reloc_model}; use jobserver::{Client, Acquired}; use rustc_demangle; use std::any::Any; -use std::ffi::CString; -use std::fmt; -use std::fs; +use std::ffi::{CString, CStr}; +use std::fs::{self, File}; use std::io; -use std::io::Write; +use std::io::{Read, Write}; +use std::mem; use std::path::{Path, PathBuf}; use std::str; use std::sync::Arc; @@ -72,6 +77,13 @@ pub const CODE_GEN_MODEL_ARGS : [(&'static str, llvm::CodeModel); 5] = [ ("large", llvm::CodeModel::Large), ]; +pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ + ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), + ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), + ("initial-exec", llvm::ThreadLocalMode::InitialExec), + ("local-exec", llvm::ThreadLocalMode::LocalExec), +]; + pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { match llvm::last_error() { Some(err) => handler.fatal(&format!("{}: {}", msg, err)), @@ -143,6 +155,14 @@ fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { } pub fn create_target_machine(sess: &Session) -> TargetMachineRef { + target_machine_factory(sess)().unwrap_or_else(|err| { + panic!(llvm_err(sess.diagnostic(), err)) + }) +} + +pub fn target_machine_factory(sess: &Session) + -> Arc Result + Send + Sync> +{ let reloc_model = get_reloc_model(sess); let opt_level = get_llvm_opt_level(sess.opts.optimize); @@ -161,53 +181,58 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef { Some(x) => x.1, _ => { sess.err(&format!("{:?} is not a valid code model", - sess.opts - .cg - .code_model)); + code_model_arg)); sess.abort_if_errors(); bug!(); } }; + let singlethread = sess.target.target.options.singlethread; + let triple = &sess.target.target.llvm_target; - let tm = unsafe { - let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.target.options.cpu - }; - let cpu = CString::new(cpu.as_bytes()).unwrap(); - let features = CString::new(target_feature(sess).as_bytes()).unwrap(); - llvm::LLVMRustCreateTargetMachine( - triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), - code_model, - reloc_model, - opt_level, - use_softfp, - is_pie_binary(sess), - ffunction_sections, - fdata_sections, - ) + let triple = CString::new(triple.as_bytes()).unwrap(); + let cpu = match sess.opts.cg.target_cpu { + Some(ref s) => &**s, + None => &*sess.target.target.options.cpu }; + let cpu = CString::new(cpu.as_bytes()).unwrap(); + let features = CString::new(target_feature(sess).as_bytes()).unwrap(); + let is_pie_binary = is_pie_binary(sess); + let trap_unreachable = sess.target.target.options.trap_unreachable; + + Arc::new(move || { + let tm = unsafe { + llvm::LLVMRustCreateTargetMachine( + triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), + code_model, + reloc_model, + opt_level, + use_softfp, + is_pie_binary, + ffunction_sections, + fdata_sections, + trap_unreachable, + singlethread, + ) + }; - if tm.is_null() { - let msg = format!("Could not create LLVM TargetMachine for triple: {}", - triple); - panic!(llvm_err(sess.diagnostic(), msg)); - } else { - return tm; - }; + if tm.is_null() { + Err(format!("Could not create LLVM TargetMachine for triple: {}", + triple.to_str().unwrap())) + } else { + Ok(tm) + } + }) } - /// Module-specific configuration for `optimize_and_codegen`. pub struct ModuleConfig { /// Names of additional optimization passes to run. passes: Vec, /// Some(level) to optimize at a certain level, or None to run /// absolutely no optimizations (used for the metadata module). - opt_level: Option, + pub opt_level: Option, /// Some(level) to optimize binary size, or None to not affect program size. opt_size: Option, @@ -215,6 +240,7 @@ pub struct ModuleConfig { // Flags indicating which outputs to produce. emit_no_opt_bc: bool, emit_bc: bool, + emit_bc_compressed: bool, emit_lto_bc: bool, emit_ir: bool, emit_asm: bool, @@ -244,6 +270,7 @@ impl ModuleConfig { emit_no_opt_bc: false, emit_bc: false, + emit_bc_compressed: false, emit_lto_bc: false, emit_ir: false, emit_asm: false, @@ -264,7 +291,7 @@ impl ModuleConfig { fn set_flags(&mut self, sess: &Session, no_builtins: bool) { self.no_verify = sess.no_verify(); self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = no_builtins; + self.no_builtins = no_builtins || sess.target.target.options.no_builtins; self.time_passes = sess.time_passes(); self.inline_threshold = sess.opts.cg.inline_threshold; self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; @@ -293,7 +320,9 @@ pub struct CodegenContext { // Resouces needed when running LTO pub time_passes: bool, pub lto: bool, + pub thinlto: bool, pub no_landing_pads: bool, + pub save_temps: bool, pub exported_symbols: Arc, pub opts: Arc, pub crate_types: Vec, @@ -302,7 +331,15 @@ pub struct CodegenContext { regular_module_config: Arc, metadata_module_config: Arc, allocator_module_config: Arc, - + pub tm_factory: Arc Result + Send + Sync>, + pub msvc_imps_needed: bool, + pub target_pointer_width: String, + binaryen_linker: bool, + debuginfo: config::DebugInfoLevel, + wasm_import_memory: bool, + + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, // Handler to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, // LLVM passes added by plugins. @@ -322,22 +359,62 @@ pub struct CodegenContext { } impl CodegenContext { - fn create_diag_handler(&self) -> Handler { + pub fn create_diag_handler(&self) -> Handler { Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) } - fn config(&self, kind: ModuleKind) -> &ModuleConfig { + pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { match kind { ModuleKind::Regular => &self.regular_module_config, ModuleKind::Metadata => &self.metadata_module_config, ModuleKind::Allocator => &self.allocator_module_config, } } + + pub fn save_temp_bitcode(&self, trans: &ModuleTranslation, name: &str) { + if !self.save_temps { + return + } + unsafe { + let ext = format!("{}.bc", name); + let cgu = Some(&trans.name[..]); + let path = self.output_filenames.temp_path_ext(&ext, cgu); + let cstr = path2cstr(&path); + let llmod = trans.llvm().unwrap().llmod; + llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + } + } +} + +struct DiagnosticHandlers<'a> { + inner: Box<(&'a CodegenContext, &'a Handler)>, + llcx: ContextRef, } -struct HandlerFreeVars<'a> { - cgcx: &'a CodegenContext, - diag_handler: &'a Handler, +impl<'a> DiagnosticHandlers<'a> { + fn new(cgcx: &'a CodegenContext, + handler: &'a Handler, + llcx: ContextRef) -> DiagnosticHandlers<'a> { + let data = Box::new((cgcx, handler)); + unsafe { + let arg = &*data as &(_, _) as *const _ as *mut _; + llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); + } + DiagnosticHandlers { + inner: data, + llcx: llcx, + } + } +} + +impl<'a> Drop for DiagnosticHandlers<'a> { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); + llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); + } + } } unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, @@ -349,7 +426,10 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, user: *const c_void, cookie: c_uint) { - let HandlerFreeVars { cgcx, .. } = *(user as *const HandlerFreeVars); + if user.is_null() { + return + } + let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) .expect("non-UTF8 SMDiagnostic"); @@ -358,7 +438,10 @@ unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, } unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - let HandlerFreeVars { cgcx, diag_handler, .. } = *(user as *const HandlerFreeVars); + if user.is_null() { + return + } + let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { @@ -389,28 +472,21 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo } // Unsafe due to LLVM calls. -unsafe fn optimize_and_codegen(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: ModuleTranslation, - tm: TargetMachineRef, - config: &ModuleConfig) - -> Result +unsafe fn optimize(cgcx: &CodegenContext, + diag_handler: &Handler, + mtrans: &ModuleTranslation, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result<(), FatalError> { - let (llmod, llcx) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx), + let (llmod, llcx, tm) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), ModuleSource::Preexisting(_) => { bug!("optimize_and_codegen: called with ModuleSource::Preexisting") } }; - let fv = HandlerFreeVars { - cgcx, - diag_handler, - }; - let fv = &fv as *const HandlerFreeVars as *mut c_void; - - llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); + let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); let module_name = mtrans.name.clone(); let module_name = Some(&module_name[..]); @@ -453,7 +529,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if !config.no_prepopulate_passes { llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - with_llvm_pmb(llmod, &config, &mut |b| { + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + with_llvm_pmb(llmod, &config, opt_level, &mut |b| { llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); }) @@ -479,30 +556,60 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, // Finally, run the actual optimization passes time(config.time_passes, &format!("llvm function passes [{}]", module_name.unwrap()), || llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + timeline.record("fpm"); time(config.time_passes, &format!("llvm module passes [{}]", module_name.unwrap()), || llvm::LLVMRunPassManager(mpm, llmod)); // Deallocate managers that we're now done with llvm::LLVMDisposePassManager(fpm); llvm::LLVMDisposePassManager(mpm); + } + Ok(()) +} - if cgcx.lto { - time(cgcx.time_passes, "all lto passes", || { - let temp_no_opt_bc_filename = - cgcx.output_filenames.temp_path_ext("no-opt.lto.bc", module_name); - lto::run(cgcx, - diag_handler, - llmod, - tm, - &config, - &temp_no_opt_bc_filename) - })?; - if config.emit_lto_bc { - let out = cgcx.output_filenames.temp_path_ext("lto.bc", module_name); - let out = path2cstr(&out); - llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); - } +fn generate_lto_work(cgcx: &CodegenContext, + modules: Vec) + -> Vec<(WorkItem, u64)> +{ + let mut timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(TRANS_WORKER_TIMELINE, + TRANS_WORK_PACKAGE_KIND, + "generate lto") + }).unwrap_or(Timeline::noop()); + let mode = if cgcx.lto { + lto::LTOMode::WholeCrateGraph + } else { + lto::LTOMode::JustThisCrate + }; + let lto_modules = lto::run(cgcx, modules, mode, &mut timeline) + .unwrap_or_else(|e| panic!(e)); + + lto_modules.into_iter().map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }).collect() +} + +unsafe fn codegen(cgcx: &CodegenContext, + diag_handler: &Handler, + mtrans: ModuleTranslation, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result +{ + timeline.record("codegen"); + let (llmod, llcx, tm) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("codegen: called with ModuleSource::Preexisting") } + }; + let module_name = mtrans.name.clone(); + let module_name = Some(&module_name[..]); + let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + if cgcx.msvc_imps_needed { + create_msvc_imps(cgcx, llcx, llmod); } // A codegen-specific pass manager is used to generate object @@ -525,21 +632,53 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, f(cpm) } + // If we're going to generate wasm code from the assembly that llvm + // generates then we'll be transitively affecting a ton of options below. + // This only happens on the wasm target now. + let asm2wasm = cgcx.binaryen_linker && + !cgcx.crate_types.contains(&config::CrateTypeRlib) && + mtrans.kind == ModuleKind::Regular; + // Change what we write and cleanup based on whether obj files are // just llvm bitcode. In that case write bitcode, and possibly // delete the bitcode if it wasn't requested. Don't generate the // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || config.obj_is_bitcode; - let rm_bc = !config.emit_bc && config.obj_is_bitcode; - let write_obj = config.emit_obj && !config.obj_is_bitcode; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; + let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm); + let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm; + let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm; let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - if write_bc { - let bc_out_c = path2cstr(&bc_out); - llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr()); + + if write_bc || config.emit_bc_compressed { + let thin; + let old; + let data = if llvm::LLVMRustThinLTOAvailable() { + thin = ThinBuffer::new(llmod); + thin.data() + } else { + old = ModuleBuffer::new(llmod); + old.data() + }; + timeline.record("make-bc"); + + if write_bc { + if let Err(e) = File::create(&bc_out).and_then(|mut f| f.write_all(data)) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("write-bc"); + } + + if config.emit_bc_compressed { + let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); + let data = bytecode::encode(&mtrans.llmod_id, data); + if let Err(e) = File::create(&dst).and_then(|mut f| f.write_all(&data)) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("compress-bc"); + } } time(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()), @@ -582,10 +721,11 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, with_codegen(tm, llmod, config.no_builtins, |cpm| { llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); llvm::LLVMDisposePassManager(cpm); - }) + }); + timeline.record("ir"); } - if config.emit_asm { + if config.emit_asm || (asm2wasm && config.emit_obj) { let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers @@ -603,13 +743,23 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if config.emit_obj { llvm::LLVMDisposeModule(llmod); } + timeline.record("asm"); } - if write_obj { + if asm2wasm && config.emit_obj { + let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + binaryen_assemble(cgcx, diag_handler, &assembly, &obj_out); + timeline.record("binaryen"); + + if !config.emit_asm { + drop(fs::remove_file(&assembly)); + } + } else if write_obj { with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) })?; + timeline.record("obj"); } Ok(()) @@ -629,7 +779,49 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } } - Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc)) + drop(handlers); + Ok(mtrans.into_compiled_module(config.emit_obj, + config.emit_bc, + config.emit_bc_compressed, + &cgcx.output_filenames)) +} + +/// Translates the LLVM-generated `assembly` on the filesystem into a wasm +/// module using binaryen, placing the output at `object`. +/// +/// In this case the "object" is actually a full and complete wasm module. We +/// won't actually be doing anything else to the output for now. This is all +/// pretty janky and will get removed as soon as a linker for wasm exists. +fn binaryen_assemble(cgcx: &CodegenContext, + handler: &Handler, + assembly: &Path, + object: &Path) { + use rustc_binaryen::{Module, ModuleOptions}; + + let input = File::open(&assembly).and_then(|mut f| { + let mut contents = Vec::new(); + f.read_to_end(&mut contents)?; + Ok(CString::new(contents)?) + }); + let mut options = ModuleOptions::new(); + if cgcx.debuginfo != config::NoDebugInfo { + options.debuginfo(true); + } + if cgcx.crate_types.contains(&config::CrateTypeExecutable) { + options.start("main"); + } + options.stack(1024 * 1024); + options.import_memory(cgcx.wasm_import_memory); + let assembled = input.and_then(|input| { + Module::new(&input, &options) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + }); + let err = assembled.and_then(|binary| { + File::create(&object).and_then(|mut f| f.write_all(binary.data())) + }); + if let Err(e) = err { + handler.err(&format!("failed to run binaryen assembler: {}", e)); + } } pub struct CompiledModules { @@ -647,7 +839,8 @@ pub fn start_async_translation(tcx: TyCtxt, time_graph: Option, link: LinkMeta, metadata: EncodedMetadata, - coordinator_receive: Receiver>) + coordinator_receive: Receiver>, + total_cgus: usize) -> OngoingCrateTranslation { let sess = tcx.sess; let crate_output = tcx.output_filenames(LOCAL_CRATE); @@ -714,11 +907,12 @@ pub fn start_async_translation(tcx: TyCtxt, allocator_config.emit_bc = true; } - // Emit bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the - // archive in order to allow LTO against it. + // Emit compressed bitcode files for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the archive in + // order to allow LTO against it. if need_crate_bitcode_for_rlib(sess) { - modules_config.emit_bc = true; + modules_config.emit_bc_compressed = true; + allocator_config.emit_bc_compressed = true; } for output_type in output_types_override.keys() { @@ -771,6 +965,7 @@ pub fn start_async_translation(tcx: TyCtxt, shared_emitter, trans_worker_send, coordinator_receive, + total_cgus, client, time_graph.clone(), Arc::new(modules_config), @@ -797,8 +992,7 @@ pub fn start_async_translation(tcx: TyCtxt, fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, dep_graph: &DepGraph, - compiled_modules: &CompiledModules, - crate_output: &OutputFilenames) { + compiled_modules: &CompiledModules) { if sess.opts.incremental.is_none() { return; } @@ -806,21 +1000,17 @@ fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, for module in compiled_modules.modules.iter() { let mut files = vec![]; - if module.emit_obj { - let path = crate_output.temp_path(OutputType::Object, Some(&module.name)); - files.push((OutputType::Object, path)); + if let Some(ref path) = module.object { + files.push((WorkProductFileKind::Object, path.clone())); } - - if module.emit_bc { - let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name)); - files.push((OutputType::Bitcode, path)); + if let Some(ref path) = module.bytecode { + files.push((WorkProductFileKind::Bytecode, path.clone())); + } + if let Some(ref path) = module.bytecode_compressed { + files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); } - save_trans_partition(sess, - dep_graph, - &module.name, - module.symbol_name_hash, - &files); + save_trans_partition(sess, dep_graph, &module.name, &files); } } @@ -924,8 +1114,6 @@ fn produce_final_output_artifacts(sess: &Session, // well. // Specific rules for keeping .#module-name#.bc: - // - If we're building an rlib (`needs_crate_bitcode`), then keep - // it. // - If the user requested bitcode (`user_wants_bitcode`), and // codegen_units > 1, then keep it. // - If the user requested bitcode but codegen_units == 1, then we @@ -935,41 +1123,37 @@ fn produce_final_output_artifacts(sess: &Session, // If you change how this works, also update back::link::link_rlib, // where .#module-name#.bc files are (maybe) deleted after making an // rlib. - let needs_crate_bitcode = need_crate_bitcode_for_rlib(sess); let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); - let keep_numbered_bitcode = needs_crate_bitcode || - (user_wants_bitcode && sess.opts.cg.codegen_units > 1); + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; let keep_numbered_objects = needs_crate_object || - (user_wants_objects && sess.opts.cg.codegen_units > 1); + (user_wants_objects && sess.codegen_units() > 1); for module in compiled_modules.modules.iter() { - let module_name = Some(&module.name[..]); - - if module.emit_obj && !keep_numbered_objects { - let path = crate_output.temp_path(OutputType::Object, module_name); - remove(sess, &path); + if let Some(ref path) = module.object { + if !keep_numbered_objects { + remove(sess, path); + } } - if module.emit_bc && !keep_numbered_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, module_name); - remove(sess, &path); + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + remove(sess, path); + } } } - if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, - Some(&compiled_modules.metadata_module.name)); - remove(sess, &path); - } - - if let Some(ref allocator_module) = compiled_modules.allocator_module { - if allocator_module.emit_bc && !user_wants_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, - Some(&allocator_module.name)); + if !user_wants_bitcode { + if let Some(ref path) = compiled_modules.metadata_module.bytecode { remove(sess, &path); } + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + remove(sess, path); + } + } } } @@ -981,46 +1165,57 @@ fn produce_final_output_artifacts(sess: &Session, } pub fn dump_incremental_data(trans: &CrateTranslation) { - let mut reuse = 0; - for mtrans in trans.modules.iter() { - if mtrans.pre_existing { - reuse += 1; - } - } - eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); + println!("[incremental] Re-using {} out of {} modules", + trans.modules.iter().filter(|m| m.pre_existing).count(), + trans.modules.len()); } -struct WorkItem { - mtrans: ModuleTranslation, - tm: TargetMachine, +enum WorkItem { + Optimize(ModuleTranslation), + LTO(lto::LtoModuleTranslation), } -impl fmt::Debug for WorkItem { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "WorkItem({})", self.mtrans.name) +impl WorkItem { + fn kind(&self) -> ModuleKind { + match *self { + WorkItem::Optimize(ref m) => m.kind, + WorkItem::LTO(_) => ModuleKind::Regular, + } } -} - -struct TargetMachine(TargetMachineRef); -unsafe impl Send for TargetMachine {} - -impl Drop for TargetMachine { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustDisposeTargetMachine(self.0); + fn name(&self) -> String { + match *self { + WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), + WorkItem::LTO(ref m) => format!("lto: {}", m.name()), } } } -fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) - -> Result +enum WorkItemResult { + Compiled(CompiledModule), + NeedsLTO(ModuleTranslation), +} + +fn execute_work_item(cgcx: &CodegenContext, + work_item: WorkItem, + timeline: &mut Timeline) + -> Result { let diag_handler = cgcx.create_diag_handler(); - let module_name = work_item.mtrans.name.clone(); - let config = cgcx.config(work_item.mtrans.kind); + let config = cgcx.config(work_item.kind()); + let mtrans = match work_item { + WorkItem::Optimize(mtrans) => mtrans, + WorkItem::LTO(mut lto) => { + unsafe { + let module = lto.optimize(cgcx, timeline)?; + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + return Ok(WorkItemResult::Compiled(module)) + } + } + }; + let module_name = mtrans.name.clone(); - let pre_existing = match work_item.mtrans.source { + let pre_existing = match mtrans.source { ModuleSource::Translated(_) => None, ModuleSource::Preexisting(ref wp) => Some(wp.clone()), }; @@ -1029,13 +1224,33 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) let incr_comp_session_dir = cgcx.incr_comp_session_dir .as_ref() .unwrap(); - let name = &work_item.mtrans.name; + let name = &mtrans.name; + let mut object = None; + let mut bytecode = None; + let mut bytecode_compressed = None; for (kind, saved_file) in wp.saved_files { - let obj_out = cgcx.output_filenames.temp_path(kind, Some(name)); + let obj_out = match kind { + WorkProductFileKind::Object => { + let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); + object = Some(path.clone()); + path + } + WorkProductFileKind::Bytecode => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); + bytecode = Some(path.clone()); + path + } + WorkProductFileKind::BytecodeCompressed => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) + .with_extension(RLIB_BYTECODE_EXTENSION); + bytecode_compressed = Some(path.clone()); + path + } + }; let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file); debug!("copying pre-existing module `{}` from {:?} to {}", - work_item.mtrans.name, + mtrans.name, source_file, obj_out.display()); match link_or_copy(&source_file, &obj_out) { @@ -1048,31 +1263,58 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) } } } + assert_eq!(object.is_some(), config.emit_obj); + assert_eq!(bytecode.is_some(), config.emit_bc); + assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); - Ok(CompiledModule { + Ok(WorkItemResult::Compiled(CompiledModule { + llmod_id: mtrans.llmod_id.clone(), name: module_name, kind: ModuleKind::Regular, pre_existing: true, - symbol_name_hash: work_item.mtrans.symbol_name_hash, - emit_bc: config.emit_bc, - emit_obj: config.emit_obj, - }) + object, + bytecode, + bytecode_compressed, + })) } else { debug!("llvm-optimizing {:?}", module_name); unsafe { - optimize_and_codegen(cgcx, - &diag_handler, - work_item.mtrans, - work_item.tm.0, - config) + optimize(cgcx, &diag_handler, &mtrans, config, timeline)?; + + let lto = cgcx.lto; + + let auto_thin_lto = + cgcx.thinlto && + cgcx.total_cgus > 1 && + mtrans.kind != ModuleKind::Allocator; + + // If we're a metadata module we never participate in LTO. + // + // If LTO was explicitly requested on the command line, we always + // LTO everything else. + // + // If LTO *wasn't* explicitly requested and we're not a metdata + // module, then we may automatically do ThinLTO if we've got + // multiple codegen units. Note, however, that the allocator module + // doesn't participate here automatically because of linker + // shenanigans later on. + if mtrans.kind == ModuleKind::Metadata || (!lto && !auto_thin_lto) { + let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?; + Ok(WorkItemResult::Compiled(module)) + } else { + Ok(WorkItemResult::NeedsLTO(mtrans)) + } } } } -#[derive(Debug)] enum Message { Token(io::Result), + NeedsLTO { + result: ModuleTranslation, + worker_id: usize, + }, Done { result: Result, worker_id: usize, @@ -1087,7 +1329,7 @@ enum Message { struct Diagnostic { msg: String, - code: Option, + code: Option, lvl: Level, } @@ -1103,12 +1345,13 @@ fn start_executing_work(tcx: TyCtxt, shared_emitter: SharedEmitter, trans_worker_send: Sender, coordinator_receive: Receiver>, + total_cgus: usize, jobserver: Client, time_graph: Option, modules_config: Arc, metadata_config: Arc, allocator_config: Arc) - -> thread::JoinHandle { + -> thread::JoinHandle> { let coordinator_send = tcx.tx_to_llvm_workers.clone(); let mut exported_symbols = FxHashMap(); exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE)); @@ -1135,17 +1378,36 @@ fn start_executing_work(tcx: TyCtxt, let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { - if link::ignored_for_lto(crate_info, cnum) { + if link::ignored_for_lto(sess, crate_info, cnum) { return } each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); + let crate_types = sess.crate_types.borrow(); + let only_rlib = crate_types.len() == 1 && + crate_types[0] == config::CrateTypeRlib; + + let wasm_import_memory = + attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory"); + let cgcx = CodegenContext { crate_types: sess.crate_types.borrow().clone(), each_linked_rlib_for_lto, - lto: sess.lto(), + // If we're only building an rlibc then allow the LTO flag to be passed + // but don't actually do anything, the full LTO will happen later + lto: sess.lto() && !only_rlib, + + // Enable ThinLTO if requested, but only if the target we're compiling + // for doesn't require full LTO. Some targets require one LLVM module + // (they effectively don't have a linker) so it's up to us to use LTO to + // link everything together. + thinlto: sess.thinlto() && + !sess.target.target.options.requires_lto && + unsafe { llvm::LLVMRustThinLTOAvailable() }, + no_landing_pads: sess.no_landing_pads(), + save_temps: sess.opts.cg.save_temps, opts: Arc::new(sess.opts.clone()), time_passes: sess.time_passes(), exported_symbols, @@ -1160,6 +1422,13 @@ fn start_executing_work(tcx: TyCtxt, regular_module_config: modules_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, + tm_factory: target_machine_factory(tcx.sess), + total_cgus, + msvc_imps_needed: msvc_imps_needed(tcx), + target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), + binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen, + debuginfo: tcx.sess.opts.debuginfo, + wasm_import_memory: wasm_import_memory, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1282,6 +1551,21 @@ fn start_executing_work(tcx: TyCtxt, // and whenever we're done with that work we release the semaphore. In this // manner we can ensure that the maximum number of parallel workers is // capped at any one point in time. + // + // LTO and the coordinator thread + // ------------------------------ + // + // The final job the coordinator thread is responsible for is managing LTO + // and how that works. When LTO is requested what we'll to is collect all + // optimized LLVM modules into a local vector on the coordinator. Once all + // modules have been translated and optimized we hand this to the `lto` + // module for further optimization. The `lto` module will return back a list + // of more modules to work on, which the coordinator will continue to spawn + // work for. + // + // Each LLVM module is automatically sent back to the coordinator for LTO if + // necessary. There's already optimizations in place to avoid sending work + // back to the coordinator if LTO isn't requested. return thread::spawn(move || { // We pretend to be within the top-level LLVM time-passes task here: set_time_depth(1); @@ -1304,6 +1588,8 @@ fn start_executing_work(tcx: TyCtxt, let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; + let mut needs_lto = Vec::new(); + let mut started_lto = false; // This flag tracks whether all items have gone through translations let mut translation_done = false; @@ -1325,6 +1611,7 @@ fn start_executing_work(tcx: TyCtxt, while !translation_done || work_items.len() > 0 || running > 0 || + needs_lto.len() > 0 || main_thread_worker_state != MainThreadWorkerState::Idle { // While there are still CGUs to be translated, the coordinator has @@ -1348,13 +1635,34 @@ fn start_executing_work(tcx: TyCtxt, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } } } else { + // If we've finished everything related to normal translation + // then it must be the case that we've got some LTO work to do. + // Perform the serial work here of figuring out what we're + // going to LTO and then push a bunch of work items onto our + // queue to do LTO + if work_items.len() == 0 && + running == 0 && + main_thread_worker_state == MainThreadWorkerState::Idle { + assert!(!started_lto); + assert!(needs_lto.len() > 0); + started_lto = true; + let modules = mem::replace(&mut needs_lto, Vec::new()); + for (work, cost) in generate_lto_work(&cgcx, modules) { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + helper.request_token(); + } + } + // In this branch, we know that everything has been translated, // so it's just a matter of determining whether the implicit // Token is free to use for LLVM work. @@ -1365,7 +1673,7 @@ fn start_executing_work(tcx: TyCtxt, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); @@ -1396,7 +1704,7 @@ fn start_executing_work(tcx: TyCtxt, while work_items.len() > 0 && running < tokens.len() { let (item, _) = work_items.pop().unwrap(); - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); let cgcx = CodegenContext { @@ -1499,10 +1807,21 @@ fn start_executing_work(tcx: TyCtxt, } } } + Message::NeedsLTO { result, worker_id } => { + assert!(!started_lto); + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + needs_lto.push(result); + } Message::Done { result: Err(()), worker_id: _ } => { - shared_emitter.fatal("aborting due to worker thread panic"); + shared_emitter.fatal("aborting due to worker thread failure"); // Exit the coordinator thread - panic!("aborting due to worker thread panic") + return Err(()) } Message::TranslateItem => { bug!("the coordinator should not receive translation requests") @@ -1520,14 +1839,19 @@ fn start_executing_work(tcx: TyCtxt, total_llvm_time); } + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); + let compiled_metadata_module = compiled_metadata_module .expect("Metadata module not compiled?"); - CompiledModules { + Ok(CompiledModules { modules: compiled_modules, metadata_module: compiled_metadata_module, allocator_module: compiled_allocator_module, - } + }) }); // A heuristic that determines if we have enough LLVM WorkItems in the @@ -1570,20 +1894,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we exit. struct Bomb { coordinator_send: Sender>, - result: Option, + result: Option, worker_id: usize, } impl Drop for Bomb { fn drop(&mut self) { - let result = match self.result.take() { - Some(compiled_module) => Ok(compiled_module), - None => Err(()) + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(WorkItemResult::Compiled(m)) => { + Message::Done { result: Ok(m), worker_id } + } + Some(WorkItemResult::NeedsLTO(m)) => { + Message::NeedsLTO { result: m, worker_id } + } + None => Message::Done { result: Err(()), worker_id } }; - - drop(self.coordinator_send.send(Box::new(Message::Done { - result, - worker_id: self.worker_id, - }))); + drop(self.coordinator_send.send(Box::new(msg))); } } @@ -1596,22 +1922,17 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. // - // Note that we ignore the result coming out of `execute_work_item` - // which will tell us if the worker failed with a `FatalError`. If that - // has happened, however, then a diagnostic was sent off to the main - // thread, along with an `AbortIfErrors` message. In that case the main - // thread is already exiting anyway most likely. - // - // In any case, there's no need for us to take further action here, so - // we just ignore the result and then send off our message saying that - // we're done, which if `execute_work_item` failed is unlikely to be - // seen by the main thread, but hey we might as well try anyway. + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. bomb.result = { - let _timing_guard = cgcx.time_graph - .as_ref() - .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker), - LLVM_WORK_PACKAGE_KIND)); - Some(execute_work_item(&cgcx, work).unwrap()) + let timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND, + &work.name()) + }); + let mut timeline = timeline.unwrap_or(Timeline::noop()); + execute_work_item(&cgcx, work, &mut timeline).ok() }; }); } @@ -1651,16 +1972,17 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { pub unsafe fn with_llvm_pmb(llmod: ModuleRef, config: &ModuleConfig, + opt_level: llvm::CodeGenOptLevel, f: &mut FnMut(llvm::PassManagerBuilderRef)) { // Create the PassManagerBuilder for LLVM. We configure it with // reasonable defaults and prepare it to actually populate the pass // manager. let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); let inline_threshold = config.inline_threshold; - llvm::LLVMRustConfigurePassManagerBuilder(builder, opt_level, + llvm::LLVMRustConfigurePassManagerBuilder(builder, + opt_level, config.merge_functions, config.vectorize_slp, config.vectorize_loop); @@ -1780,7 +2102,7 @@ impl SharedEmitterMain { Some(ref code) => { handler.emit_with_code(&MultiSpan::new(), &diag.msg, - &code, + code.clone(), diag.lvl); } None => { @@ -1823,7 +2145,7 @@ pub struct OngoingCrateTranslation { coordinator_send: Sender>, trans_worker_receive: Receiver, shared_emitter_main: SharedEmitterMain, - future: thread::JoinHandle, + future: thread::JoinHandle>, output_filenames: Arc, } @@ -1831,7 +2153,11 @@ impl OngoingCrateTranslation { pub fn join(self, sess: &Session, dep_graph: &DepGraph) -> CrateTranslation { self.shared_emitter_main.check(sess, true); let compiled_modules = match self.future.join() { - Ok(compiled_modules) => compiled_modules, + Ok(Ok(compiled_modules)) => compiled_modules, + Ok(Err(())) => { + sess.abort_if_errors(); + panic!("expected abort due to worker thread errors") + }, Err(_) => { sess.fatal("Error during translation/LLVM phase."); } @@ -1845,15 +2171,14 @@ impl OngoingCrateTranslation { copy_module_artifacts_into_incr_comp_cache(sess, dep_graph, - &compiled_modules, - &self.output_filenames); + &compiled_modules); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); // FIXME: time_llvm_passes support - does this use a global context or // something? - if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { + if sess.codegen_units() == 1 && sess.time_llvm_passes() { unsafe { llvm::LLVMRustPrintPassTimings(); } } @@ -1867,6 +2192,7 @@ impl OngoingCrateTranslation { modules: compiled_modules.modules, allocator_module: compiled_modules.allocator_module, + metadata_module: compiled_modules.metadata_module, }; if self.no_integrated_as { @@ -1918,9 +2244,7 @@ impl OngoingCrateTranslation { Ok(Message::TranslateItem) => { // Nothing to do } - Ok(message) => { - panic!("unexpected message: {:?}", message) - } + Ok(_) => panic!("unexpected message"), Err(_) => { // One of the LLVM threads must have panicked, fall through so // error handling can be reached. @@ -1932,12 +2256,57 @@ impl OngoingCrateTranslation { pub fn submit_translated_module_to_llvm(tcx: TyCtxt, mtrans: ModuleTranslation, cost: u64) { - let llvm_work_item = WorkItem { - mtrans, - tm: TargetMachine(create_target_machine(tcx.sess)), - }; + let llvm_work_item = WorkItem::Optimize(mtrans); drop(tcx.tx_to_llvm_workers.send(Box::new(Message::TranslationDone { llvm_work_item, cost, }))); } + +fn msvc_imps_needed(tcx: TyCtxt) -> bool { + tcx.sess.target.target.options.is_like_msvc && + tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) +} + +// Create a `__imp_ = &symbol` global for every public static `symbol`. +// This is required to satisfy `dllimport` references to static data in .rlibs +// when using MSVC linker. We do this only for data, as linker can fix up +// code references on its own. +// See #26591, #27438 +fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { + if !cgcx.msvc_imps_needed { + return + } + // The x86 ABI seems to require that leading underscores are added to symbol + // names, so we need an extra underscore on 32-bit. There's also a leading + // '\x01' here which disables LLVM's symbol mangling (e.g. no extra + // underscores added in front). + let prefix = if cgcx.target_pointer_width == "32" { + "\x01__imp__" + } else { + "\x01__imp_" + }; + unsafe { + let i8p_ty = Type::i8p_llcx(llcx); + let globals = base::iter_globals(llmod) + .filter(|&val| { + llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && + llvm::LLVMIsDeclaration(val) == 0 + }) + .map(move |val| { + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name.to_bytes()); + let imp_name = CString::new(imp_name).unwrap(); + (imp_name, val) + }) + .collect::>(); + for (imp_name, val) in globals { + let imp = llvm::LLVMAddGlobal(llmod, + i8p_ty.to_ref(), + imp_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); + llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); + } + } +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 2d01d2947d6eb..bfc72ff06aa70 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -28,59 +28,53 @@ use super::ModuleSource; use super::ModuleTranslation; use super::ModuleKind; -use assert_module_sources::{self, Disposition}; +use abi; +use assert_module_sources; use back::link; use back::symbol_export; -use back::write::{self, OngoingCrateTranslation}; +use back::write::{self, OngoingCrateTranslation, create_target_machine}; use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; use llvm; use metadata; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::middle::lang_items::StartFnLangItem; use rustc::middle::trans::{Linkage, Visibility, Stats}; -use rustc::middle::cstore::{EncodedMetadata, EncodedMetadataHashes}; +use rustc::middle::cstore::EncodedMetadata; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; use rustc::ty::maps::Providers; -use rustc::dep_graph::{DepNode, DepKind}; +use rustc::dep_graph::{DepNode, DepKind, DepConstructor}; use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; use rustc::util::common::{time, print_time_passes_entry}; use rustc::session::config::{self, NoDebugInfo}; use rustc::session::Session; use rustc_incremental; -use abi; use allocator; -use mir::lvalue::LvalueRef; +use mir::place::PlaceRef; use attributes; use builder::Builder; use callee; use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; use collector::{self, TransItemCollectionMode}; -use common::{C_struct_in_context, C_u64, C_undef, C_array}; -use common::CrateContext; -use common::{type_is_zero_size, val_ty}; -use common; +use common::{self, C_struct_in_context, C_array, CrateContext, val_ty}; use consts; use context::{self, LocalCrateContext, SharedCrateContext}; use debuginfo; use declare; -use machine; use meth; use mir; -use monomorphize::{self, Instance}; +use monomorphize::Instance; use partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; use symbol_names_test; use time_graph; -use trans_item::{TransItem, TransItemExt, DefPathBasedNames}; +use trans_item::{TransItem, BaseTransItemExt, TransItemExt, DefPathBasedNames}; use type_::Type; -use type_of; -use value::Value; +use type_of::LayoutLlvmExt; use rustc::util::nodemap::{NodeSet, FxHashMap, FxHashSet, DefIdSet}; use CrateInfo; -use libc::c_uint; use std::any::Any; -use std::cell::RefCell; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::str; use std::sync::Arc; use std::time::{Instant, Duration}; @@ -92,9 +86,10 @@ use syntax::attr; use rustc::hir; use syntax::ast; -use mir::lvalue::Alignment; +use mir::operand::OperandValue; pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr}; +pub use rustc_trans_utils::trans_item::linkage_by_name; pub struct StatRecorder<'a, 'tcx: 'a> { ccx: &'a CrateContext<'a, 'tcx>, @@ -126,14 +121,6 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { } } -pub fn get_meta(bcx: &Builder, fat_ptr: ValueRef) -> ValueRef { - bcx.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA) -} - -pub fn get_dataptr(bcx: &Builder, fat_ptr: ValueRef) -> ValueRef { - bcx.struct_gep(fat_ptr, abi::FAT_PTR_ADDR) -} - pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, signed: bool) -> llvm::IntPredicate { @@ -217,8 +204,10 @@ pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, old_info.expect("unsized_info: missing old info for trait upcast") } (_, &ty::TyDynamic(ref data, ..)) => { + let vtable_ptr = ccx.layout_of(ccx.tcx().mk_mut_ptr(target)) + .field(ccx, abi::FAT_PTR_EXTRA); consts::ptrcast(meth::get_vtable(ccx, source, data.principal()), - Type::vtable_ptr(ccx)) + vtable_ptr.llvm_type(ccx)) } _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, @@ -242,15 +231,40 @@ pub fn unsize_thin_ptr<'a, 'tcx>( (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { assert!(bcx.ccx.shared().type_is_sized(a)); - let ptr_ty = type_of::in_memory_type_of(bcx.ccx, b).ptr_to(); + let ptr_ty = bcx.ccx.layout_of(b).llvm_type(bcx.ccx).ptr_to(); (bcx.pointercast(src, ptr_ty), unsized_info(bcx.ccx, a, b, None)) } (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); assert!(bcx.ccx.shared().type_is_sized(a)); - let ptr_ty = type_of::in_memory_type_of(bcx.ccx, b).ptr_to(); + let ptr_ty = bcx.ccx.layout_of(b).llvm_type(bcx.ccx).ptr_to(); (bcx.pointercast(src, ptr_ty), unsized_info(bcx.ccx, a, b, None)) } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { + assert_eq!(def_a, def_b); + + let src_layout = bcx.ccx.layout_of(src_ty); + let dst_layout = bcx.ccx.layout_of(dst_ty); + let mut result = None; + for i in 0..src_layout.fields.count() { + let src_f = src_layout.field(bcx.ccx, i); + assert_eq!(src_layout.fields.offset(i).bytes(), 0); + assert_eq!(dst_layout.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src_layout.size, src_f.size); + + let dst_f = dst_layout.field(bcx.ccx, i); + assert_ne!(src_f.ty, dst_f.ty); + assert_eq!(result, None); + result = Some(unsize_thin_ptr(bcx, src, src_f.ty, dst_f.ty)); + } + let (lldata, llextra) = result.unwrap(); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + (bcx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bcx.ccx, 0)), + bcx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bcx.ccx, 1))) + } _ => bug!("unsize_thin_ptr: called on bad types"), } } @@ -258,25 +272,26 @@ 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: &Builder<'a, 'tcx>, - src: &LvalueRef<'tcx>, - dst: &LvalueRef<'tcx>) { - let src_ty = src.ty.to_ty(bcx.tcx()); - let dst_ty = dst.ty.to_ty(bcx.tcx()); + src: PlaceRef<'tcx>, + dst: PlaceRef<'tcx>) { + let src_ty = src.layout.ty; + let dst_ty = dst.layout.ty; let coerce_ptr = || { - let (base, info) = if common::type_is_fat_ptr(bcx.ccx, src_ty) { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e. &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - let (base, info) = load_fat_ptr(bcx, src.llval, src.alignment, src_ty); - let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, dst_ty); - let base = bcx.pointercast(base, llcast_ty); - (base, info) - } else { - let base = load_ty(bcx, src.llval, src.alignment, src_ty); - unsize_thin_ptr(bcx, base, src_ty, dst_ty) + let (base, info) = match src.load(bcx).val { + OperandValue::Pair(base, info) => { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e. &'a fmt::Debug+Send => &'a fmt::Debug + // So we need to pointercast the base to ensure + // the types match up. + let thin_ptr = dst.layout.field(bcx.ccx, abi::FAT_PTR_ADDR); + (bcx.pointercast(base, thin_ptr.llvm_type(bcx.ccx)), info) + } + OperandValue::Immediate(base) => { + unsize_thin_ptr(bcx, base, src_ty, dst_ty) + } + OperandValue::Ref(..) => bug!() }; - store_fat_ptr(bcx, base, info, dst.llval, dst.alignment, dst_ty); + OperandValue::Pair(base, info).store(bcx, dst); }; match (&src_ty.sty, &dst_ty.sty) { (&ty::TyRef(..), &ty::TyRef(..)) | @@ -288,32 +303,22 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, coerce_ptr() } - (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => { + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { assert_eq!(def_a, def_b); - let src_fields = def_a.variants[0].fields.iter().map(|f| { - monomorphize::field_ty(bcx.tcx(), substs_a, f) - }); - let dst_fields = def_b.variants[0].fields.iter().map(|f| { - monomorphize::field_ty(bcx.tcx(), substs_b, f) - }); + for i in 0..def_a.variants[0].fields.len() { + let src_f = src.project_field(bcx, i); + let dst_f = dst.project_field(bcx, i); - let iter = src_fields.zip(dst_fields).enumerate(); - for (i, (src_fty, dst_fty)) in iter { - if type_is_zero_size(bcx.ccx, dst_fty) { + if dst_f.layout.is_zst() { continue; } - let (src_f, src_f_align) = src.trans_field_ptr(bcx, i); - let (dst_f, dst_f_align) = dst.trans_field_ptr(bcx, i); - if src_fty == dst_fty { - memcpy_ty(bcx, dst_f, src_f, src_fty, None); + if src_f.layout.ty == dst_f.layout.ty { + memcpy_ty(bcx, dst_f.llval, src_f.llval, src_f.layout, + (src_f.alignment | dst_f.alignment).non_abi()); } else { - coerce_unsized_into( - bcx, - &LvalueRef::new_sized_ty(src_f, src_fty, src_f_align), - &LvalueRef::new_sized_ty(dst_f, dst_fty, dst_f_align) - ); + coerce_unsized_into(bcx, src_f, dst_f); } } } @@ -386,94 +391,6 @@ pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) { b.call(assume_intrinsic, &[val], None); } -/// Helper for loading values from memory. Does the necessary conversion if the in-memory type -/// differs from the type used for SSA values. Also handles various special cases where the type -/// gives us better information about what we are loading. -pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, - alignment: Alignment, t: Ty<'tcx>) -> ValueRef { - let ccx = b.ccx; - if type_is_zero_size(ccx, t) { - return C_undef(type_of::type_of(ccx, t)); - } - - unsafe { - let global = llvm::LLVMIsAGlobalVariable(ptr); - if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { - let val = llvm::LLVMGetInitializer(global); - if !val.is_null() { - if t.is_bool() { - return llvm::LLVMConstTrunc(val, Type::i1(ccx).to_ref()); - } - return val; - } - } - } - - if t.is_bool() { - b.trunc(b.load_range_assert(ptr, 0, 2, llvm::False, alignment.to_align()), - Type::i1(ccx)) - } else if t.is_char() { - // a char is a Unicode codepoint, and so takes values from 0 - // to 0x10FFFF inclusive only. - b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False, alignment.to_align()) - } else if (t.is_region_ptr() || t.is_box() || t.is_fn()) - && !common::type_is_fat_ptr(ccx, t) - { - b.load_nonnull(ptr, alignment.to_align()) - } else { - b.load(ptr, alignment.to_align()) - } -} - -/// 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: &Builder<'a, 'tcx>, v: ValueRef, dst: ValueRef, - dst_align: Alignment, t: Ty<'tcx>) { - debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v)); - - if common::type_is_fat_ptr(cx.ccx, t) { - let lladdr = cx.extract_value(v, abi::FAT_PTR_ADDR); - let llextra = cx.extract_value(v, abi::FAT_PTR_EXTRA); - store_fat_ptr(cx, lladdr, llextra, dst, dst_align, t); - } else { - cx.store(from_immediate(cx, v), dst, dst_align.to_align()); - } -} - -pub fn store_fat_ptr<'a, 'tcx>(cx: &Builder<'a, 'tcx>, - data: ValueRef, - extra: ValueRef, - dst: ValueRef, - dst_align: Alignment, - _ty: Ty<'tcx>) { - // FIXME: emit metadata - cx.store(data, get_dataptr(cx, dst), dst_align.to_align()); - cx.store(extra, get_meta(cx, dst), dst_align.to_align()); -} - -pub fn load_fat_ptr<'a, 'tcx>( - b: &Builder<'a, 'tcx>, src: ValueRef, alignment: Alignment, t: Ty<'tcx> -) -> (ValueRef, ValueRef) { - let ptr = get_dataptr(b, src); - let ptr = if t.is_region_ptr() || t.is_box() { - b.load_nonnull(ptr, alignment.to_align()) - } else { - b.load(ptr, alignment.to_align()) - }; - - let meta = get_meta(b, src); - let meta_ty = val_ty(meta); - // If the 'meta' field is a pointer, it's a vtable, so use load_nonnull - // instead - let meta = if meta_ty.element_type().kind() == llvm::TypeKind::Pointer { - b.load_nonnull(meta, None) - } else { - b.load(meta, None) - }; - - (ptr, meta) -} - pub fn from_immediate(bcx: &Builder, val: ValueRef) -> ValueRef { if val_ty(val) == Type::i1(bcx.ccx) { bcx.zext(val, Type::i8(bcx.ccx)) @@ -482,50 +399,20 @@ pub fn from_immediate(bcx: &Builder, val: ValueRef) -> ValueRef { } } -pub fn to_immediate(bcx: &Builder, val: ValueRef, ty: Ty) -> ValueRef { - if ty.is_bool() { - bcx.trunc(val, Type::i1(bcx.ccx)) - } else { - val - } -} - -pub enum Lifetime { Start, End } - -impl Lifetime { - // If LLVM lifetime intrinsic support is enabled (i.e. optimizations - // on), and `ptr` is nonzero-sized, then extracts the size of `ptr` - // and the intrinsic for `lt` and passes them to `emit`, which is in - // charge of generating code to call the passed intrinsic on whatever - // block of generated code is targeted for the intrinsic. - // - // If LLVM lifetime intrinsic support is disabled (i.e. optimizations - // off) or `ptr` is zero-sized, then no-op (does not call `emit`). - pub fn call(self, b: &Builder, ptr: ValueRef) { - if b.ccx.sess().opts.optimize == config::OptLevel::No { - return; - } - - let size = machine::llsize_of_alloc(b.ccx, val_ty(ptr).element_type()); - if size == 0 { - return; +pub fn to_immediate(bcx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { + if let layout::Abi::Scalar(ref scalar) = layout.abi { + if scalar.is_bool() { + return bcx.trunc(val, Type::i1(bcx.ccx)); } - - let lifetime_intrinsic = b.ccx.get_intrinsic(match self { - Lifetime::Start => "llvm.lifetime.start", - Lifetime::End => "llvm.lifetime.end" - }); - - let ptr = b.pointercast(ptr, Type::i8p(b.ccx)); - b.call(lifetime_intrinsic, &[C_u64(b.ccx, size), ptr], None); } + val } -pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>, - dst: ValueRef, - src: ValueRef, - n_bytes: ValueRef, - align: u32) { +pub fn call_memcpy(b: &Builder, + dst: ValueRef, + src: ValueRef, + n_bytes: ValueRef, + align: Align) { let ccx = b.ccx; let ptr_width = &ccx.sess().target.target.target_pointer_width; let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); @@ -533,7 +420,7 @@ pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>, let src_ptr = b.pointercast(src, Type::i8p(ccx)); let dst_ptr = b.pointercast(dst, Type::i8p(ccx)); let size = b.intcast(n_bytes, ccx.isize_ty(), false); - let align = C_i32(ccx, align as i32); + let align = C_i32(ccx, align.abi() as i32); let volatile = C_bool(ccx, false); b.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); } @@ -542,18 +429,16 @@ pub fn memcpy_ty<'a, 'tcx>( bcx: &Builder<'a, 'tcx>, dst: ValueRef, src: ValueRef, - t: Ty<'tcx>, - align: Option, + layout: TyLayout<'tcx>, + align: Option, ) { - let ccx = bcx.ccx; - - let size = ccx.size_of(t); + let size = layout.size.bytes(); if size == 0 { return; } - let align = align.unwrap_or_else(|| ccx.align_of(t)); - call_memcpy(bcx, dst, src, C_usize(ccx, size), align); + let align = align.unwrap_or(layout.align); + call_memcpy(bcx, dst, src, C_usize(bcx.ccx, size), align); } pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, @@ -620,33 +505,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance mir::trans_mir(ccx, lldecl, &mir, instance, sig); } -pub fn linkage_by_name(name: &str) -> Option { - use rustc::middle::trans::Linkage::*; - - // Use the names from src/llvm/docs/LangRef.rst here. Most types are only - // applicable to variable declarations and may not really make sense for - // Rust code in the first place but whitelist them anyway and trust that - // the user knows what s/he's doing. Who knows, unanticipated use cases - // may pop up in the future. - // - // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported - // and don't have to be, LLVM treats them as no-ops. - match name { - "appending" => Some(Appending), - "available_externally" => Some(AvailableExternally), - "common" => Some(Common), - "extern_weak" => Some(ExternalWeak), - "external" => Some(External), - "internal" => Some(Internal), - "linkonce" => Some(LinkOnceAny), - "linkonce_odr" => Some(LinkOnceODR), - "private" => Some(Private), - "weak" => Some(WeakAny), - "weak_odr" => Some(WeakODR), - _ => None, - } -} - pub fn set_link_section(ccx: &CrateContext, llval: ValueRef, attrs: &[ast::Attribute]) { @@ -692,7 +550,8 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) { sp: Span, rust_main: ValueRef, use_start_lang_item: bool) { - let llfty = Type::func(&[ccx.isize_ty(), Type::i8p(ccx).ptr_to()], &ccx.isize_ty()); + // Signature of native main(), corresponding to C's `int main(int, char **)` + let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &Type::c_int(ccx)); if declare::get_defined_value(ccx, "main").is_some() { // FIXME: We should be smart and show a better diagnostic here. @@ -711,19 +570,27 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) { debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx, &bld); + // Params from native main() used as args for rust start function + let param_argc = get_param(llfn, 0); + let param_argv = get_param(llfn, 1); + let arg_argc = bld.intcast(param_argc, ccx.isize_ty(), true); + let arg_argv = param_argv; + let (start_fn, args) = if use_start_lang_item { let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem); let start_instance = Instance::mono(ccx.tcx(), start_def_id); let start_fn = callee::get_fn(ccx, start_instance); - (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()), get_param(llfn, 0), - get_param(llfn, 1)]) + (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()), + arg_argc, arg_argv]) } else { debug!("using user-defined start fn"); - (rust_main, vec![get_param(llfn, 0 as c_uint), get_param(llfn, 1 as c_uint)]) + (rust_main, vec![arg_argc, arg_argv]) }; let result = bld.call(start_fn, &args, None); - bld.ret(result); + + // Return rust start function's result from native main() + bld.ret(bld.intcast(result, Type::c_int(ccx), true)); } } @@ -732,16 +599,16 @@ fn contains_null(s: &str) -> bool { } fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, + llmod_id: &str, link_meta: &LinkMeta, exported_symbols: &NodeSet) - -> (ContextRef, ModuleRef, - EncodedMetadata, EncodedMetadataHashes) { + -> (ContextRef, ModuleRef, EncodedMetadata) { use std::io::Write; use flate2::Compression; use flate2::write::DeflateEncoder; let (metadata_llcx, metadata_llmod) = unsafe { - context::create_context_and_module(tcx.sess, "metadata") + context::create_context_and_module(tcx.sess, llmod_id) }; #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -767,13 +634,12 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, if kind == MetadataKind::None { return (metadata_llcx, metadata_llmod, - EncodedMetadata::new(), - EncodedMetadataHashes::new()); + EncodedMetadata::new()); } - let (metadata, hashes) = tcx.encode_metadata(link_meta, exported_symbols); + let metadata = tcx.encode_metadata(link_meta, exported_symbols); if kind == MetadataKind::Uncompressed { - return (metadata_llcx, metadata_llmod, metadata, hashes); + return (metadata_llcx, metadata_llmod, metadata); } assert!(kind == MetadataKind::Compressed); @@ -801,50 +667,10 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, let directive = CString::new(directive).unwrap(); llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) } - return (metadata_llcx, metadata_llmod, metadata, hashes); -} - -// Create a `__imp_ = &symbol` global for every public static `symbol`. -// This is required to satisfy `dllimport` references to static data in .rlibs -// when using MSVC linker. We do this only for data, as linker can fix up -// code references on its own. -// See #26591, #27438 -fn create_imps(sess: &Session, - llvm_module: &ModuleLlvm) { - // The x86 ABI seems to require that leading underscores are added to symbol - // names, so we need an extra underscore on 32-bit. There's also a leading - // '\x01' here which disables LLVM's symbol mangling (e.g. no extra - // underscores added in front). - let prefix = if sess.target.target.target_pointer_width == "32" { - "\x01__imp__" - } else { - "\x01__imp_" - }; - unsafe { - let exported: Vec<_> = iter_globals(llvm_module.llmod) - .filter(|&val| { - llvm::LLVMRustGetLinkage(val) == - llvm::Linkage::ExternalLinkage && - llvm::LLVMIsDeclaration(val) == 0 - }) - .collect(); - - let i8p_ty = Type::i8p_llcx(llvm_module.llcx); - for val in exported { - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name.to_bytes()); - let imp_name = CString::new(imp_name).unwrap(); - let imp = llvm::LLVMAddGlobal(llvm_module.llmod, - i8p_ty.to_ref(), - imp_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); - llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); - } - } + return (metadata_llcx, metadata_llmod, metadata); } -struct ValueIter { +pub struct ValueIter { cur: ValueRef, step: unsafe extern "C" fn(ValueRef) -> ValueRef, } @@ -863,7 +689,7 @@ impl Iterator for ValueIter { } } -fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { +pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { unsafe { ValueIter { cur: llvm::LLVMGetFirstGlobal(llmod), @@ -878,6 +704,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, check_for_rustc_errors_attr(tcx); + if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { + if unsafe { !llvm::LLVMRustThinLTOAvailable() } { + tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); + } + } let crate_hash = tcx.dep_graph .fingerprint_of(&DepNode::new_no_params(DepKind::Krate)); @@ -886,17 +717,19 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let shared_ccx = SharedCrateContext::new(tcx); // Translate the metadata. - let (metadata_llcx, metadata_llmod, metadata, metadata_incr_hashes) = + let llmod_id = "metadata"; + let (metadata_llcx, metadata_llmod, metadata) = time(tcx.sess.time_passes(), "write metadata", || { - write_metadata(tcx, &link_meta, &exported_symbol_node_ids) + write_metadata(tcx, llmod_id, &link_meta, &exported_symbol_node_ids) }); let metadata_module = ModuleTranslation { name: link::METADATA_MODULE_NAME.to_string(), - symbol_name_hash: 0, // we always rebuild metadata, at least for now + llmod_id: llmod_id.to_string(), source: ModuleSource::Translated(ModuleLlvm { llcx: metadata_llcx, llmod: metadata_llmod, + tm: create_target_machine(tcx.sess), }), kind: ModuleKind::Metadata, }; @@ -915,14 +748,13 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time_graph.clone(), link_meta, metadata, - rx); + rx, + 1); ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); ongoing_translation.translation_finished(tcx); - assert_and_save_dep_graph(tcx, - metadata_incr_hashes, - link_meta); + assert_and_save_dep_graph(tcx); ongoing_translation.check_for_errors(tcx.sess); @@ -935,34 +767,35 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, shared_ccx.tcx().collect_and_partition_translation_items(LOCAL_CRATE).1; let codegen_units = (*codegen_units).clone(); - assert!(codegen_units.len() <= 1 || !tcx.sess.lto()); + // Force all codegen_unit queries so they are already either red or green + // when compile_codegen_unit accesses them. We are not able to re-execute + // the codegen_unit query from just the DepNode, so an unknown color would + // lead to having to re-execute compile_codegen_unit, possibly + // unnecessarily. + if tcx.dep_graph.is_fully_enabled() { + for cgu in &codegen_units { + tcx.codegen_unit(cgu.name().clone()); + } + } let ongoing_translation = write::start_async_translation( tcx, time_graph.clone(), link_meta, metadata, - rx); + rx, + codegen_units.len()); // Translate an allocator shim, if any - // - // If LTO is enabled and we've got some previous LLVM module we translated - // above, then we can just translate directly into that LLVM module. If not, - // however, we need to create a separate module and trans into that. Note - // that the separate translation is critical for the standard library where - // the rlib's object file doesn't have allocator functions but the dylib - // links in an object file that has allocator functions. When we're - // compiling a final LTO artifact, though, there's no need to worry about - // this as we're not working with this dual "rlib/dylib" functionality. - let allocator_module = if tcx.sess.lto() { - None - } else if let Some(kind) = tcx.sess.allocator_kind.get() { + let allocator_module = if let Some(kind) = tcx.sess.allocator_kind.get() { unsafe { + let llmod_id = "allocator"; let (llcx, llmod) = - context::create_context_and_module(tcx.sess, "allocator"); + context::create_context_and_module(tcx.sess, llmod_id); let modules = ModuleLlvm { llmod, llcx, + tm: create_target_machine(tcx.sess), }; time(tcx.sess.time_passes(), "write allocator module", || { allocator::trans(tcx, &modules, kind) @@ -970,7 +803,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Some(ModuleTranslation { name: link::ALLOCATOR_MODULE_NAME.to_string(), - symbol_name_hash: 0, // we always rebuild allocator shims + llmod_id: llmod_id.to_string(), source: ModuleSource::Translated(modules), kind: ModuleKind::Allocator, }) @@ -1002,10 +835,55 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.wait_for_signal_to_translate_item(); ongoing_translation.check_for_errors(tcx.sess); - let _timing_guard = time_graph - .as_ref() - .map(|time_graph| time_graph.start(write::TRANS_WORKER_TIMELINE, - write::TRANS_WORK_PACKAGE_KIND)); + // First, if incremental compilation is enabled, we try to re-use the + // codegen unit from the cache. + if tcx.dep_graph.is_fully_enabled() { + let cgu_id = cgu.work_product_id(); + + // Check whether there is a previous work-product we can + // re-use. Not only must the file exist, and the inputs not + // be dirty, but the hash of the symbols we will generate must + // be the same. + if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { + let dep_node = &DepNode::new(tcx, + DepConstructor::CompileCodegenUnit(cgu.name().clone())); + + // We try to mark the DepNode::CompileCodegenUnit green. If we + // succeed it means that none of the dependencies has changed + // and we can safely re-use. + if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}.rs", cgu.name()); + + let module = ModuleTranslation { + name: cgu.name().to_string(), + source: ModuleSource::Preexisting(buf), + kind: ModuleKind::Regular, + llmod_id, + }; + tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); + write::submit_translated_module_to_llvm(tcx, module, 0); + // Continue to next cgu, this one is done. + continue + } + } else { + // This can happen if files were deleted from the cache + // directory for some reason. We just re-compile then. + } + } + + let _timing_guard = time_graph.as_ref().map(|time_graph| { + time_graph.start(write::TRANS_WORKER_TIMELINE, + write::TRANS_WORK_PACKAGE_KIND, + &format!("codegen {}", cgu.name())) + }); let start_time = Instant::now(); all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); total_trans_time += start_time.elapsed(); @@ -1021,9 +899,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, total_trans_time); if tcx.sess.opts.incremental.is_some() { - DISPOSITIONS.with(|d| { - assert_module_sources::assert_module_sources(tcx, &d.borrow()); - }); + assert_module_sources::assert_module_sources(tcx); } symbol_names_test::report_symbol_names(tcx); @@ -1052,28 +928,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.check_for_errors(tcx.sess); - assert_and_save_dep_graph(tcx, - metadata_incr_hashes, - link_meta); + assert_and_save_dep_graph(tcx); ongoing_translation } -// FIXME(#42293) hopefully once red/green is enabled we're testing everything -// via a method that doesn't require this! -thread_local!(static DISPOSITIONS: RefCell> = Default::default()); - -fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - metadata_incr_hashes: EncodedMetadataHashes, - link_meta: LinkMeta) { +fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { time(tcx.sess.time_passes(), "assert dep graph", || rustc_incremental::assert_dep_graph(tcx)); time(tcx.sess.time_passes(), "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx, - &metadata_incr_hashes, - link_meta.crate_hash)); + || rustc_incremental::save_dep_graph(tcx)); } #[inline(never)] // give this a place in the profiler @@ -1162,7 +1028,7 @@ fn collect_and_partition_translation_items<'a, 'tcx>( let strategy = if tcx.sess.opts.debugging_opts.incremental.is_some() { PartitioningStrategy::PerModule } else { - PartitioningStrategy::FixedUnitCount(tcx.sess.opts.cg.codegen_units) + PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) }; let codegen_units = time(time_passes, "codegen unit partitioning", || { @@ -1175,9 +1041,6 @@ fn collect_and_partition_translation_items<'a, 'tcx>( .collect::>() }); - assert!(tcx.sess.opts.cg.codegen_units == codegen_units.len() || - tcx.sess.opts.debugging_opts.incremental.is_some()); - let translation_items: DefIdSet = items.iter().filter_map(|trans_item| { match *trans_item { TransItem::Fn(ref instance) => Some(instance.def_id()), @@ -1285,38 +1148,19 @@ impl CrateInfo { } fn is_translated_function(tcx: TyCtxt, id: DefId) -> bool { - // FIXME(#42293) needs red/green tracking to avoid failing a bunch of - // existing tests - tcx.dep_graph.with_ignore(|| { - let (all_trans_items, _) = - tcx.collect_and_partition_translation_items(LOCAL_CRATE); - all_trans_items.contains(&id) - }) + let (all_trans_items, _) = + tcx.collect_and_partition_translation_items(LOCAL_CRATE); + all_trans_items.contains(&id) } fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cgu: InternedString) -> Stats { - // FIXME(#42293) needs red/green tracking to avoid failing a bunch of - // existing tests - let cgu = tcx.dep_graph.with_ignore(|| { - tcx.codegen_unit(cgu) - }); + let cgu = tcx.codegen_unit(cgu); let start_time = Instant::now(); - let dep_node = cgu.work_product_dep_node(); - let ((stats, module), _) = - tcx.dep_graph.with_task(dep_node, - tcx, - cgu, - module_translation); + let (stats, module) = module_translation(tcx, cgu); let time_to_translate = start_time.elapsed(); - if tcx.sess.opts.incremental.is_some() { - DISPOSITIONS.with(|d| { - d.borrow_mut().push(module.disposition()); - }); - } - // We assume that the cost to run LLVM on a CGU is proportional to // the time we needed for translating it. let cost = time_to_translate.as_secs() * 1_000_000_000 + @@ -1333,45 +1177,23 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> (Stats, ModuleTranslation) { let cgu_name = cgu.name().to_string(); - let cgu_id = cgu.work_product_id(); - let symbol_name_hash = cgu.compute_symbol_name_hash(tcx); - - // Check whether there is a previous work-product we can - // re-use. Not only must the file exist, and the inputs not - // be dirty, but the hash of the symbols we will generate must - // be the same. - let previous_work_product = - tcx.dep_graph.previous_work_product(&cgu_id).and_then(|work_product| { - if work_product.input_hash == symbol_name_hash { - debug!("trans_reuse_previous_work_products: reusing {:?}", work_product); - Some(work_product) - } else { - if tcx.sess.opts.debugging_opts.incremental_info { - eprintln!("incremental: CGU `{}` invalidated because of \ - changed partitioning hash.", - cgu.name()); - } - debug!("trans_reuse_previous_work_products: \ - not reusing {:?} because hash changed to {:?}", - work_product, symbol_name_hash); - None - } - }); - if let Some(buf) = previous_work_product { - // Don't need to translate this module. - let module = ModuleTranslation { - name: cgu_name, - symbol_name_hash, - source: ModuleSource::Preexisting(buf.clone()), - kind: ModuleKind::Regular, - }; - return (Stats::default(), module); - } + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}-{}.rs", + cgu.name(), + tcx.crate_disambiguator(LOCAL_CRATE) + .to_fingerprint().to_hex()); // Instantiate translation items without filling out definitions yet... let scx = SharedCrateContext::new(tcx); - let lcx = LocalCrateContext::new(&scx, cgu); + let lcx = LocalCrateContext::new(&scx, cgu, &llmod_id); let module = { let ccx = CrateContext::new(&scx, &lcx); let trans_items = ccx.codegen_unit() @@ -1423,31 +1245,14 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let llvm_module = ModuleLlvm { llcx: ccx.llcx(), llmod: ccx.llmod(), + tm: create_target_machine(ccx.sess()), }; - // In LTO mode we inject the allocator shim into the existing - // module. - if ccx.sess().lto() { - if let Some(kind) = ccx.sess().allocator_kind.get() { - time(ccx.sess().time_passes(), "write allocator module", || { - unsafe { - allocator::trans(ccx.tcx(), &llvm_module, kind); - } - }); - } - } - - // Adjust exported symbols for MSVC dllimport - if ccx.sess().target.target.options.is_like_msvc && - ccx.sess().crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) { - create_imps(ccx.sess(), &llvm_module); - } - ModuleTranslation { name: cgu_name, - symbol_name_hash, source: ModuleSource::Translated(llvm_module), kind: ModuleKind::Regular, + llmod_id, } }; @@ -1455,7 +1260,7 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -pub fn provide_local(providers: &mut Providers) { +pub fn provide(providers: &mut Providers) { providers.collect_and_partition_translation_items = collect_and_partition_translation_items; @@ -1471,10 +1276,6 @@ pub fn provide_local(providers: &mut Providers) { providers.compile_codegen_unit = compile_codegen_unit; } -pub fn provide_extern(providers: &mut Providers) { - providers.is_translated_function = is_translated_function; -} - pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { match linkage { Linkage::External => llvm::Linkage::ExternalLinkage, diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 41a238ea8e3fa..e40311af595cd 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -15,15 +15,16 @@ use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; use common::*; -use machine::llalign_of_pref; use type_::Type; use value::Value; use libc::{c_uint, c_char}; use rustc::ty::TyCtxt; -use rustc::session::Session; +use rustc::ty::layout::{Align, Size}; +use rustc::session::{config, Session}; use std::borrow::Cow; use std::ffi::CString; +use std::ops::Range; use std::ptr; use syntax_pos::Span; @@ -112,6 +113,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn set_value_name(&self, value: ValueRef, name: &str) { + let cname = CString::new(name.as_bytes()).unwrap(); + unsafe { + llvm::LLVMSetValueName(value, cname.as_ptr()); + } + } + pub fn position_before(&self, insn: ValueRef) { unsafe { llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); @@ -480,7 +488,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn alloca(&self, ty: Type, name: &str, align: Option) -> ValueRef { + pub fn alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { let builder = Builder::with_ccx(self.ccx); builder.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) @@ -488,7 +496,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { builder.dynamic_alloca(ty, name, align) } - pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Option) -> ValueRef { + pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { self.count_insn("alloca"); unsafe { let alloca = if name.is_empty() { @@ -498,9 +506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), name.as_ptr()) }; - if let Some(align) = align { - llvm::LLVMSetAlignment(alloca, align as c_uint); - } + llvm::LLVMSetAlignment(alloca, align.abi() as c_uint); alloca } } @@ -512,12 +518,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn load(&self, ptr: ValueRef, align: Option) -> ValueRef { + pub fn load(&self, ptr: ValueRef, align: Option) -> ValueRef { self.count_insn("load"); unsafe { let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); if let Some(align) = align { - llvm::LLVMSetAlignment(load, align as c_uint); + llvm::LLVMSetAlignment(load, align.abi() as c_uint); } load } @@ -532,49 +538,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering) -> ValueRef { + pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) -> ValueRef { self.count_insn("load.atomic"); unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); - let align = llalign_of_pref(self.ccx, ty.element_type()); - llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order, - align as c_uint) + let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // However, 64-bit atomic loads on `i686-apple-darwin` appear to + // require `___atomic_load` with ABI-alignment, so it's staying. + llvm::LLVMSetAlignment(load, align.pref() as c_uint); + load } } - pub fn load_range_assert(&self, ptr: ValueRef, lo: u64, - hi: u64, signed: llvm::Bool, - align: Option) -> ValueRef { - let value = self.load(ptr, align); - + pub fn range_metadata(&self, load: ValueRef, range: Range) { unsafe { - let t = llvm::LLVMGetElementType(llvm::LLVMTypeOf(ptr)); - let min = llvm::LLVMConstInt(t, lo, signed); - let max = llvm::LLVMConstInt(t, hi, signed); + let llty = val_ty(load); + let v = [ + C_uint_big(llty, range.start), + C_uint_big(llty, range.end) + ]; - let v = [min, max]; - - llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint, + llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint, llvm::LLVMMDNodeInContext(self.ccx.llcx(), v.as_ptr(), v.len() as c_uint)); } - - value } - pub fn load_nonnull(&self, ptr: ValueRef, align: Option) -> ValueRef { - let value = self.load(ptr, align); + pub fn nonnull_metadata(&self, load: ValueRef) { unsafe { - llvm::LLVMSetMetadata(value, llvm::MD_nonnull as c_uint, + llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint, llvm::LLVMMDNodeInContext(self.ccx.llcx(), ptr::null(), 0)); } - - value } - pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Option) -> 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"); @@ -582,7 +581,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unsafe { let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); if let Some(align) = align { - llvm::LLVMSetAlignment(store, align as c_uint); + llvm::LLVMSetAlignment(store, align.abi() as c_uint); } store } @@ -600,14 +599,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { + pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, + order: AtomicOrdering, align: Align) { debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); self.count_insn("store.atomic"); let ptr = self.check_store(val, ptr); unsafe { - let ty = Type::from_ref(llvm::LLVMTypeOf(ptr)); - let align = llalign_of_pref(self.ccx, ty.element_type()); - llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order, align as c_uint); + let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // Also see `atomic_load` for more context. + llvm::LLVMSetAlignment(store, align.pref() as c_uint); + } + } + + pub fn nontemporal_store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef { + debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); + assert!(!self.llbuilder.is_null()); + self.count_insn("store.nontemporal"); + let ptr = self.check_store(val, ptr); + unsafe { + let insn = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + + // According to LLVM [1] building a nontemporal store must *always* + // point to a metadata value of the integer 1. Who knew? + // + // [1]: http://llvm.org/docs/LangRef.html#store-instruction + let one = C_i32(self.ccx, 1); + let node = llvm::LLVMMDNodeInContext(self.ccx.llcx(), + &one, + 1); + llvm::LLVMSetMetadata(insn, + llvm::MD_nontemporal as c_uint, + node); + insn } } @@ -619,25 +643,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // Simple wrapper around GEP that takes an array of ints and wraps them - // in C_i32() - #[inline] - pub fn gepi(&self, base: ValueRef, ixs: &[usize]) -> ValueRef { - // Small vector optimization. This should catch 100% of the cases that - // we care about. - if ixs.len() < 16 { - let mut small_vec = [ C_i32(self.ccx, 0); 16 ]; - for (small_vec_e, &ix) in small_vec.iter_mut().zip(ixs) { - *small_vec_e = C_i32(self.ccx, ix as i32); - } - self.inbounds_gep(base, &small_vec[..ixs.len()]) - } else { - let v = ixs.iter().map(|i| C_i32(self.ccx, *i as i32)).collect::>(); - self.count_insn("gepi"); - self.inbounds_gep(base, &v) - } - } - pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { self.count_insn("inboundsgep"); unsafe { @@ -646,8 +651,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn struct_gep(&self, ptr: ValueRef, idx: usize) -> ValueRef { + pub fn struct_gep(&self, ptr: ValueRef, idx: u64) -> ValueRef { self.count_insn("structgep"); + assert_eq!(idx as c_uint as u64, idx); unsafe { llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) } @@ -953,16 +959,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn extract_value(&self, agg_val: ValueRef, idx: usize) -> ValueRef { + pub fn extract_value(&self, agg_val: ValueRef, idx: u64) -> ValueRef { self.count_insn("extractvalue"); + assert_eq!(idx as c_uint as u64, idx); unsafe { llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) } } pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, - idx: usize) -> ValueRef { + idx: u64) -> ValueRef { self.count_insn("insertvalue"); + assert_eq!(idx as c_uint as u64, idx); unsafe { llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, noname()) @@ -1144,14 +1152,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { unsafe { - if llvm::LLVMIsUndef(s) == llvm::True { return; } llvm::LLVMAddCase(s, on_val, dest) } } pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { unsafe { - if llvm::LLVMIsUndef(phi) == llvm::True { return; } llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } @@ -1226,4 +1232,36 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return Cow::Owned(casted_args); } + + pub fn lifetime_start(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); + } + + pub fn lifetime_end(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); + } + + /// If LLVM lifetime intrinsic support is enabled (i.e. optimizations + /// on), and `ptr` is nonzero-sized, then extracts the size of `ptr` + /// and the intrinsic for `lt` and passes them to `emit`, which is in + /// charge of generating code to call the passed intrinsic on whatever + /// block of generated code is targetted for the intrinsic. + /// + /// If LLVM lifetime intrinsic support is disabled (i.e. optimizations + /// off) or `ptr` is zero-sized, then no-op (does not call `emit`). + fn call_lifetime_intrinsic(&self, intrinsic: &str, ptr: ValueRef, size: Size) { + if self.ccx.sess().opts.optimize == config::OptLevel::No { + return; + } + + let size = size.bytes(); + if size == 0 { + return; + } + + let lifetime_intrinsic = self.ccx.get_intrinsic(intrinsic); + + let ptr = self.pointercast(ptr, Type::i8p(self.ccx)); + self.call(lifetime_intrinsic, &[C_u64(self.ccx, size), ptr], None); + } } diff --git a/src/librustc_trans/cabi_aarch64.rs b/src/librustc_trans/cabi_aarch64.rs index bf842e6358f87..d5f341f968583 100644 --- a/src/librustc_trans/cabi_aarch64.rs +++ b/src/librustc_trans/cabi_aarch64.rs @@ -14,7 +14,7 @@ use context::CrateContext; fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) -> Option { arg.layout.homogeneous_aggregate(ccx).and_then(|unit| { - let size = arg.layout.size(ccx); + let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. if size > unit.size.checked_mul(4, ccx).unwrap() { @@ -44,10 +44,10 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc return; } if let Some(uniform) = is_homogeneous_aggregate(ccx, ret) { - ret.cast_to(ccx, uniform); + ret.cast_to(uniform); return; } - let size = ret.layout.size(ccx); + let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { let unit = if bits <= 8 { @@ -60,13 +60,13 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc Reg::i64() }; - ret.cast_to(ccx, Uniform { + ret.cast_to(Uniform { unit, total: size }); return; } - ret.make_indirect(ccx); + ret.make_indirect(); } fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { @@ -75,10 +75,10 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc return; } if let Some(uniform) = is_homogeneous_aggregate(ccx, arg) { - arg.cast_to(ccx, uniform); + arg.cast_to(uniform); return; } - let size = arg.layout.size(ccx); + let size = arg.layout.size; let bits = size.bits(); if bits <= 128 { let unit = if bits <= 8 { @@ -91,13 +91,13 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc Reg::i64() }; - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit, total: size }); return; } - arg.make_indirect(ccx); + arg.make_indirect(); } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { diff --git a/src/librustc_trans/cabi_arm.rs b/src/librustc_trans/cabi_arm.rs index 635741b4d1ac5..438053d63b51d 100644 --- a/src/librustc_trans/cabi_arm.rs +++ b/src/librustc_trans/cabi_arm.rs @@ -15,7 +15,7 @@ use llvm::CallConv; fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) -> Option { arg.layout.homogeneous_aggregate(ccx).and_then(|unit| { - let size = arg.layout.size(ccx); + let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. if size > unit.size.checked_mul(4, ccx).unwrap() { @@ -47,12 +47,12 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc if vfp { if let Some(uniform) = is_homogeneous_aggregate(ccx, ret) { - ret.cast_to(ccx, uniform); + ret.cast_to(uniform); return; } } - let size = ret.layout.size(ccx); + let size = ret.layout.size; let bits = size.bits(); if bits <= 32 { let unit = if bits <= 8 { @@ -62,13 +62,13 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc } else { Reg::i32() }; - ret.cast_to(ccx, Uniform { + ret.cast_to(Uniform { unit, total: size }); return; } - ret.make_indirect(ccx); + ret.make_indirect(); } fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>, vfp: bool) { @@ -79,14 +79,14 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc if vfp { if let Some(uniform) = is_homogeneous_aggregate(ccx, arg) { - arg.cast_to(ccx, uniform); + arg.cast_to(uniform); return; } } - let align = arg.layout.align(ccx).abi(); - let total = arg.layout.size(ccx); - arg.cast_to(ccx, Uniform { + let align = arg.layout.align.abi(); + let total = arg.layout.size; + arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total }); diff --git a/src/librustc_trans/cabi_asmjs.rs b/src/librustc_trans/cabi_asmjs.rs index 6fcd3ed581d27..1664251cf897b 100644 --- a/src/librustc_trans/cabi_asmjs.rs +++ b/src/librustc_trans/cabi_asmjs.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{FnType, ArgType, ArgAttribute, LayoutExt, Uniform}; +use abi::{FnType, ArgType, LayoutExt, Uniform}; use context::CrateContext; // Data layout: e-p:32:32-i64:64-v128:32:128-n32-S128 @@ -19,9 +19,9 @@ use context::CrateContext; fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { if ret.layout.is_aggregate() { if let Some(unit) = ret.layout.homogeneous_aggregate(ccx) { - let size = ret.layout.size(ccx); + let size = ret.layout.size; if unit.size == size { - ret.cast_to(ccx, Uniform { + ret.cast_to(Uniform { unit, total: size }); @@ -29,14 +29,13 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc } } - ret.make_indirect(ccx); + ret.make_indirect(); } } -fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { +fn classify_arg_ty(arg: &mut ArgType) { if arg.layout.is_aggregate() { - arg.make_indirect(ccx); - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } } @@ -47,6 +46,6 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(arg); } } diff --git a/src/librustc_trans/cabi_hexagon.rs b/src/librustc_trans/cabi_hexagon.rs index 1acda72675c31..7e7e483fea0c0 100644 --- a/src/librustc_trans/cabi_hexagon.rs +++ b/src/librustc_trans/cabi_hexagon.rs @@ -11,33 +11,32 @@ #![allow(non_upper_case_globals)] use abi::{FnType, ArgType, LayoutExt}; -use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { - if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 64 { - ret.make_indirect(ccx); +fn classify_ret_ty(ret: &mut ArgType) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { + ret.make_indirect(); } else { ret.extend_integer_width_to(32); } } -fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { - if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 64 { - arg.make_indirect(ccx); +fn classify_arg_ty(arg: &mut ArgType) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { + arg.make_indirect(); } else { arg.extend_integer_width_to(32); } } -pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { +pub fn compute_abi_info(fty: &mut FnType) { if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(&mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(arg); } } diff --git a/src/librustc_trans/cabi_mips.rs b/src/librustc_trans/cabi_mips.rs index b7b60859d4a04..fe61670a1086f 100644 --- a/src/librustc_trans/cabi_mips.rs +++ b/src/librustc_trans/cabi_mips.rs @@ -8,45 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; -use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform}; +use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { +use rustc::ty::layout::Size; + +fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ret: &mut ArgType<'tcx>, + offset: &mut Size) { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); } else { - ret.make_indirect(ccx); + ret.make_indirect(); + *offset += ccx.tcx().data_layout.pointer_size; } } -fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) { - let size = arg.layout.size(ccx); - let mut align = arg.layout.align(ccx).abi(); - align = cmp::min(cmp::max(align, 4), 8); +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut Size) { + let dl = &ccx.tcx().data_layout; + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit: Reg::i32(), total: size }); - if ((align - 1) & *offset) > 0 { - arg.pad_with(ccx, Reg::i32()); + if !offset.is_abi_aligned(align) { + arg.pad_with(Reg::i32()); } } else { arg.extend_integer_width_to(32); } - *offset = align_up_to(*offset, align); - *offset += align_up_to(size.bytes(), align); + *offset = offset.abi_align(align) + size.abi_align(align); } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { + let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(ccx, &mut fty.ret, &mut offset); } - let mut offset = if fty.ret.is_indirect() { 4 } else { 0 }; for arg in &mut fty.args { if arg.is_ignore() { continue; } classify_arg_ty(ccx, arg, &mut offset); diff --git a/src/librustc_trans/cabi_mips64.rs b/src/librustc_trans/cabi_mips64.rs index dff75e628de10..16d0cfe072d57 100644 --- a/src/librustc_trans/cabi_mips64.rs +++ b/src/librustc_trans/cabi_mips64.rs @@ -8,45 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; -use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform}; +use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { +use rustc::ty::layout::Size; + +fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ret: &mut ArgType<'tcx>, + offset: &mut Size) { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(64); } else { - ret.make_indirect(ccx); + ret.make_indirect(); + *offset += ccx.tcx().data_layout.pointer_size; } } -fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) { - let size = arg.layout.size(ccx); - let mut align = arg.layout.align(ccx).abi(); - align = cmp::min(cmp::max(align, 4), 8); +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut Size) { + let dl = &ccx.tcx().data_layout; + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit: Reg::i64(), total: size }); - if ((align - 1) & *offset) > 0 { - arg.pad_with(ccx, Reg::i64()); + if !offset.is_abi_aligned(align) { + arg.pad_with(Reg::i64()); } } else { arg.extend_integer_width_to(64); } - *offset = align_up_to(*offset, align); - *offset += align_up_to(size.bytes(), align); + *offset = offset.abi_align(align) + size.abi_align(align); } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { + let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(ccx, &mut fty.ret, &mut offset); } - let mut offset = if fty.ret.is_indirect() { 8 } else { 0 }; for arg in &mut fty.args { if arg.is_ignore() { continue; } classify_arg_ty(ccx, arg, &mut offset); diff --git a/src/librustc_trans/cabi_msp430.rs b/src/librustc_trans/cabi_msp430.rs index 546bb5ad9b44e..d270886a19cd1 100644 --- a/src/librustc_trans/cabi_msp430.rs +++ b/src/librustc_trans/cabi_msp430.rs @@ -12,7 +12,6 @@ // http://www.ti.com/lit/an/slaa534/slaa534.pdf use abi::{ArgType, FnType, LayoutExt}; -use context::CrateContext; // 3.5 Structures or Unions Passed and Returned by Reference // @@ -20,31 +19,31 @@ use context::CrateContext; // returned by reference. To pass a structure or union by reference, the caller // places its address in the appropriate location: either in a register or on // the stack, according to its position in the argument list. (..)" -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { - if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 32 { - ret.make_indirect(ccx); +fn classify_ret_ty(ret: &mut ArgType) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 { + ret.make_indirect(); } else { ret.extend_integer_width_to(16); } } -fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { - if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 32 { - arg.make_indirect(ccx); +fn classify_arg_ty(arg: &mut ArgType) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 { + arg.make_indirect(); } else { arg.extend_integer_width_to(16); } } -pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { +pub fn compute_abi_info(fty: &mut FnType) { if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(&mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(arg); } } diff --git a/src/librustc_trans/cabi_nvptx.rs b/src/librustc_trans/cabi_nvptx.rs index 3873752b25470..69cfc690a9f9d 100644 --- a/src/librustc_trans/cabi_nvptx.rs +++ b/src/librustc_trans/cabi_nvptx.rs @@ -12,33 +12,32 @@ // http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability use abi::{ArgType, FnType, LayoutExt}; -use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { - if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 32 { - ret.make_indirect(ccx); +fn classify_ret_ty(ret: &mut ArgType) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 { + ret.make_indirect(); } else { ret.extend_integer_width_to(32); } } -fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { - if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 32 { - arg.make_indirect(ccx); +fn classify_arg_ty(arg: &mut ArgType) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 { + arg.make_indirect(); } else { arg.extend_integer_width_to(32); } } -pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { +pub fn compute_abi_info(fty: &mut FnType) { if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(&mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(arg); } } diff --git a/src/librustc_trans/cabi_nvptx64.rs b/src/librustc_trans/cabi_nvptx64.rs index 24bf4920c16c1..4d76c15603800 100644 --- a/src/librustc_trans/cabi_nvptx64.rs +++ b/src/librustc_trans/cabi_nvptx64.rs @@ -12,33 +12,32 @@ // http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability use abi::{ArgType, FnType, LayoutExt}; -use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { - if ret.layout.is_aggregate() && ret.layout.size(ccx).bits() > 64 { - ret.make_indirect(ccx); +fn classify_ret_ty(ret: &mut ArgType) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { + ret.make_indirect(); } else { ret.extend_integer_width_to(64); } } -fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { - if arg.layout.is_aggregate() && arg.layout.size(ccx).bits() > 64 { - arg.make_indirect(ccx); +fn classify_arg_ty(arg: &mut ArgType) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { + arg.make_indirect(); } else { arg.extend_integer_width_to(64); } } -pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { +pub fn compute_abi_info(fty: &mut FnType) { if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(&mut fty.ret); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(arg); } } diff --git a/src/librustc_trans/cabi_powerpc.rs b/src/librustc_trans/cabi_powerpc.rs index f951ac76391f6..c3c8c745e3a61 100644 --- a/src/librustc_trans/cabi_powerpc.rs +++ b/src/librustc_trans/cabi_powerpc.rs @@ -8,46 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{align_up_to, FnType, ArgType, LayoutExt, Reg, Uniform}; +use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; use context::CrateContext; -use std::cmp; +use rustc::ty::layout::Size; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { +fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ret: &mut ArgType<'tcx>, + offset: &mut Size) { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); } else { - ret.make_indirect(ccx); + ret.make_indirect(); + *offset += ccx.tcx().data_layout.pointer_size; } } -fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) { - let size = arg.layout.size(ccx); - let mut align = arg.layout.align(ccx).abi(); - align = cmp::min(cmp::max(align, 4), 8); +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut Size) { + let dl = &ccx.tcx().data_layout; + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit: Reg::i32(), total: size }); - if ((align - 1) & *offset) > 0 { - arg.pad_with(ccx, Reg::i32()); + if !offset.is_abi_aligned(align) { + arg.pad_with(Reg::i32()); } } else { arg.extend_integer_width_to(32); } - *offset = align_up_to(*offset, align); - *offset += align_up_to(size.bytes(), align); + *offset = offset.abi_align(align) + size.abi_align(align); } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { + let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(ccx, &mut fty.ret, &mut offset); } - let mut offset = if fty.ret.is_indirect() { 4 } else { 0 }; for arg in &mut fty.args { if arg.is_ignore() { continue; } classify_arg_ty(ccx, arg, &mut offset); diff --git a/src/librustc_trans/cabi_powerpc64.rs b/src/librustc_trans/cabi_powerpc64.rs index fb5472eb6ae1f..2206a4fa00cc3 100644 --- a/src/librustc_trans/cabi_powerpc64.rs +++ b/src/librustc_trans/cabi_powerpc64.rs @@ -28,25 +28,23 @@ fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, abi: ABI) -> Option { arg.layout.homogeneous_aggregate(ccx).and_then(|unit| { - let size = arg.layout.size(ccx); - // ELFv1 only passes one-member aggregates transparently. // ELFv2 passes up to eight uniquely addressable members. - if (abi == ELFv1 && size > unit.size) - || size > unit.size.checked_mul(8, ccx).unwrap() { + if (abi == ELFv1 && arg.layout.size > unit.size) + || arg.layout.size > unit.size.checked_mul(8, ccx).unwrap() { return None; } let valid_unit = match unit.kind { RegKind::Integer => false, RegKind::Float => true, - RegKind::Vector => size.bits() == 128 + RegKind::Vector => arg.layout.size.bits() == 128 }; if valid_unit { Some(Uniform { unit, - total: size + total: arg.layout.size }) } else { None @@ -62,16 +60,16 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc // The ELFv1 ABI doesn't return aggregates in registers if abi == ELFv1 { - ret.make_indirect(ccx); + ret.make_indirect(); return; } if let Some(uniform) = is_homogeneous_aggregate(ccx, ret, abi) { - ret.cast_to(ccx, uniform); + ret.cast_to(uniform); return; } - let size = ret.layout.size(ccx); + let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { let unit = if bits <= 8 { @@ -84,14 +82,14 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc Reg::i64() }; - ret.cast_to(ccx, Uniform { + ret.cast_to(Uniform { unit, total: size }); return; } - ret.make_indirect(ccx); + ret.make_indirect(); } fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>, abi: ABI) { @@ -101,11 +99,11 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc } if let Some(uniform) = is_homogeneous_aggregate(ccx, arg, abi) { - arg.cast_to(ccx, uniform); + arg.cast_to(uniform); return; } - let size = arg.layout.size(ccx); + let size = arg.layout.size; let (unit, total) = match abi { ELFv1 => { // In ELFv1, aggregates smaller than a doubleword should appear in @@ -124,7 +122,7 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc }, }; - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit, total }); diff --git a/src/librustc_trans/cabi_s390x.rs b/src/librustc_trans/cabi_s390x.rs index fedebea3f4c99..9fb460043ae81 100644 --- a/src/librustc_trans/cabi_s390x.rs +++ b/src/librustc_trans/cabi_s390x.rs @@ -14,23 +14,27 @@ use abi::{FnType, ArgType, LayoutExt, Reg}; use context::CrateContext; -use rustc::ty::layout::{self, Layout, TyLayout}; +use rustc::ty::layout::{self, TyLayout}; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { - if !ret.layout.is_aggregate() && ret.layout.size(ccx).bits() <= 64 { +fn classify_ret_ty(ret: &mut ArgType) { + if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { ret.extend_integer_width_to(64); } else { - ret.make_indirect(ccx); + ret.make_indirect(); } } fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, layout: TyLayout<'tcx>) -> bool { - match *layout { - Layout::Scalar { value: layout::F32, .. } | - Layout::Scalar { value: layout::F64, .. } => true, - Layout::Univariant { .. } => { - if layout.field_count() == 1 { + match layout.abi { + layout::Abi::Scalar(ref scalar) => { + match scalar.value { + layout::F32 | layout::F64 => true, + _ => false + } + } + layout::Abi::Aggregate { .. } => { + if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { is_single_fp_element(ccx, layout.field(ccx, 0)) } else { false @@ -41,32 +45,31 @@ fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { - let size = arg.layout.size(ccx); - if !arg.layout.is_aggregate() && size.bits() <= 64 { + if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 { arg.extend_integer_width_to(64); return; } if is_single_fp_element(ccx, arg.layout) { - match size.bytes() { - 4 => arg.cast_to(ccx, Reg::f32()), - 8 => arg.cast_to(ccx, Reg::f64()), - _ => arg.make_indirect(ccx) + match arg.layout.size.bytes() { + 4 => arg.cast_to(Reg::f32()), + 8 => arg.cast_to(Reg::f64()), + _ => arg.make_indirect() } } else { - match size.bytes() { - 1 => arg.cast_to(ccx, Reg::i8()), - 2 => arg.cast_to(ccx, Reg::i16()), - 4 => arg.cast_to(ccx, Reg::i32()), - 8 => arg.cast_to(ccx, Reg::i64()), - _ => arg.make_indirect(ccx) + match arg.layout.size.bytes() { + 1 => arg.cast_to(Reg::i8()), + 2 => arg.cast_to(Reg::i16()), + 4 => arg.cast_to(Reg::i32()), + 8 => arg.cast_to(Reg::i64()), + _ => arg.make_indirect() } } } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(&mut fty.ret); } for arg in &mut fty.args { diff --git a/src/librustc_trans/cabi_sparc.rs b/src/librustc_trans/cabi_sparc.rs index c17901e1adebc..fe61670a1086f 100644 --- a/src/librustc_trans/cabi_sparc.rs +++ b/src/librustc_trans/cabi_sparc.rs @@ -8,45 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cmp; -use abi::{align_up_to, ArgType, FnType, LayoutExt, Reg, Uniform}; +use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; use context::CrateContext; -fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) { +use rustc::ty::layout::Size; + +fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ret: &mut ArgType<'tcx>, + offset: &mut Size) { if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); } else { - ret.make_indirect(ccx); + ret.make_indirect(); + *offset += ccx.tcx().data_layout.pointer_size; } } -fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut u64) { - let size = arg.layout.size(ccx); - let mut align = arg.layout.align(ccx).abi(); - align = cmp::min(cmp::max(align, 4), 8); +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut Size) { + let dl = &ccx.tcx().data_layout; + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { - arg.cast_to(ccx, Uniform { + arg.cast_to(Uniform { unit: Reg::i32(), total: size }); - if ((align - 1) & *offset) > 0 { - arg.pad_with(ccx, Reg::i32()); + if !offset.is_abi_aligned(align) { + arg.pad_with(Reg::i32()); } } else { - arg.extend_integer_width_to(32) + arg.extend_integer_width_to(32); } - *offset = align_up_to(*offset, align); - *offset += align_up_to(size.bytes(), align); + *offset = offset.abi_align(align) + size.abi_align(align); } pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { + let mut offset = Size::from_bytes(0); if !fty.ret.is_ignore() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(ccx, &mut fty.ret, &mut offset); } - let mut offset = if fty.ret.is_indirect() { 4 } else { 0 }; for arg in &mut fty.args { if arg.is_ignore() { continue; } classify_arg_ty(ccx, arg, &mut offset); diff --git a/src/librustc_trans/cabi_sparc64.rs b/src/librustc_trans/cabi_sparc64.rs index 8383007550e1e..7c52e27fa67d1 100644 --- a/src/librustc_trans/cabi_sparc64.rs +++ b/src/librustc_trans/cabi_sparc64.rs @@ -16,23 +16,21 @@ use context::CrateContext; fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) -> Option { arg.layout.homogeneous_aggregate(ccx).and_then(|unit| { - let size = arg.layout.size(ccx); - // Ensure we have at most eight uniquely addressable members. - if size > unit.size.checked_mul(8, ccx).unwrap() { + if arg.layout.size > unit.size.checked_mul(8, ccx).unwrap() { return None; } let valid_unit = match unit.kind { RegKind::Integer => false, RegKind::Float => true, - RegKind::Vector => size.bits() == 128 + RegKind::Vector => arg.layout.size.bits() == 128 }; if valid_unit { Some(Uniform { unit, - total: size + total: arg.layout.size }) } else { None @@ -47,10 +45,10 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc } if let Some(uniform) = is_homogeneous_aggregate(ccx, ret) { - ret.cast_to(ccx, uniform); + ret.cast_to(uniform); return; } - let size = ret.layout.size(ccx); + let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { let unit = if bits <= 8 { @@ -63,7 +61,7 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc Reg::i64() }; - ret.cast_to(ccx, Uniform { + ret.cast_to(Uniform { unit, total: size }); @@ -71,7 +69,7 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc } // don't return aggregates in registers - ret.make_indirect(ccx); + ret.make_indirect(); } fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) { @@ -81,12 +79,12 @@ fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tc } if let Some(uniform) = is_homogeneous_aggregate(ccx, arg) { - arg.cast_to(ccx, uniform); + arg.cast_to(uniform); return; } - let total = arg.layout.size(ccx); - arg.cast_to(ccx, Uniform { + let total = arg.layout.size; + arg.cast_to(Uniform { unit: Reg::i64(), total }); diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index 49634d6e78ce9..6fd0140c39901 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind}; +use abi::{ArgAttribute, FnType, LayoutExt, PassMode, Reg, RegKind}; use common::CrateContext; -use rustc::ty::layout::{self, Layout, TyLayout}; +use rustc::ty::layout::{self, TyLayout}; #[derive(PartialEq)] pub enum Flavor { @@ -21,11 +21,15 @@ pub enum Flavor { fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, layout: TyLayout<'tcx>) -> bool { - match *layout { - Layout::Scalar { value: layout::F32, .. } | - Layout::Scalar { value: layout::F64, .. } => true, - Layout::Univariant { .. } => { - if layout.field_count() == 1 { + match layout.abi { + layout::Abi::Scalar(ref scalar) => { + match scalar.value { + layout::F32 | layout::F64 => true, + _ => false + } + } + layout::Abi::Aggregate { .. } => { + if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { is_single_fp_element(ccx, layout.field(ccx, 0)) } else { false @@ -50,27 +54,25 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let t = &ccx.sess().target.target; if t.options.is_like_osx || t.options.is_like_windows || t.options.is_like_openbsd { - let size = fty.ret.layout.size(ccx); - // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. if !t.options.is_like_msvc && is_single_fp_element(ccx, fty.ret.layout) { - match size.bytes() { - 4 => fty.ret.cast_to(ccx, Reg::f32()), - 8 => fty.ret.cast_to(ccx, Reg::f64()), - _ => fty.ret.make_indirect(ccx) + match fty.ret.layout.size.bytes() { + 4 => fty.ret.cast_to(Reg::f32()), + 8 => fty.ret.cast_to(Reg::f64()), + _ => fty.ret.make_indirect() } } else { - match size.bytes() { - 1 => fty.ret.cast_to(ccx, Reg::i8()), - 2 => fty.ret.cast_to(ccx, Reg::i16()), - 4 => fty.ret.cast_to(ccx, Reg::i32()), - 8 => fty.ret.cast_to(ccx, Reg::i64()), - _ => fty.ret.make_indirect(ccx) + match fty.ret.layout.size.bytes() { + 1 => fty.ret.cast_to(Reg::i8()), + 2 => fty.ret.cast_to(Reg::i16()), + 4 => fty.ret.cast_to(Reg::i32()), + 8 => fty.ret.cast_to(Reg::i64()), + _ => fty.ret.make_indirect() } } } else { - fty.ret.make_indirect(ccx); + fty.ret.make_indirect(); } } else { fty.ret.extend_integer_width_to(32); @@ -80,8 +82,7 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, for arg in &mut fty.args { if arg.is_ignore() { continue; } if arg.layout.is_aggregate() { - arg.make_indirect(ccx); - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } else { arg.extend_integer_width_to(32); } @@ -100,17 +101,24 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let mut free_regs = 2; for arg in &mut fty.args { - if arg.is_ignore() || arg.is_indirect() { continue; } + let attrs = match arg.mode { + PassMode::Ignore | + PassMode::Indirect(_) => continue, + PassMode::Direct(ref mut attrs) => attrs, + PassMode::Pair(..) | + PassMode::Cast(_) => { + bug!("x86 shouldn't be passing arguments by {:?}", arg.mode) + } + }; // At this point we know this must be a primitive of sorts. let unit = arg.layout.homogeneous_aggregate(ccx).unwrap(); - let size = arg.layout.size(ccx); - assert_eq!(unit.size, size); + assert_eq!(unit.size, arg.layout.size); if unit.kind == RegKind::Float { continue; } - let size_in_regs = (size.bits() + 31) / 32; + let size_in_regs = (arg.layout.size.bits() + 31) / 32; if size_in_regs == 0 { continue; @@ -122,8 +130,8 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, free_regs -= size_in_regs; - if size.bits() <= 32 && unit.kind == RegKind::Integer { - arg.attrs.set(ArgAttribute::InReg); + if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { + attrs.set(ArgAttribute::InReg); } if free_regs == 0 { diff --git a/src/librustc_trans/cabi_x86_64.rs b/src/librustc_trans/cabi_x86_64.rs index 2cfab7df8b30b..81eb362ca46dc 100644 --- a/src/librustc_trans/cabi_x86_64.rs +++ b/src/librustc_trans/cabi_x86_64.rs @@ -11,10 +11,10 @@ // The classification code for the x86_64 ABI is taken from the clay language // https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp -use abi::{ArgType, ArgAttribute, CastTarget, FnType, LayoutExt, Reg, RegKind}; +use abi::{ArgType, CastTarget, FnType, LayoutExt, Reg, RegKind}; use context::CrateContext; -use rustc::ty::layout::{self, Layout, TyLayout, Size}; +use rustc::ty::layout::{self, TyLayout, Size}; #[derive(Clone, Copy, PartialEq, Debug)] enum Class { @@ -27,16 +27,16 @@ enum Class { #[derive(Clone, Copy, Debug)] struct Memory; -// Currently supported vector size (AVX). -const LARGEST_VECTOR_SIZE: usize = 256; +// Currently supported vector size (AVX-512). +const LARGEST_VECTOR_SIZE: usize = 512; const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64; fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>) -> Result<[Class; MAX_EIGHTBYTES], Memory> { fn unify(cls: &mut [Class], - off: u64, + off: Size, c: Class) { - let i = (off / 8) as usize; + let i = (off.bytes() / 8) as usize; let to_write = match (cls[i], c) { (Class::None, _) => c, (_, Class::None) => return, @@ -55,20 +55,21 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>) fn classify<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, layout: TyLayout<'tcx>, cls: &mut [Class], - off: u64) + off: Size) -> Result<(), Memory> { - if off % layout.align(ccx).abi() != 0 { - if layout.size(ccx).bytes() > 0 { + if !off.is_abi_aligned(layout.align) { + if !layout.is_zst() { return Err(Memory); } return Ok(()); } - match *layout { - Layout::Scalar { value, .. } | - Layout::RawNullablePointer { value, .. } => { - let reg = match value { - layout::Int(_) | + match layout.abi { + layout::Abi::Uninhabited => {} + + layout::Abi::Scalar(ref scalar) => { + let reg = match scalar.value { + layout::Int(..) | layout::Pointer => Class::Int, layout::F32 | layout::F64 => Class::Sse @@ -76,59 +77,43 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>) unify(cls, off, reg); } - Layout::CEnum { .. } => { - unify(cls, off, Class::Int); - } - - Layout::Vector { element, count } => { + layout::Abi::Vector => { unify(cls, off, Class::Sse); // everything after the first one is the upper // half of a register. - let eltsz = element.size(ccx).bytes(); - for i in 1..count { - unify(cls, off + i * eltsz, Class::SseUp); + for i in 1..layout.fields.count() { + let field_off = off + layout.fields.offset(i); + unify(cls, field_off, Class::SseUp); } } - Layout::Array { count, .. } => { - if count > 0 { - let elt = layout.field(ccx, 0); - let eltsz = elt.size(ccx).bytes(); - for i in 0..count { - classify(ccx, elt, cls, off + i * eltsz)?; + layout::Abi::ScalarPair(..) | + layout::Abi::Aggregate { .. } => { + match layout.variants { + layout::Variants::Single { .. } => { + for i in 0..layout.fields.count() { + let field_off = off + layout.fields.offset(i); + classify(ccx, layout.field(ccx, i), cls, field_off)?; + } } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => return Err(Memory), } } - Layout::Univariant { ref variant, .. } => { - for i in 0..layout.field_count() { - let field_off = off + variant.offsets[i].bytes(); - classify(ccx, layout.field(ccx, i), cls, field_off)?; - } - } - - Layout::UntaggedUnion { .. } => { - for i in 0..layout.field_count() { - classify(ccx, layout.field(ccx, i), cls, off)?; - } - } - - Layout::FatPointer { .. } | - Layout::General { .. } | - Layout::StructWrappedNullablePointer { .. } => return Err(Memory) } Ok(()) } - let n = ((arg.layout.size(ccx).bytes() + 7) / 8) as usize; + let n = ((arg.layout.size.bytes() + 7) / 8) as usize; if n > MAX_EIGHTBYTES { return Err(Memory); } let mut cls = [Class::None; MAX_EIGHTBYTES]; - classify(ccx, arg.layout, &mut cls, 0)?; + classify(ccx, arg.layout, &mut cls, Size::from_bytes(0))?; if n > 2 { if cls[0] != Class::Sse { return Err(Memory); @@ -153,7 +138,7 @@ fn classify_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &ArgType<'tcx>) Ok(cls) } -fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option { +fn reg_component(cls: &[Class], i: &mut usize, size: Size) -> Option { if *i >= cls.len() { return None; } @@ -162,7 +147,7 @@ fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option { Class::None => None, Class::Int => { *i += 1; - Some(match size { + Some(match size.bytes() { 1 => Reg::i8(), 2 => Reg::i16(), 3 | @@ -174,14 +159,14 @@ fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option { let vec_len = 1 + cls[*i+1..].iter().take_while(|&&c| c == Class::SseUp).count(); *i += vec_len; Some(if vec_len == 1 { - match size { + match size.bytes() { 4 => Reg::f32(), _ => Reg::f64() } } else { Reg { kind: RegKind::Vector, - size: Size::from_bytes(vec_len as u64 * 8) + size: Size::from_bytes(8) * (vec_len as u64) } }) } @@ -189,17 +174,17 @@ fn reg_component(cls: &[Class], i: &mut usize, size: u64) -> Option { } } -fn cast_target(cls: &[Class], size: u64) -> CastTarget { +fn cast_target(cls: &[Class], size: Size) -> CastTarget { let mut i = 0; let lo = reg_component(cls, &mut i, size).unwrap(); - let offset = i as u64 * 8; + let offset = Size::from_bytes(8) * (i as u64); let target = if size <= offset { CastTarget::from(lo) } else { let hi = reg_component(cls, &mut i, size - offset).unwrap(); CastTarget::Pair(lo, hi) }; - assert_eq!(reg_component(cls, &mut i, 0), None); + assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None); target } @@ -229,11 +214,11 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType }; if in_mem { - arg.make_indirect(ccx); if is_arg { - arg.attrs.set(ArgAttribute::ByVal); + arg.make_indirect_byval(); } else { // `sret` parameter thus one less integer register available + arg.make_indirect(); int_regs -= 1; } } else { @@ -242,8 +227,8 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType sse_regs -= needed_sse; if arg.layout.is_aggregate() { - let size = arg.layout.size(ccx).bytes(); - arg.cast_to(ccx, cast_target(cls.as_ref().unwrap(), size)) + let size = arg.layout.size; + arg.cast_to(cast_target(cls.as_ref().unwrap(), size)) } else { arg.extend_integer_width_to(32); } diff --git a/src/librustc_trans/cabi_x86_win64.rs b/src/librustc_trans/cabi_x86_win64.rs index 39e728d4e4f9b..473c00120a740 100644 --- a/src/librustc_trans/cabi_x86_win64.rs +++ b/src/librustc_trans/cabi_x86_win64.rs @@ -8,32 +8,36 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use abi::{ArgType, FnType, LayoutExt, Reg}; -use common::CrateContext; +use abi::{ArgType, FnType, Reg}; -use rustc::ty::layout::Layout; +use rustc::ty::layout; // Win64 ABI: http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx -pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) { - let fixup = |a: &mut ArgType<'tcx>| { - let size = a.layout.size(ccx); - if a.layout.is_aggregate() { - match size.bits() { - 8 => a.cast_to(ccx, Reg::i8()), - 16 => a.cast_to(ccx, Reg::i16()), - 32 => a.cast_to(ccx, Reg::i32()), - 64 => a.cast_to(ccx, Reg::i64()), - _ => a.make_indirect(ccx) - }; - } else { - if let Layout::Vector { .. } = *a.layout { +pub fn compute_abi_info(fty: &mut FnType) { + let fixup = |a: &mut ArgType| { + match a.layout.abi { + layout::Abi::Uninhabited => {} + layout::Abi::ScalarPair(..) | + layout::Abi::Aggregate { .. } => { + match a.layout.size.bits() { + 8 => a.cast_to(Reg::i8()), + 16 => a.cast_to(Reg::i16()), + 32 => a.cast_to(Reg::i32()), + 64 => a.cast_to(Reg::i64()), + _ => a.make_indirect() + } + } + layout::Abi::Vector => { // FIXME(eddyb) there should be a size cap here // (probably what clang calls "illegal vectors"). - } else if size.bytes() > 8 { - a.make_indirect(ccx); - } else { - a.extend_integer_width_to(32); + } + layout::Abi::Scalar(_) => { + if a.layout.size.bytes() > 8 { + a.make_indirect(); + } else { + a.extend_integer_width_to(32); + } } } }; diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 52e6dce24ed92..4afeac2e8f589 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -19,11 +19,15 @@ use common::{self, CrateContext}; use consts; use declare; use llvm::{self, ValueRef}; -use monomorphize::{self, Instance}; +use monomorphize::Instance; +use type_of::LayoutLlvmExt; + use rustc::hir::def_id::DefId; -use rustc::ty::TypeFoldable; +use rustc::ty::{self, TypeFoldable}; +use rustc::ty::layout::LayoutOf; +use rustc::traits; use rustc::ty::subst::Substs; -use type_of; +use rustc_back::PanicStrategy; /// Translates a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -54,7 +58,7 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Create a fn pointer with the substituted signature. let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(ccx, fn_ty)); - let llptrty = type_of::type_of(ccx, fn_ptr_ty); + let llptrty = ccx.layout_of(fn_ptr_ty).llvm_type(ccx); let llfn = if let Some(llfn) = declare::get_declared_value(ccx, &sym) { // This is subtle and surprising, but sometimes we have to bitcast @@ -104,8 +108,10 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // *in Rust code* may unwind. Foreign items like `extern "C" { // fn foo(); }` are assumed not to unwind **unless** they have // a `#[unwind]` attribute. - if !tcx.is_foreign_item(instance_def_id) { - attributes::unwind(llfn, true); + if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if !tcx.is_foreign_item(instance_def_id) { + attributes::unwind(llfn, true); + } } // Apply an appropriate linkage/visibility value to our item that we @@ -155,17 +161,14 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } - // FIXME(#42293) we should actually track this, but fails too many tests - // today. - tcx.dep_graph.with_ignore(|| { - if ccx.use_dll_storage_attrs() && - tcx.is_dllimport_foreign_item(instance_def_id) - { - unsafe { - llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); - } + if ccx.use_dll_storage_attrs() && + tcx.is_dllimport_foreign_item(instance_def_id) + { + unsafe { + llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } - }); + } + llfn }; @@ -179,5 +182,13 @@ pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, substs: &'tcx Substs<'tcx>) -> ValueRef { - get_fn(ccx, monomorphize::resolve(ccx.tcx(), def_id, substs)) + get_fn( + ccx, + ty::Instance::resolve( + ccx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs + ).unwrap() + ) } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 52607904f73c4..405647af324d6 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -18,17 +18,17 @@ use llvm::{True, False, Bool, OperandBundleDef}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::middle::lang_items::LangItem; +use abi; use base; use builder::Builder; use consts; use declare; -use machine; -use monomorphize; use type_::Type; +use type_of::LayoutLlvmExt; use value::Value; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{Layout, LayoutTyper}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::hir; @@ -36,111 +36,11 @@ use libc::{c_uint, c_char}; use std::iter; use syntax::abi::Abi; -use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::{Span, DUMMY_SP}; pub use context::{CrateContext, SharedCrateContext}; -pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - 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 { - let layout = ccx.layout_of(ty); - match *layout { - Layout::CEnum { .. } | - Layout::Scalar { .. } | - Layout::Vector { .. } => true, - - Layout::FatPointer { .. } => false, - - Layout::Array { .. } | - Layout::Univariant { .. } | - Layout::General { .. } | - Layout::UntaggedUnion { .. } | - Layout::RawNullablePointer { .. } | - Layout::StructWrappedNullablePointer { .. } => { - !layout.is_unsized() && layout.size(ccx).bytes() == 0 - } - } -} - -/// Returns Some([a, b]) if the type has a pair of fields with types a and b. -pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - -> Option<[Ty<'tcx>; 2]> { - match ty.sty { - ty::TyAdt(adt, substs) => { - assert_eq!(adt.variants.len(), 1); - let fields = &adt.variants[0].fields; - if fields.len() != 2 { - return None; - } - Some([monomorphize::field_ty(ccx.tcx(), substs, &fields[0]), - monomorphize::field_ty(ccx.tcx(), substs, &fields[1])]) - } - ty::TyClosure(def_id, substs) => { - let mut tys = substs.upvar_tys(def_id, ccx.tcx()); - tys.next().and_then(|first_ty| tys.next().and_then(|second_ty| { - if tys.next().is_some() { - None - } else { - Some([first_ty, second_ty]) - } - })) - } - ty::TyGenerator(def_id, substs, _) => { - let mut tys = substs.field_tys(def_id, ccx.tcx()); - tys.next().and_then(|first_ty| tys.next().and_then(|second_ty| { - if tys.next().is_some() { - None - } else { - Some([first_ty, second_ty]) - } - })) - } - ty::TyTuple(tys, _) => { - if tys.len() != 2 { - return None; - } - Some([tys[0], tys[1]]) - } - _ => None - } -} - -/// Returns true if the type is represented as a pair of immediates. -pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - -> bool { - match *ccx.layout_of(ty) { - Layout::FatPointer { .. } => true, - Layout::Univariant { ref variant, .. } => { - // There must be only 2 fields. - if variant.offsets.len() != 2 { - return false; - } - - match type_pair_fields(ccx, ty) { - Some([a, b]) => { - type_is_immediate(ccx, a) && type_is_immediate(ccx, b) - } - None => false - } - } - _ => false - } -} - -/// Identify types which have size zero at runtime. -pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - let layout = ccx.layout_of(ty); - !layout.is_unsized() && layout.size(ccx).bytes() == 0 -} - pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) } @@ -246,17 +146,13 @@ pub fn C_uint(t: Type, i: u64) -> ValueRef { } } -pub fn C_big_integral(t: Type, u: u128) -> ValueRef { +pub fn C_uint_big(t: Type, u: u128) -> ValueRef { unsafe { - let words = [u as u64, u.wrapping_shr(64) as u64]; + let words = [u as u64, (u >> 64) as u64]; llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr()) } } -pub fn C_nil(ccx: &CrateContext) -> ValueRef { - C_struct(ccx, &[], false) -} - pub fn C_bool(ccx: &CrateContext, val: bool) -> ValueRef { C_uint(Type::i1(ccx), val as u64) } @@ -274,8 +170,7 @@ pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef { } pub fn C_usize(ccx: &CrateContext, i: u64) -> ValueRef { - let bit_size = machine::llbitsize_of_real(ccx, ccx.isize_ty()); - + let bit_size = ccx.data_layout().pointer_size.bits(); if bit_size < 64 { // make sure it doesn't overflow assert!(i < (1< Va // you will be kicked off fast isel. See issue #4352 for an example of this. pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef { let len = s.len(); - let cs = consts::ptrcast(C_cstr(cx, s, false), Type::i8p(cx)); - C_named_struct(cx.str_slice_type(), &[cs, C_usize(cx, len as u64)]) + let cs = consts::ptrcast(C_cstr(cx, s, false), + cx.layout_of(cx.tcx().mk_str()).llvm_type(cx).ptr_to()); + C_fat_ptr(cx, cs, C_usize(cx, len as u64)) +} + +pub fn C_fat_ptr(cx: &CrateContext, ptr: ValueRef, meta: ValueRef) -> ValueRef { + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + C_struct(cx, &[ptr, meta], false) } pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef { @@ -334,12 +236,6 @@ pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> } } -pub fn C_named_struct(t: Type, elts: &[ValueRef]) -> ValueRef { - unsafe { - llvm::LLVMConstNamedStruct(t.to_ref(), elts.as_ptr(), elts.len() as c_uint) - } -} - pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { unsafe { return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); @@ -363,13 +259,14 @@ pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { } } -pub fn const_get_elt(v: ValueRef, us: &[c_uint]) - -> ValueRef { +pub fn const_get_elt(v: ValueRef, idx: u64) -> ValueRef { unsafe { + assert_eq!(idx as c_uint as u64, idx); + let us = &[idx as c_uint]; let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); - debug!("const_get_elt(v={:?}, us={:?}, r={:?})", - Value(v), us, Value(r)); + debug!("const_get_elt(v={:?}, idx={}, r={:?})", + Value(v), idx, Value(r)); r } @@ -409,19 +306,6 @@ pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { } } -pub fn is_undef(val: ValueRef) -> bool { - unsafe { - llvm::LLVMIsUndef(val) != False - } -} - -#[allow(dead_code)] // potentially useful -pub fn is_null(val: ValueRef) -> bool { - unsafe { - llvm::LLVMIsNull(val) != False - } -} - pub fn langcall(tcx: TyCtxt, span: Option, msg: &str, @@ -511,15 +395,9 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let tcx = ccx.tcx(); let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs); - let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); - let env_ty = match tcx.closure_kind(def_id) { - ty::ClosureKind::Fn => tcx.mk_imm_ref(tcx.mk_region(env_region), ty), - ty::ClosureKind::FnMut => tcx.mk_mut_ref(tcx.mk_region(env_region), ty), - ty::ClosureKind::FnOnce => ty, - }; - + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(env_ty).chain(sig.inputs().iter().cloned()), + iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), sig.output(), sig.variadic, sig.unsafety, @@ -528,7 +406,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } ty::TyGenerator(def_id, substs, _) => { let tcx = ccx.tcx(); - let sig = tcx.generator_sig(def_id).unwrap().subst(tcx, substs.substs); + let sig = substs.generator_poly_sig(def_id, ccx.tcx()); let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); @@ -552,22 +430,6 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn requests_inline<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - instance: &ty::Instance<'tcx> -) -> bool { - if is_inline_instance(tcx, instance) { - return true - } - if let ty::InstanceDef::DropGlue(..) = instance.def { - // Drop glue wants to be instantiated at every translation - // unit, but without an #[inline] hint. We should make this - // available to normal end-users. - return true - } - attr::requests_inline(&instance.def.attrs(tcx)[..]) -} - pub fn is_inline_instance<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &ty::Instance<'tcx> diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 78ece020d1d6b..cfca3b57cb9d7 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -14,19 +14,19 @@ use llvm::{ValueRef, True}; use rustc::hir::def_id::DefId; use rustc::hir::map as hir_map; use rustc::middle::const_val::ConstEvalErr; -use {debuginfo, machine}; +use debuginfo; use base; use trans_item::{TransItem, TransItemExt}; use common::{self, CrateContext, val_ty}; use declare; use monomorphize::Instance; use type_::Type; -use type_of; +use type_of::LayoutLlvmExt; use rustc::ty; +use rustc::ty::layout::{Align, LayoutOf}; use rustc::hir; -use std::cmp; use std::ffi::{CStr, CString}; use syntax::ast; use syntax::attr; @@ -45,26 +45,26 @@ pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef { fn set_global_alignment(ccx: &CrateContext, gv: ValueRef, - mut align: machine::llalign) { + mut align: Align) { // The target may require greater alignment for globals than the type does. // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, // which can force it to be smaller. Rust doesn't support this yet. if let Some(min) = ccx.sess().target.target.options.min_global_align { match ty::layout::Align::from_bits(min, min) { - Ok(min) => align = cmp::max(align, min.abi() as machine::llalign), + Ok(min) => align = align.max(min), Err(err) => { ccx.sess().err(&format!("invalid minimum global alignment: {}", err)); } } } unsafe { - llvm::LLVMSetAlignment(gv, align); + llvm::LLVMSetAlignment(gv, align.abi() as u32); } } pub fn addr_of_mut(ccx: &CrateContext, cv: ValueRef, - align: machine::llalign, + align: Align, kind: &str) -> ValueRef { unsafe { @@ -82,15 +82,16 @@ pub fn addr_of_mut(ccx: &CrateContext, pub fn addr_of(ccx: &CrateContext, cv: ValueRef, - align: machine::llalign, + align: Align, kind: &str) -> ValueRef { if let Some(&gv) = ccx.const_globals().borrow().get(&cv) { unsafe { // Upgrade the alignment in cases where the same constant is used with different // alignment requirements - if align > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, align); + let llalign = align.abi() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); } } return gv; @@ -112,7 +113,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { let ty = common::instance_ty(ccx.tcx(), &instance); let g = if let Some(id) = ccx.tcx().hir.as_local_node_id(def_id) { - let llty = type_of::type_of(ccx, ty); + let llty = ccx.layout_of(ty).llvm_type(ccx); let (g, attrs) = match ccx.tcx().hir.get(id) { hir_map::NodeItem(&hir::Item { ref attrs, span, node: hir::ItemStatic(..), .. @@ -157,7 +158,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { } }; let llty2 = match ty.sty { - ty::TyRawPtr(ref mt) => type_of::type_of(ccx, mt.ty), + ty::TyRawPtr(ref mt) => ccx.layout_of(mt.ty).llvm_type(ccx), _ => { ccx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); } @@ -196,7 +197,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { for attr in attrs { if attr.check_name("thread_local") { - llvm::set_thread_local(g, true); + llvm::set_thread_local_mode(g, ccx.tls_model()); } } @@ -206,7 +207,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? // FIXME(nagisa): investigate whether it can be changed into define_global - let g = declare::declare_global(ccx, &sym, type_of::type_of(ccx, ty)); + let g = declare::declare_global(ccx, &sym, ccx.layout_of(ty).llvm_type(ccx)); // Thread-local statics in some other crate need to *always* be linked // against in a thread-local fashion, so we need to be sure to apply the // thread-local attribute locally if it was present remotely. If we @@ -215,7 +216,7 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { // symbol and another one doesn't. for attr in ccx.tcx().get_attrs(def_id).iter() { if attr.check_name("thread_local") { - llvm::set_thread_local(g, true); + llvm::set_thread_local_mode(g, ccx.tls_model()); } } if ccx.use_dll_storage_attrs() && !ccx.tcx().is_foreign_item(def_id) { @@ -231,17 +232,13 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { g }; - - // FIXME(#42293) we should actually track this, but fails too many tests - // today. - ccx.tcx().dep_graph.with_ignore(|| { - if ccx.use_dll_storage_attrs() && ccx.tcx().is_dllimport_foreign_item(def_id) { - // For foreign (native) libs we know the exact storage type to use. - unsafe { - llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); - } + if ccx.use_dll_storage_attrs() && ccx.tcx().is_dllimport_foreign_item(def_id) { + // For foreign (native) libs we know the exact storage type to use. + unsafe { + llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); } - }); + } + ccx.instances().borrow_mut().insert(instance, g); ccx.statics().borrow_mut().insert(g, def_id); g @@ -270,7 +267,7 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let instance = Instance::mono(ccx.tcx(), def_id); let ty = common::instance_ty(ccx.tcx(), &instance); - let llty = type_of::type_of(ccx, ty); + let llty = ccx.layout_of(ty).llvm_type(ccx); let g = if val_llty == llty { g } else { @@ -309,9 +306,8 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, debuginfo::create_global_var_metadata(ccx, id, g); - if attr::contains_name(attrs, - "thread_local") { - llvm::set_thread_local(g, true); + if attr::contains_name(attrs, "thread_local") { + llvm::set_thread_local_mode(g, ccx.tls_model()); } base::set_link_section(ccx, g, attrs); diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index b394911c9234d..b2bb605d01b46 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -24,14 +24,17 @@ use monomorphize::Instance; use partitioning::CodegenUnit; use type_::Type; +use type_of::PointeeInfo; + use rustc_data_structures::base_n; use rustc::middle::trans::Stats; use rustc_data_structures::stable_hasher::StableHashingContextProvider; use rustc::session::config::{self, NoDebugInfo}; use rustc::session::Session; -use rustc::ty::layout::{LayoutCx, LayoutError, LayoutTyper, TyLayout}; +use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; +use rustc_trans_utils; use std::ffi::{CStr, CString}; use std::cell::{Cell, RefCell}; @@ -51,6 +54,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, check_overflow: bool, use_dll_storage_attrs: bool, + tls_model: llvm::ThreadLocalMode, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -97,10 +101,10 @@ pub struct LocalCrateContext<'a, 'tcx: 'a> { /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details used_statics: RefCell>, - lltypes: RefCell, Type>>, + lltypes: RefCell, Option), Type>>, + scalar_lltypes: RefCell, Type>>, + pointee_infos: RefCell, Size), Option>>, isize_ty: Type, - opaque_vec_type: Type, - str_slice_type: Type, dbg_cx: Option>, @@ -158,9 +162,25 @@ pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { Some(x) => x.1, _ => { sess.err(&format!("{:?} is not a valid relocation mode", - sess.opts - .cg - .code_model)); + reloc_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } +} + +fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { + let tls_model_arg = match sess.opts.debugging_opts.tls_model { + Some(ref s) => &s[..], + None => &sess.target.target.options.tls_model[..], + }; + + match ::back::write::TLS_MODEL_ARGS.iter().find( + |&&arg| arg.0 == tls_model_arg) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid TLS model", + tls_model_arg)); sess.abort_if_errors(); bug!(); } @@ -282,10 +302,13 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { let check_overflow = tcx.sess.overflow_checks(); + let tls_model = get_tls_model(&tcx.sess); + SharedCrateContext { tcx, check_overflow, use_dll_storage_attrs, + tls_model, } } @@ -301,6 +324,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { common::type_is_freeze(self.tcx, ty) } + pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { + rustc_trans_utils::common::type_has_metadata(self.tcx, ty) + } + pub fn tcx(&self) -> TyCtxt<'b, 'tcx, 'tcx> { self.tcx } @@ -320,19 +347,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { pub fn new(shared: &SharedCrateContext<'a, 'tcx>, - codegen_unit: Arc>) + codegen_unit: Arc>, + llmod_id: &str) -> LocalCrateContext<'a, 'tcx> { unsafe { - // Append ".rs" to LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}.rs", codegen_unit.name()); - let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, &llmod_id[..]); @@ -361,9 +379,9 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { statics_to_rauw: RefCell::new(Vec::new()), used_statics: RefCell::new(Vec::new()), lltypes: RefCell::new(FxHashMap()), + scalar_lltypes: RefCell::new(FxHashMap()), + pointee_infos: RefCell::new(FxHashMap()), isize_ty: Type::from_ref(ptr::null_mut()), - opaque_vec_type: Type::from_ref(ptr::null_mut()), - str_slice_type: Type::from_ref(ptr::null_mut()), dbg_cx, eh_personality: Cell::new(None), eh_unwind_resume: Cell::new(None), @@ -373,25 +391,19 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { placeholder: PhantomData, }; - let (isize_ty, opaque_vec_type, str_slice_ty, mut local_ccx) = { + let (isize_ty, mut local_ccx) = { // Do a little dance to create a dummy CrateContext, so we can // create some things in the LLVM module of this codegen unit let mut local_ccxs = vec![local_ccx]; - let (isize_ty, opaque_vec_type, str_slice_ty) = { + let isize_ty = { let dummy_ccx = LocalCrateContext::dummy_ccx(shared, local_ccxs.as_mut_slice()); - let mut str_slice_ty = Type::named_struct(&dummy_ccx, "str_slice"); - str_slice_ty.set_struct_body(&[Type::i8p(&dummy_ccx), - Type::isize(&dummy_ccx)], - false); - (Type::isize(&dummy_ccx), Type::opaque_vec(&dummy_ccx), str_slice_ty) + Type::isize(&dummy_ccx) }; - (isize_ty, opaque_vec_type, str_slice_ty, local_ccxs.pop().unwrap()) + (isize_ty, local_ccxs.pop().unwrap()) }; local_ccx.isize_ty = isize_ty; - local_ccx.opaque_vec_type = opaque_vec_type; - local_ccx.str_slice_type = str_slice_ty; local_ccx } @@ -496,10 +508,19 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().used_statics } - pub fn lltypes<'a>(&'a self) -> &'a RefCell, Type>> { + pub fn lltypes<'a>(&'a self) -> &'a RefCell, Option), Type>> { &self.local().lltypes } + pub fn scalar_lltypes<'a>(&'a self) -> &'a RefCell, Type>> { + &self.local().scalar_lltypes + } + + pub fn pointee_infos<'a>(&'a self) + -> &'a RefCell, Size), Option>> { + &self.local().pointee_infos + } + pub fn stats<'a>(&'a self) -> &'a RefCell { &self.local().stats } @@ -508,10 +529,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { self.local().isize_ty } - pub fn str_slice_type(&self) -> Type { - self.local().str_slice_type - } - pub fn dbg_cx<'a>(&'a self) -> &'a Option> { &self.local().dbg_cx } @@ -532,6 +549,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { self.shared.use_dll_storage_attrs() } + pub fn tls_model(&self) -> llvm::ThreadLocalMode { + self.shared.tls_model + } + /// Generate a new symbol name with the given prefix. This symbol name must /// only be used for definitions with `internal` or `private` linkage. pub fn generate_local_symbol_name(&self, prefix: &str) -> String { @@ -627,48 +648,44 @@ impl<'a, 'tcx> ty::layout::HasDataLayout for &'a SharedCrateContext<'a, 'tcx> { } } +impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a SharedCrateContext<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} + impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CrateContext<'a, 'tcx> { fn data_layout(&self) -> &ty::layout::TargetDataLayout { &self.shared.tcx.data_layout } } -impl<'a, 'tcx> LayoutTyper<'tcx> for &'a SharedCrateContext<'a, 'tcx> { - type TyLayout = TyLayout<'tcx>; - +impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CrateContext<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx + self.shared.tcx } +} + +impl<'a, 'tcx> LayoutOf> for &'a SharedCrateContext<'a, 'tcx> { + type TyLayout = TyLayout<'tcx>; fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { - let param_env = ty::ParamEnv::empty(traits::Reveal::All); - LayoutCx::new(self.tcx, param_env) + (self.tcx, ty::ParamEnv::empty(traits::Reveal::All)) .layout_of(ty) .unwrap_or_else(|e| match e { LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), _ => bug!("failed to get layout for `{}`: {}", ty, e) }) } - - fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.tcx().normalize_associated_type(&ty) - } } -impl<'a, 'tcx> LayoutTyper<'tcx> for &'a CrateContext<'a, 'tcx> { +impl<'a, 'tcx> LayoutOf> for &'a CrateContext<'a, 'tcx> { type TyLayout = TyLayout<'tcx>; - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.shared.tcx - } fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { self.shared.layout_of(ty) } - - fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.shared.normalize_projections(ty) - } } /// Declare any llvm intrinsics that you might need diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 8a89bfee4ac26..b2ad538a8ab29 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -9,15 +9,15 @@ // except according to those terms. use self::RecursiveTypeDescription::*; -use self::MemberOffset::*; use self::MemberDescriptionFactory::*; use self::EnumDiscriminantInfo::*; -use super::utils::{debug_context, DIB, span_start, bytes_to_bits, size_and_align_of, +use super::utils::{debug_context, DIB, span_start, get_namespace_for_item, create_DIArray, is_node_local_to_unit}; use super::namespace::mangled_name_of_item; use super::type_names::compute_debuginfo_type_name; use super::{CrateDebugContext}; +use abi; use context::SharedCrateContext; use llvm::{self, ValueRef}; @@ -29,19 +29,17 @@ use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::ty::fold::TypeVisitor; use rustc::ty::subst::Substs; use rustc::ty::util::TypeIdHasher; -use rustc::hir; -use rustc_data_structures::ToHex; -use {type_of, machine, monomorphize}; +use rustc::ich::Fingerprint; use common::{self, CrateContext}; -use type_::Type; use rustc::ty::{self, AdtKind, Ty}; -use rustc::ty::layout::{self, LayoutTyper}; +use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; use rustc::session::{Session, config}; use rustc::util::nodemap::FxHashMap; use rustc::util::common::path2cstr; use libc::{c_uint, c_longlong}; use std::ffi::CString; +use std::fmt::Write; use std::ptr; use std::path::Path; use syntax::ast; @@ -146,11 +144,10 @@ impl<'tcx> TypeMap<'tcx> { // The hasher we are using to generate the UniqueTypeId. We want // something that provides more than the 64 bits of the DefaultHasher. - - let mut type_id_hasher = TypeIdHasher::<[u8; 20]>::new(cx.tcx()); + let mut type_id_hasher = TypeIdHasher::::new(cx.tcx()); type_id_hasher.visit_ty(type_); - let unique_type_id = type_id_hasher.finish().to_hex(); + let key = self.unique_id_interner.intern(&unique_type_id); self.type_to_unique_id.insert(type_, UniqueTypeId(key)); @@ -184,7 +181,6 @@ enum RecursiveTypeDescription<'tcx> { unfinished_type: Ty<'tcx>, unique_type_id: UniqueTypeId, metadata_stub: DICompositeType, - llvm_type: Type, member_description_factory: MemberDescriptionFactory<'tcx>, }, FinalMetadata(DICompositeType) @@ -195,7 +191,6 @@ fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( unfinished_type: Ty<'tcx>, unique_type_id: UniqueTypeId, metadata_stub: DICompositeType, - llvm_type: Type, member_description_factory: MemberDescriptionFactory<'tcx>) -> RecursiveTypeDescription<'tcx> { @@ -208,7 +203,6 @@ fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( unfinished_type, unique_type_id, metadata_stub, - llvm_type, member_description_factory, } } @@ -224,9 +218,7 @@ impl<'tcx> RecursiveTypeDescription<'tcx> { unfinished_type, unique_type_id, metadata_stub, - llvm_type, ref member_description_factory, - .. } => { // Make sure that we have a forward declaration of the type in // the TypeMap so that recursive references are possible. This @@ -251,7 +243,6 @@ impl<'tcx> RecursiveTypeDescription<'tcx> { // ... and attach them to the stub to complete it. set_members_of_composite_type(cx, metadata_stub, - llvm_type, &member_descriptions[..]); return MetadataCreationResult::new(metadata_stub, true); } @@ -274,20 +265,21 @@ macro_rules! return_if_metadata_created_in_meantime { fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, unique_type_id: UniqueTypeId, + array_or_slice_type: Ty<'tcx>, element_type: Ty<'tcx>, - len: Option, span: Span) -> MetadataCreationResult { let element_type_metadata = type_metadata(cx, element_type, span); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let element_llvm_type = type_of::type_of(cx, element_type); - let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); + let (size, align) = cx.size_and_align_of(array_or_slice_type); - let (array_size_in_bytes, upper_bound) = match len { - Some(len) => (element_type_size * len, len as c_longlong), - None => (0, -1) + let upper_bound = match array_or_slice_type.sty { + ty::TyArray(_, len) => { + len.val.to_const_int().unwrap().to_u64().unwrap() as c_longlong + } + _ => -1 }; let subrange = unsafe { @@ -298,8 +290,8 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let metadata = unsafe { llvm::LLVMRustDIBuilderCreateArrayType( DIB(cx), - bytes_to_bits(array_size_in_bytes), - bytes_to_bits(element_type_align), + size.bits(), + align.abi_bits() as u32, element_type_metadata, subscripts) }; @@ -308,66 +300,52 @@ fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - vec_type: Ty<'tcx>, + slice_ptr_type: Ty<'tcx>, element_type: Ty<'tcx>, unique_type_id: UniqueTypeId, span: Span) -> MetadataCreationResult { - let data_ptr_type = cx.tcx().mk_ptr(ty::TypeAndMut { - ty: element_type, - mutbl: hir::MutImmutable - }); + let data_ptr_type = cx.tcx().mk_imm_ptr(element_type); - let element_type_metadata = type_metadata(cx, data_ptr_type, span); + let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let slice_llvm_type = type_of::type_of(cx, vec_type); - let slice_type_name = compute_debuginfo_type_name(cx, vec_type, true); + let slice_type_name = compute_debuginfo_type_name(cx, slice_ptr_type, true); + + let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); + let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx().types.usize); - let member_llvm_types = slice_llvm_type.field_types(); - assert!(slice_layout_is_correct(cx, - &member_llvm_types[..], - element_type)); let member_descriptions = [ MemberDescription { name: "data_ptr".to_string(), - llvm_type: member_llvm_types[0], - type_metadata: element_type_metadata, - offset: ComputedMemberOffset, + type_metadata: data_ptr_metadata, + offset: Size::from_bytes(0), + size: pointer_size, + align: pointer_align, flags: DIFlags::FlagZero, }, MemberDescription { name: "length".to_string(), - llvm_type: member_llvm_types[1], type_metadata: type_metadata(cx, cx.tcx().types.usize, span), - offset: ComputedMemberOffset, + offset: pointer_size, + size: usize_size, + align: usize_align, flags: DIFlags::FlagZero, }, ]; - assert!(member_descriptions.len() == member_llvm_types.len()); - let file_metadata = unknown_file_metadata(cx); let metadata = composite_type_metadata(cx, - slice_llvm_type, + slice_ptr_type, &slice_type_name[..], unique_type_id, &member_descriptions, NO_SCOPE_METADATA, file_metadata, span); - return MetadataCreationResult::new(metadata, false); - - fn slice_layout_is_correct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - member_llvm_types: &[Type], - element_type: Ty<'tcx>) - -> bool { - member_llvm_types.len() == 2 && - member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && - member_llvm_types[1] == cx.isize_ty() - } + MetadataCreationResult::new(metadata, false) } fn subroutine_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -436,14 +414,41 @@ fn trait_pointer_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let trait_type_name = compute_debuginfo_type_name(cx, trait_object_type, false); - let trait_llvm_type = type_of::type_of(cx, trait_object_type); let file_metadata = unknown_file_metadata(cx); + let layout = cx.layout_of(cx.tcx().mk_mut_ptr(trait_type)); + + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + + let data_ptr_field = layout.field(cx, 0); + let vtable_field = layout.field(cx, 1); + let member_descriptions = [ + MemberDescription { + name: "pointer".to_string(), + type_metadata: type_metadata(cx, + cx.tcx().mk_mut_ptr(cx.tcx().types.u8), + syntax_pos::DUMMY_SP), + offset: layout.fields.offset(0), + size: data_ptr_field.size, + align: data_ptr_field.align, + flags: DIFlags::FlagArtificial, + }, + MemberDescription { + name: "vtable".to_string(), + type_metadata: type_metadata(cx, vtable_field.ty, syntax_pos::DUMMY_SP), + offset: layout.fields.offset(1), + size: vtable_field.size, + align: vtable_field.align, + flags: DIFlags::FlagArtificial, + }, + ]; + composite_type_metadata(cx, - trait_llvm_type, + trait_object_type, &trait_type_name[..], unique_type_id, - &[], + &member_descriptions, containing_scope, file_metadata, syntax_pos::DUMMY_SP) @@ -529,21 +534,23 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::TyTuple(ref elements, _) if elements.is_empty() => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) } - ty::TyArray(typ, len) => { - let len = len.val.to_const_int().unwrap().to_u64().unwrap(); - fixed_vec_metadata(cx, unique_type_id, typ, Some(len), usage_site_span) - } + ty::TyArray(typ, _) | ty::TySlice(typ) => { - fixed_vec_metadata(cx, unique_type_id, typ, None, usage_site_span) + fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) } ty::TyStr => { - fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, None, usage_site_span) + fixed_vec_metadata(cx, unique_type_id, t, cx.tcx().types.i8, usage_site_span) } ty::TyDynamic(..) => { MetadataCreationResult::new( trait_pointer_metadata(cx, t, None, unique_type_id), false) } + ty::TyForeign(..) => { + MetadataCreationResult::new( + foreign_type_metadata(cx, t, unique_type_id), + false) + } ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) => { match ptr_metadata(ty) { @@ -583,7 +590,7 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } ty::TyGenerator(def_id, substs, _) => { let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx()).map(|t| { - cx.tcx().normalize_associated_type(&t) + cx.tcx().fully_normalize_associated_types_in(&t) }).collect(); prepare_tuple_metadata(cx, t, @@ -738,38 +745,44 @@ fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, _ => bug!("debuginfo::basic_type_metadata - t is invalid type") }; - let llvm_type = type_of::type_of(cx, t); - let (size, align) = size_and_align_of(cx, llvm_type); + let (size, align) = cx.size_and_align_of(t); let name = CString::new(name).unwrap(); let ty_metadata = unsafe { llvm::LLVMRustDIBuilderCreateBasicType( DIB(cx), name.as_ptr(), - bytes_to_bits(size), - bytes_to_bits(align), + size.bits(), + align.abi_bits() as u32, encoding) }; return ty_metadata; } +fn foreign_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>, + unique_type_id: UniqueTypeId) -> DIType { + debug!("foreign_type_metadata: {:?}", t); + + let name = compute_debuginfo_type_name(cx, t, false); + create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA) +} + fn pointer_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, pointer_type: Ty<'tcx>, pointee_type_metadata: DIType) -> DIType { - let pointer_llvm_type = type_of::type_of(cx, pointer_type); - let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); + let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); let name = compute_debuginfo_type_name(cx, pointer_type, false); let name = CString::new(name).unwrap(); - let ptr_metadata = unsafe { + unsafe { llvm::LLVMRustDIBuilderCreatePointerType( DIB(cx), pointee_type_metadata, - bytes_to_bits(pointer_size), - bytes_to_bits(pointer_align), + pointer_size.bits(), + pointer_align.abi_bits() as u32, name.as_ptr()) - }; - return ptr_metadata; + } } pub fn compile_unit_metadata(scc: &SharedCrateContext, @@ -864,21 +877,15 @@ impl MetadataCreationResult { } } -#[derive(Debug)] -enum MemberOffset { - FixedMemberOffset { bytes: usize }, - // For ComputedMemberOffset, the offset is read from the llvm type definition. - ComputedMemberOffset -} - // Description of a type member, which can either be a regular field (as in // structs or tuples) or an enum variant. #[derive(Debug)] struct MemberDescription { name: String, - llvm_type: Type, type_metadata: DIType, - offset: MemberOffset, + offset: Size, + size: Size, + align: Align, flags: DIFlags, } @@ -925,7 +932,6 @@ impl<'tcx> MemberDescriptionFactory<'tcx> { struct StructMemberDescriptionFactory<'tcx> { ty: Ty<'tcx>, variant: &'tcx ty::VariantDef, - substs: &'tcx Substs<'tcx>, span: Span, } @@ -933,35 +939,20 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { let layout = cx.layout_of(self.ty); - - let tmp; - let offsets = match *layout { - layout::Univariant { ref variant, .. } => &variant.offsets, - layout::Vector { element, count } => { - let element_size = element.size(cx).bytes(); - tmp = (0..count). - map(|i| layout::Size::from_bytes(i*element_size)) - .collect::>(); - &tmp - } - _ => bug!("{} is not a struct", self.ty) - }; - self.variant.fields.iter().enumerate().map(|(i, f)| { let name = if self.variant.ctor_kind == CtorKind::Fn { format!("__{}", i) } else { f.name.to_string() }; - let fty = monomorphize::field_ty(cx.tcx(), self.substs, f); - - let offset = FixedMemberOffset { bytes: offsets[i].bytes() as usize}; - + let field = layout.field(cx, i); + let (size, align) = field.size_and_align(); MemberDescription { name, - llvm_type: type_of::in_memory_type_of(cx, fty), - type_metadata: type_metadata(cx, fty, self.span), - offset, + type_metadata: type_metadata(cx, field.ty, self.span), + offset: layout.fields.offset(i), + size, + align, flags: DIFlags::FlagZero, } }).collect() @@ -975,17 +966,16 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, span: Span) -> RecursiveTypeDescription<'tcx> { let struct_name = compute_debuginfo_type_name(cx, struct_type, false); - let struct_llvm_type = type_of::in_memory_type_of(cx, struct_type); - let (struct_def_id, variant, substs) = match struct_type.sty { - ty::TyAdt(def, substs) => (def.did, def.struct_variant(), substs), + let (struct_def_id, variant) = match struct_type.sty { + ty::TyAdt(def, _) => (def.did, def.struct_variant()), _ => bug!("prepare_struct_metadata on a non-ADT") }; let containing_scope = get_namespace_for_item(cx, struct_def_id); let struct_metadata_stub = create_struct_stub(cx, - struct_llvm_type, + struct_type, &struct_name, unique_type_id, containing_scope); @@ -995,11 +985,9 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, struct_type, unique_type_id, struct_metadata_stub, - struct_llvm_type, StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant, - substs, span, }) ) @@ -1020,21 +1008,14 @@ impl<'tcx> TupleMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { let layout = cx.layout_of(self.ty); - let offsets = if let layout::Univariant { ref variant, .. } = *layout { - &variant.offsets - } else { - bug!("{} is not a tuple", self.ty); - }; - - self.component_types - .iter() - .enumerate() - .map(|(i, &component_type)| { + self.component_types.iter().enumerate().map(|(i, &component_type)| { + let (size, align) = cx.size_and_align_of(component_type); MemberDescription { name: format!("__{}", i), - llvm_type: type_of::type_of(cx, component_type), type_metadata: type_metadata(cx, component_type, self.span), - offset: FixedMemberOffset { bytes: offsets[i].bytes() as usize }, + offset: layout.fields.offset(i), + size, + align, flags: DIFlags::FlagZero, } }).collect() @@ -1048,18 +1029,16 @@ fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, span: Span) -> RecursiveTypeDescription<'tcx> { let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); - let tuple_llvm_type = type_of::type_of(cx, tuple_type); create_and_register_recursive_type_forward_declaration( cx, tuple_type, unique_type_id, create_struct_stub(cx, - tuple_llvm_type, + tuple_type, &tuple_name[..], unique_type_id, NO_SCOPE_METADATA), - tuple_llvm_type, TupleMDF(TupleMemberDescriptionFactory { ty: tuple_type, component_types: component_types.to_vec(), @@ -1073,21 +1052,23 @@ fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, //=----------------------------------------------------------------------------- struct UnionMemberDescriptionFactory<'tcx> { + layout: TyLayout<'tcx>, variant: &'tcx ty::VariantDef, - substs: &'tcx Substs<'tcx>, span: Span, } impl<'tcx> UnionMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { - self.variant.fields.iter().map(|field| { - let fty = monomorphize::field_ty(cx.tcx(), self.substs, field); + self.variant.fields.iter().enumerate().map(|(i, f)| { + let field = self.layout.field(cx, i); + let (size, align) = field.size_and_align(); MemberDescription { - name: field.name.to_string(), - llvm_type: type_of::type_of(cx, fty), - type_metadata: type_metadata(cx, fty, self.span), - offset: FixedMemberOffset { bytes: 0 }, + name: f.name.to_string(), + type_metadata: type_metadata(cx, field.ty, self.span), + offset: Size::from_bytes(0), + size, + align, flags: DIFlags::FlagZero, } }).collect() @@ -1100,17 +1081,16 @@ fn prepare_union_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, span: Span) -> RecursiveTypeDescription<'tcx> { let union_name = compute_debuginfo_type_name(cx, union_type, false); - let union_llvm_type = type_of::in_memory_type_of(cx, union_type); - let (union_def_id, variant, substs) = match union_type.sty { - ty::TyAdt(def, substs) => (def.did, def.struct_variant(), substs), + let (union_def_id, variant) = match union_type.sty { + ty::TyAdt(def, _) => (def.did, def.struct_variant()), _ => bug!("prepare_union_metadata on a non-ADT") }; let containing_scope = get_namespace_for_item(cx, union_def_id); let union_metadata_stub = create_union_stub(cx, - union_llvm_type, + union_type, &union_name, unique_type_id, containing_scope); @@ -1120,10 +1100,9 @@ fn prepare_union_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, union_type, unique_type_id, union_metadata_stub, - union_llvm_type, UnionMDF(UnionMemberDescriptionFactory { + layout: cx.layout_of(union_type), variant, - substs, span, }) ) @@ -1140,10 +1119,9 @@ fn prepare_union_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // offset of zero bytes). struct EnumMemberDescriptionFactory<'tcx> { enum_type: Ty<'tcx>, - type_rep: &'tcx layout::Layout, + layout: TyLayout<'tcx>, discriminant_type_metadata: Option, containing_scope: DIScope, - file_metadata: DIFile, span: Span, } @@ -1151,162 +1129,70 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { let adt = &self.enum_type.ty_adt_def().unwrap(); - let substs = match self.enum_type.sty { - ty::TyAdt(def, ref s) if def.adt_kind() == AdtKind::Enum => s, - _ => bug!("{} is not an enum", self.enum_type) - }; - match *self.type_rep { - layout::General { ref variants, .. } => { + match self.layout.variants { + layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], + layout::Variants::Single { index } => { + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + self.layout, + &adt.variants[index], + NoDiscriminant, + self.containing_scope, + self.span); + + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &member_descriptions[..]); + vec![ + MemberDescription { + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: self.layout.size, + align: self.layout.align, + flags: DIFlags::FlagZero + } + ] + } + layout::Variants::Tagged { ref variants, .. } => { let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata .expect("")); - variants - .iter() - .enumerate() - .map(|(i, struct_def)| { - let (variant_type_metadata, - variant_llvm_type, - member_desc_factory) = - describe_enum_variant(cx, - self.enum_type, - struct_def, - &adt.variants[i], - discriminant_info, - self.containing_scope, - self.span); - - let member_descriptions = member_desc_factory - .create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - variant_llvm_type, - &member_descriptions); - MemberDescription { - name: "".to_string(), - llvm_type: variant_llvm_type, - type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: DIFlags::FlagZero - } - }).collect() - }, - layout::Univariant{ ref variant, .. } => { - assert!(adt.variants.len() <= 1); - - if adt.variants.is_empty() { - vec![] - } else { - let (variant_type_metadata, - variant_llvm_type, - member_description_factory) = + (0..variants.len()).map(|i| { + let variant = self.layout.for_variant(cx, i); + let (variant_type_metadata, member_desc_factory) = describe_enum_variant(cx, - self.enum_type, variant, - &adt.variants[0], - NoDiscriminant, + &adt.variants[i], + discriminant_info, self.containing_scope, self.span); - let member_descriptions = - member_description_factory.create_member_descriptions(cx); + let member_descriptions = member_desc_factory + .create_member_descriptions(cx); set_members_of_composite_type(cx, variant_type_metadata, - variant_llvm_type, - &member_descriptions[..]); - vec![ - MemberDescription { - name: "".to_string(), - llvm_type: variant_llvm_type, - type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: DIFlags::FlagZero - } - ] - } - } - layout::RawNullablePointer { nndiscr: non_null_variant_index, .. } => { - // As far as debuginfo is concerned, the pointer this enum - // represents is still wrapped in a struct. This is to make the - // DWARF representation of enums uniform. - - // First create a description of the artificial wrapper struct: - let non_null_variant = &adt.variants[non_null_variant_index as usize]; - let non_null_variant_name = non_null_variant.name.as_str(); - - // The llvm type and metadata of the pointer - let nnty = monomorphize::field_ty(cx.tcx(), &substs, &non_null_variant.fields[0] ); - let non_null_llvm_type = type_of::type_of(cx, nnty); - let non_null_type_metadata = type_metadata(cx, nnty, self.span); - - // The type of the artificial struct wrapping the pointer - let artificial_struct_llvm_type = Type::struct_(cx, - &[non_null_llvm_type], - false); - - // For the metadata of the wrapper struct, we need to create a - // MemberDescription of the struct's single field. - let sole_struct_member_description = MemberDescription { - name: match non_null_variant.ctor_kind { - CtorKind::Fn => "__0".to_string(), - CtorKind::Fictive => { - non_null_variant.fields[0].name.to_string() - } - CtorKind::Const => bug!() - }, - llvm_type: non_null_llvm_type, - type_metadata: non_null_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, - flags: DIFlags::FlagZero - }; - - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - self.enum_type, - &non_null_variant_name); - - // Now we can create the metadata of the artificial struct - let artificial_struct_metadata = - composite_type_metadata(cx, - artificial_struct_llvm_type, - &non_null_variant_name, - unique_type_id, - &[sole_struct_member_description], - self.containing_scope, - self.file_metadata, - syntax_pos::DUMMY_SP); - - // Encode the information about the null variant in the union - // member's name. - let null_variant_index = (1 - non_null_variant_index) as usize; - let null_variant_name = adt.variants[null_variant_index].name; - let union_member_name = format!("RUST$ENCODED$ENUM${}${}", - 0, - null_variant_name); - - // Finally create the (singleton) list of descriptions of union - // members. - vec![ + &member_descriptions); MemberDescription { - name: union_member_name, - llvm_type: artificial_struct_llvm_type, - type_metadata: artificial_struct_metadata, - offset: FixedMemberOffset { bytes: 0 }, + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, flags: DIFlags::FlagZero } - ] - }, - layout::StructWrappedNullablePointer { nonnull: ref struct_def, - nndiscr, - ref discrfield_source, ..} => { + }).collect() + } + layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { + let variant = self.layout.for_variant(cx, dataful_variant); // Create a description of the non-null variant - let (variant_type_metadata, variant_llvm_type, member_description_factory) = + let (variant_type_metadata, member_description_factory) = describe_enum_variant(cx, - self.enum_type, - struct_def, - &adt.variants[nndiscr as usize], + variant, + &adt.variants[dataful_variant], OptimizedDiscriminant, self.containing_scope, self.span); @@ -1316,34 +1202,51 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { set_members_of_composite_type(cx, variant_type_metadata, - variant_llvm_type, &variant_member_descriptions[..]); // Encode the information about the null variant in the union // member's name. - let null_variant_index = (1 - nndiscr) as usize; - let null_variant_name = adt.variants[null_variant_index].name; - let discrfield_source = discrfield_source.iter() - .skip(1) - .map(|x| x.to_string()) - .collect::>().join("$"); - let union_member_name = format!("RUST$ENCODED$ENUM${}${}", - discrfield_source, - null_variant_name); + let mut name = String::from("RUST$ENCODED$ENUM$"); + // HACK(eddyb) the debuggers should just handle offset+size + // of discriminant instead of us having to recover its path. + // Right now it's not even going to work for `niche_start > 0`, + // and for multiple niche variants it only supports the first. + fn compute_field_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + name: &mut String, + layout: TyLayout<'tcx>, + offset: Size, + size: Size) { + for i in 0..layout.fields.count() { + let field_offset = layout.fields.offset(i); + if field_offset > offset { + continue; + } + let inner_offset = offset - field_offset; + let field = layout.field(ccx, i); + if inner_offset + size <= field.size { + write!(name, "{}$", i).unwrap(); + compute_field_path(ccx, name, field, inner_offset, size); + } + } + } + compute_field_path(cx, &mut name, + self.layout, + self.layout.fields.offset(0), + self.layout.field(cx, 0).size); + name.push_str(&adt.variants[niche_variants.start].name.as_str()); // Create the (singleton) list of descriptions of union members. vec![ MemberDescription { - name: union_member_name, - llvm_type: variant_llvm_type, + name, type_metadata: variant_type_metadata, - offset: FixedMemberOffset { bytes: 0 }, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, flags: DIFlags::FlagZero } ] - }, - layout::CEnum { .. } => span_bug!(self.span, "This should be unreachable."), - ref l @ _ => bug!("Not an enum layout: {:#?}", l) + } } } } @@ -1351,7 +1254,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { // Creates MemberDescriptions for the fields of a single enum variant. struct VariantMemberDescriptionFactory<'tcx> { // Cloned from the layout::Struct describing the variant. - offsets: &'tcx [layout::Size], + offsets: Vec, args: Vec<(String, Ty<'tcx>)>, discriminant_type_metadata: Option, span: Span, @@ -1361,14 +1264,16 @@ impl<'tcx> VariantMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { self.args.iter().enumerate().map(|(i, &(ref name, ty))| { + let (size, align) = cx.size_and_align_of(ty); MemberDescription { name: name.to_string(), - llvm_type: type_of::type_of(cx, ty), type_metadata: match self.discriminant_type_metadata { Some(metadata) if i == 0 => metadata, _ => type_metadata(cx, ty, self.span) }, - offset: FixedMemberOffset { bytes: self.offsets[i].bytes() as usize }, + offset: self.offsets[i], + size, + align, flags: DIFlags::FlagZero } }).collect() @@ -1387,92 +1292,52 @@ enum EnumDiscriminantInfo { // descriptions of the fields of the variant. This is a rudimentary version of a // full RecursiveTypeDescription. fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - enum_type: Ty<'tcx>, - struct_def: &'tcx layout::Struct, + layout: layout::TyLayout<'tcx>, variant: &'tcx ty::VariantDef, discriminant_info: EnumDiscriminantInfo, containing_scope: DIScope, span: Span) - -> (DICompositeType, Type, MemberDescriptionFactory<'tcx>) { - let substs = match enum_type.sty { - ty::TyAdt(def, s) if def.adt_kind() == AdtKind::Enum => s, - ref t @ _ => bug!("{:#?} is not an enum", t) - }; - - let maybe_discr_and_signed: Option<(layout::Integer, bool)> = match *cx.layout_of(enum_type) { - layout::CEnum {discr, ..} => Some((discr, true)), - layout::General{discr, ..} => Some((discr, false)), - layout::Univariant { .. } - | layout::RawNullablePointer { .. } - | layout::StructWrappedNullablePointer { .. } => None, - ref l @ _ => bug!("This should be unreachable. Type is {:#?} layout is {:#?}", enum_type, l) - }; - - let mut field_tys = variant.fields.iter().map(|f| { - monomorphize::field_ty(cx.tcx(), &substs, f) - }).collect::>(); - - if let Some((discr, signed)) = maybe_discr_and_signed { - field_tys.insert(0, discr.to_ty(&cx.tcx(), signed)); - } - - - let variant_llvm_type = - Type::struct_(cx, &field_tys - .iter() - .map(|t| type_of::type_of(cx, t)) - .collect::>() - , - struct_def.packed); - // Could do some consistency checks here: size, align, field count, discr type - + -> (DICompositeType, MemberDescriptionFactory<'tcx>) { let variant_name = variant.name.as_str(); let unique_type_id = debug_context(cx).type_map .borrow_mut() .get_unique_type_id_of_enum_variant( cx, - enum_type, + layout.ty, &variant_name); let metadata_stub = create_struct_stub(cx, - variant_llvm_type, + layout.ty, &variant_name, unique_type_id, containing_scope); - // Get the argument names from the enum variant info - let mut arg_names: Vec<_> = match variant.ctor_kind { - CtorKind::Const => vec![], - CtorKind::Fn => { - variant.fields - .iter() - .enumerate() - .map(|(i, _)| format!("__{}", i)) - .collect() - } - CtorKind::Fictive => { - variant.fields - .iter() - .map(|f| f.name.to_string()) - .collect() - } - }; - // If this is not a univariant enum, there is also the discriminant field. - match discriminant_info { - RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), - _ => { /* do nothing */ } + let (discr_offset, discr_arg) = match discriminant_info { + RegularDiscriminant(_) => { + let enum_layout = cx.layout_of(layout.ty); + (Some(enum_layout.fields.offset(0)), + Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty))) + } + _ => (None, None), }; + let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { + layout.fields.offset(i) + })).collect(); // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args: Vec<(String, Ty)> = arg_names.iter() - .zip(field_tys.iter()) - .map(|(s, &t)| (s.to_string(), t)) - .collect(); + let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { + let name = if variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + variant.fields[i].name.to_string() + }; + (name, layout.field(cx, i).ty) + })).collect(); let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { - offsets: &struct_def.offsets[..], + offsets, args, discriminant_type_metadata: match discriminant_info { RegularDiscriminant(discriminant_type_metadata) => { @@ -1483,7 +1348,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, span, }); - (metadata_stub, variant_llvm_type, member_description_factory) + (metadata_stub, member_description_factory) } fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -1519,21 +1384,18 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }) .collect(); - let discriminant_type_metadata = |inttype: layout::Integer, signed: bool| { - let disr_type_key = (enum_def_id, inttype); + let discriminant_type_metadata = |discr: layout::Primitive| { + let disr_type_key = (enum_def_id, discr); let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types .borrow() .get(&disr_type_key).cloned(); match cached_discriminant_type_metadata { Some(discriminant_type_metadata) => discriminant_type_metadata, None => { - let discriminant_llvm_type = Type::from_integer(cx, inttype); let (discriminant_size, discriminant_align) = - size_and_align_of(cx, discriminant_llvm_type); + (discr.size(cx), discr.align(cx)); let discriminant_base_type_metadata = - type_metadata(cx, - inttype.to_ty(&cx.tcx(), signed), - syntax_pos::DUMMY_SP); + type_metadata(cx, discr.to_ty(cx.tcx()), syntax_pos::DUMMY_SP); let discriminant_name = get_enum_discriminant_name(cx, enum_def_id); let name = CString::new(discriminant_name.as_bytes()).unwrap(); @@ -1544,8 +1406,8 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, name.as_ptr(), file_metadata, UNKNOWN_LINE_NUMBER, - bytes_to_bits(discriminant_size), - bytes_to_bits(discriminant_align), + discriminant_size.bits(), + discriminant_align.abi_bits() as u32, create_DIArray(DIB(cx), &enumerators_metadata), discriminant_base_type_metadata) }; @@ -1559,21 +1421,22 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } }; - let type_rep = cx.layout_of(enum_type); + let layout = cx.layout_of(enum_type); - let discriminant_type_metadata = match *type_rep { - layout::CEnum { discr, signed, .. } => { - return FinalMetadata(discriminant_type_metadata(discr, signed)) - }, - layout::RawNullablePointer { .. } | - layout::StructWrappedNullablePointer { .. } | - layout::Univariant { .. } => None, - layout::General { discr, .. } => Some(discriminant_type_metadata(discr, false)), - ref l @ _ => bug!("Not an enum layout: {:#?}", l) + let discriminant_type_metadata = match layout.variants { + layout::Variants::Single { .. } | + layout::Variants::NicheFilling { .. } => None, + layout::Variants::Tagged { ref discr, .. } => { + Some(discriminant_type_metadata(discr.value)) + } }; - let enum_llvm_type = type_of::type_of(cx, enum_type); - let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); + match (&layout.abi, discriminant_type_metadata) { + (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), + _ => {} + } + + let (enum_type_size, enum_type_align) = layout.size_and_align(); let enum_name = CString::new(enum_name).unwrap(); let unique_type_id_str = CString::new( @@ -1586,8 +1449,8 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, enum_name.as_ptr(), file_metadata, UNKNOWN_LINE_NUMBER, - bytes_to_bits(enum_type_size), - bytes_to_bits(enum_type_align), + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, DIFlags::FlagZero, ptr::null_mut(), 0, // RuntimeLang @@ -1599,13 +1462,11 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, enum_type, unique_type_id, enum_metadata, - enum_llvm_type, EnumMDF(EnumMemberDescriptionFactory { enum_type, - type_rep: type_rep.layout, + layout, discriminant_type_metadata, containing_scope, - file_metadata, span, }), ); @@ -1621,28 +1482,27 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, /// results in a LLVM struct. /// /// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata(cx: &CrateContext, - composite_llvm_type: Type, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription], - containing_scope: DIScope, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: DIFile, - _definition_span: Span) - -> DICompositeType { +fn composite_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + composite_type: Ty<'tcx>, + composite_type_name: &str, + composite_type_unique_id: UniqueTypeId, + member_descriptions: &[MemberDescription], + containing_scope: DIScope, + + // Ignore source location information as long as it + // can't be reconstructed for non-local crates. + _file_metadata: DIFile, + _definition_span: Span) + -> DICompositeType { // Create the (empty) struct metadata node ... let composite_type_metadata = create_struct_stub(cx, - composite_llvm_type, + composite_type, composite_type_name, composite_type_unique_id, containing_scope); // ... and immediately create and add the member descriptions. set_members_of_composite_type(cx, composite_type_metadata, - composite_llvm_type, member_descriptions); return composite_type_metadata; @@ -1650,7 +1510,6 @@ fn composite_type_metadata(cx: &CrateContext, fn set_members_of_composite_type(cx: &CrateContext, composite_type_metadata: DICompositeType, - composite_llvm_type: Type, member_descriptions: &[MemberDescription]) { // In some rare cases LLVM metadata uniquing would lead to an existing type // description being used instead of a new one created in @@ -1671,14 +1530,7 @@ fn set_members_of_composite_type(cx: &CrateContext, let member_metadata: Vec = member_descriptions .iter() - .enumerate() - .map(|(i, member_description)| { - let (member_size, member_align) = size_and_align_of(cx, member_description.llvm_type); - let member_offset = match member_description.offset { - FixedMemberOffset { bytes } => bytes as u64, - ComputedMemberOffset => machine::llelement_offset(cx, composite_llvm_type, i) - }; - + .map(|member_description| { let member_name = member_description.name.as_bytes(); let member_name = CString::new(member_name).unwrap(); unsafe { @@ -1688,9 +1540,9 @@ fn set_members_of_composite_type(cx: &CrateContext, member_name.as_ptr(), unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, - bytes_to_bits(member_size), - bytes_to_bits(member_align), - bytes_to_bits(member_offset), + member_description.size.bits(), + member_description.align.abi_bits() as u32, + member_description.offset.bits(), member_description.flags, member_description.type_metadata) } @@ -1707,13 +1559,13 @@ fn set_members_of_composite_type(cx: &CrateContext, // A convenience wrapper around LLVMRustDIBuilderCreateStructType(). Does not do // any caching, does not add any fields to the struct. This can be done later // with set_members_of_composite_type(). -fn create_struct_stub(cx: &CrateContext, - struct_llvm_type: Type, - struct_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type); +fn create_struct_stub<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + struct_type: Ty<'tcx>, + struct_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (struct_size, struct_align) = cx.size_and_align_of(struct_type); let name = CString::new(struct_type_name).unwrap(); let unique_type_id = CString::new( @@ -1731,8 +1583,8 @@ fn create_struct_stub(cx: &CrateContext, name.as_ptr(), unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, - bytes_to_bits(struct_size), - bytes_to_bits(struct_align), + struct_size.bits(), + struct_align.abi_bits() as u32, DIFlags::FlagZero, ptr::null_mut(), empty_array, @@ -1744,13 +1596,13 @@ fn create_struct_stub(cx: &CrateContext, return metadata_stub; } -fn create_union_stub(cx: &CrateContext, - union_llvm_type: Type, - union_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (union_size, union_align) = size_and_align_of(cx, union_llvm_type); +fn create_union_stub<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + union_type: Ty<'tcx>, + union_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (union_size, union_align) = cx.size_and_align_of(union_type); let name = CString::new(union_type_name).unwrap(); let unique_type_id = CString::new( @@ -1768,8 +1620,8 @@ fn create_union_stub(cx: &CrateContext, name.as_ptr(), unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, - bytes_to_bits(union_size), - bytes_to_bits(union_align), + union_size.bits(), + union_align.abi_bits() as u32, DIFlags::FlagZero, empty_array, 0, // RuntimeLang @@ -1824,7 +1676,7 @@ pub fn create_global_var_metadata(cx: &CrateContext, is_local_to_unit, global, ptr::null_mut(), - global_align, + global_align.abi() as u32, ); } } @@ -1843,3 +1695,63 @@ pub fn extend_scope_to_file(ccx: &CrateContext, file_metadata) } } + +/// Creates debug information for the given vtable, which is for the +/// given type. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_vtable_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + vtable: ValueRef) { + if cx.dbg_cx().is_none() { + return; + } + + let type_metadata = type_metadata(cx, ty, syntax_pos::DUMMY_SP); + + unsafe { + // LLVMRustDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + let name = CString::new("vtable").unwrap(); + + // Create a new one each time. We don't want metadata caching + // here, because each vtable will refer to a unique containing + // type. + let vtable_type = llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + Size::from_bytes(0).bits(), + cx.tcx().data_layout.pointer_align.abi_bits() as u32, + DIFlags::FlagArtificial, + ptr::null_mut(), + empty_array, + 0, + type_metadata, + name.as_ptr() + ); + + llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + // LLVM 3.9 + // doesn't accept + // null here, so + // pass the name + // as the linkage + // name. + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + vtable_type, + true, + vtable, + ptr::null_mut(), + 0); + } +} diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 7e2ac95cd845c..c0df25202d8a9 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -43,7 +43,7 @@ use std::ptr; use syntax_pos::{self, Span, Pos}; use syntax::ast; use syntax::symbol::Symbol; -use rustc::ty::layout::{self, LayoutTyper}; +use rustc::ty::layout::{self, LayoutOf}; pub mod gdb; mod utils; @@ -56,6 +56,7 @@ mod source_loc; pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; pub use self::source_loc::start_emitting_source_locations; pub use self::metadata::create_global_var_metadata; +pub use self::metadata::create_vtable_metadata; pub use self::metadata::extend_scope_to_file; pub use self::source_loc::set_source_location; @@ -70,7 +71,7 @@ pub struct CrateDebugContext<'tcx> { llmod: ModuleRef, builder: DIBuilderRef, created_files: RefCell>, - created_enum_disr_types: RefCell>, + created_enum_disr_types: RefCell>, type_map: RefCell>, namespace_map: RefCell>, @@ -334,8 +335,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, signature.extend(inputs.iter().map(|&t| { let t = match t.sty { ty::TyArray(ct, _) - if (ct == cx.tcx().types.u8) || - (cx.layout_of(ct).size(cx).bytes() == 0) => { + if (ct == cx.tcx().types.u8) || cx.layout_of(ct).is_zst() => { cx.tcx().mk_imm_ptr(ct) } _ => t @@ -376,7 +376,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, name_to_append_suffix_to.push_str(","); } - let actual_type = cx.tcx().normalize_associated_type(&actual_type); + let actual_type = cx.tcx().fully_normalize_associated_types_in(&actual_type); // Add actual type name to <...> clause of function name let actual_type_name = compute_debuginfo_type_name(cx, actual_type, @@ -389,7 +389,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { let names = get_type_parameter_names(cx, generics); substs.types().zip(names).map(|(ty, name)| { - let actual_type = cx.tcx().normalize_associated_type(&ty); + let actual_type = cx.tcx().fully_normalize_associated_types_in(&ty); let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); let name = CString::new(name.as_str().as_bytes()).unwrap(); unsafe { @@ -498,7 +498,7 @@ pub fn declare_local<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, cx.sess().opts.optimize != config::OptLevel::No, DIFlags::FlagZero, argument_index, - align, + align.abi() as u32, ) }; source_loc::set_debug_location(bcx, diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index 7bf9d39ea2f25..85467f5bfbd22 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -48,6 +48,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()), ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), + ty::TyForeign(def_id) => push_item_name(cx, def_id, qualified, output), ty::TyAdt(def, substs) => { push_item_name(cx, def.did, qualified, output); push_type_params(cx, substs, output); diff --git a/src/librustc_trans/debuginfo/utils.rs b/src/librustc_trans/debuginfo/utils.rs index ad4fdfca7261f..95427d9b3cd4e 100644 --- a/src/librustc_trans/debuginfo/utils.rs +++ b/src/librustc_trans/debuginfo/utils.rs @@ -18,15 +18,11 @@ use rustc::ty::DefIdTree; use llvm; use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; -use machine; use common::{CrateContext}; -use type_::Type; use syntax_pos::{self, Span}; use syntax::ast; -use std::ops; - pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool { // The is_local_to_unit flag indicates whether a function is local to the @@ -53,15 +49,6 @@ pub fn span_start(cx: &CrateContext, span: Span) -> syntax_pos::Loc { cx.sess().codemap().lookup_char_pos(span.lo()) } -pub fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u32) { - (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type)) -} - -pub fn bytes_to_bits(bytes: T) -> T - where T: ops::Mul + From { - bytes * 8u8.into() -} - #[inline] pub fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>) -> &'a CrateDebugContext<'tcx> { diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs index 3c8ff45499780..f894bdf16e4de 100644 --- a/src/librustc_trans/declare.rs +++ b/src/librustc_trans/declare.rs @@ -24,6 +24,7 @@ use llvm::{self, ValueRef}; use llvm::AttributePlace::Function; use rustc::ty::Ty; use rustc::session::config::Sanitizer; +use rustc_back::PanicStrategy; use abi::{Abi, FnType}; use attributes; use context::CrateContext; @@ -98,6 +99,10 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: _ => {}, } + if ccx.tcx().sess.panic_strategy() != PanicStrategy::Unwind { + attributes::unwind(llfn, false); + } + llfn } diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs index 8485867689129..8f5d836f56f3f 100644 --- a/src/librustc_trans/diagnostics.rs +++ b/src/librustc_trans/diagnostics.rs @@ -37,13 +37,13 @@ The generic type has to be a SIMD type. Example: #[repr(simd)] #[derive(Copy, Clone)] -struct i32x1(i32); +struct i32x2(i32, i32); extern "platform-intrinsic" { fn simd_add(a: T, b: T) -> T; } -unsafe { simd_add(i32x1(0), i32x1(1)); } // ok! +unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! ``` "##, diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 453b98a1d74f7..6c7d7700adeb2 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -19,8 +19,7 @@ use common::*; use llvm::{ValueRef}; use llvm; use meth; -use monomorphize; -use rustc::ty::layout::LayoutTyper; +use rustc::ty::layout::LayoutOf; use rustc::ty::{self, Ty}; use value::Value; @@ -29,17 +28,28 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf debug!("calculate size of DST: {}; with lost info: {:?}", t, Value(info)); if bcx.ccx.shared().type_is_sized(t) { - let size = bcx.ccx.size_of(t); - let align = bcx.ccx.align_of(t); - debug!("size_and_align_of_dst t={} info={:?} size: {} align: {}", + let (size, align) = bcx.ccx.size_and_align_of(t); + debug!("size_and_align_of_dst t={} info={:?} size: {:?} align: {:?}", t, Value(info), size, align); - let size = C_usize(bcx.ccx, size); - let align = C_usize(bcx.ccx, align as u64); + let size = C_usize(bcx.ccx, size.bytes()); + let align = C_usize(bcx.ccx, align.abi()); return (size, align); } assert!(!info.is_null()); match t.sty { - ty::TyAdt(..) | ty::TyTuple(..) => { + ty::TyDynamic(..) => { + // load size/align from vtable + (meth::SIZE.get_usize(bcx, info), meth::ALIGN.get_usize(bcx, info)) + } + ty::TySlice(_) | ty::TyStr => { + let unit = t.sequence_element_type(bcx.tcx()); + // The info in this case is the length of the str, so the size is that + // times the unit size. + let (size, align) = bcx.ccx.size_and_align_of(unit); + (bcx.mul(info, C_usize(bcx.ccx, size.bytes())), + C_usize(bcx.ccx, align.abi())) + } + _ => { let ccx = bcx.ccx; // First get the size of all statically known fields. // Don't use size_of because it also rounds up to alignment, which we @@ -48,15 +58,9 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf let layout = ccx.layout_of(t); debug!("DST {} layout: {:?}", t, layout); - let (sized_size, sized_align) = match *layout { - ty::layout::Layout::Univariant { ref variant, .. } => { - (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align.abi()) - } - _ => { - bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", - t, layout); - } - }; + let i = layout.fields.count() - 1; + let sized_size = layout.fields.offset(i).bytes(); + let sized_align = layout.align.abi(); debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align); let sized_size = C_usize(ccx, sized_size); @@ -64,14 +68,7 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf // Recurse to get the size of the dynamically sized field (must be // the last field). - let field_ty = match t.sty { - ty::TyAdt(def, substs) => { - let last_field = def.struct_variant().fields.last().unwrap(); - monomorphize::field_ty(bcx.tcx(), substs, last_field) - }, - ty::TyTuple(tys, _) => tys.last().unwrap(), - _ => unreachable!(), - }; + let field_ty = layout.field(ccx, i).ty; let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); // FIXME (#26403, #27023): We should be adding padding @@ -114,17 +111,5 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf (size, align) } - ty::TyDynamic(..) => { - // load size/align from vtable - (meth::SIZE.get_usize(bcx, info), meth::ALIGN.get_usize(bcx, info)) - } - ty::TySlice(_) | ty::TyStr => { - let unit = t.sequence_element_type(bcx.tcx()); - // The info in this case is the length of the str, so the size is that - // times the unit size. - (bcx.mul(info, C_usize(bcx.ccx, bcx.ccx.size_of(unit))), - C_usize(bcx.ccx, bcx.ccx.align_of(unit) as u64)) - } - _ => bug!("Unexpected unsized type, found {}", t) } } diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index f78d80a197ca9..a35afb806111c 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -11,20 +11,19 @@ #![allow(non_upper_case_globals)] use intrinsics::{self, Intrinsic}; -use libc; use llvm; use llvm::{ValueRef}; -use abi::{Abi, FnType}; -use adt; -use mir::lvalue::{LvalueRef, Alignment}; +use abi::{Abi, FnType, PassMode}; +use mir::place::{PlaceRef, Alignment}; +use mir::operand::{OperandRef, OperandValue}; use base::*; use common::*; use declare; use glue; -use type_of; -use machine; use type_::Type; +use type_of::LayoutLlvmExt; use rustc::ty::{self, Ty}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; use rustc::hir; use syntax::ast; use syntax::symbol::Symbol; @@ -88,8 +87,8 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option { /// add them to librustc_trans/trans/context.rs pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, callee_ty: Ty<'tcx>, - fn_ty: &FnType, - llargs: &[ValueRef], + fn_ty: &FnType<'tcx>, + args: &[OperandRef<'tcx>], llresult: ValueRef, span: Span) { let ccx = bcx.ccx; @@ -106,27 +105,34 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let ret_ty = sig.output(); let name = &*tcx.item_name(def_id); - let llret_ty = type_of::type_of(ccx, ret_ty); + let llret_ty = ccx.layout_of(ret_ty).llvm_type(ccx); + let result = PlaceRef::new_sized(llresult, fn_ty.ret.layout, Alignment::AbiAligned); let simple = get_simple_intrinsic(ccx, name); let llval = match name { _ if simple.is_some() => { - bcx.call(simple.unwrap(), &llargs, None) + bcx.call(simple.unwrap(), + &args.iter().map(|arg| arg.immediate()).collect::>(), + None) } "unreachable" => { return; }, "likely" => { let expect = ccx.get_intrinsic(&("llvm.expect.i1")); - bcx.call(expect, &[llargs[0], C_bool(ccx, true)], None) + bcx.call(expect, &[args[0].immediate(), C_bool(ccx, true)], None) } "unlikely" => { let expect = ccx.get_intrinsic(&("llvm.expect.i1")); - bcx.call(expect, &[llargs[0], C_bool(ccx, false)], None) + bcx.call(expect, &[args[0].immediate(), C_bool(ccx, false)], None) } "try" => { - try_intrinsic(bcx, ccx, llargs[0], llargs[1], llargs[2], llresult); - C_nil(ccx) + try_intrinsic(bcx, ccx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + llresult); + return; } "breakpoint" => { let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); @@ -134,38 +140,35 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } "size_of" => { let tp_ty = substs.type_at(0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_usize(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) + C_usize(ccx, ccx.size_of(tp_ty).bytes()) } "size_of_val" => { let tp_ty = substs.type_at(0); - if !bcx.ccx.shared().type_is_sized(tp_ty) { + if let OperandValue::Pair(_, meta) = args[0].val { let (llsize, _) = - glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]); + glue::size_and_align_of_dst(bcx, tp_ty, meta); llsize } else { - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_usize(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) + C_usize(ccx, ccx.size_of(tp_ty).bytes()) } } "min_align_of" => { let tp_ty = substs.type_at(0); - C_usize(ccx, ccx.align_of(tp_ty) as u64) + C_usize(ccx, ccx.align_of(tp_ty).abi()) } "min_align_of_val" => { let tp_ty = substs.type_at(0); - if !bcx.ccx.shared().type_is_sized(tp_ty) { + if let OperandValue::Pair(_, meta) = args[0].val { let (_, llalign) = - glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]); + glue::size_and_align_of_dst(bcx, tp_ty, meta); llalign } else { - C_usize(ccx, ccx.align_of(tp_ty) as u64) + C_usize(ccx, ccx.align_of(tp_ty).abi()) } } "pref_align_of" => { let tp_ty = substs.type_at(0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_usize(ccx, machine::llalign_of_pref(ccx, lltp_ty) as u64) + C_usize(ccx, ccx.align_of(tp_ty).pref()) } "type_name" => { let tp_ty = substs.type_at(0); @@ -177,18 +180,18 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } "init" => { let ty = substs.type_at(0); - if !type_is_zero_size(ccx, ty) { + if !ccx.layout_of(ty).is_zst() { // Just zero out the stack slot. // If we store a zero constant, LLVM will drown in vreg allocation for large data // structures, and the generated code will be awful. (A telltale sign of this is // large quantities of `mov [byte ptr foo],0` in the generated code.) memset_intrinsic(bcx, false, ty, llresult, C_u8(ccx, 0), C_usize(ccx, 1)); } - C_nil(ccx) + return; } // Effectively no-ops "uninit" => { - C_nil(ccx) + return; } "needs_drop" => { let tp_ty = substs.type_at(0); @@ -196,69 +199,75 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, C_bool(ccx, bcx.ccx.shared().type_needs_drop(tp_ty)) } "offset" => { - let ptr = llargs[0]; - let offset = llargs[1]; + let ptr = args[0].immediate(); + let offset = args[1].immediate(); bcx.inbounds_gep(ptr, &[offset]) } "arith_offset" => { - let ptr = llargs[0]; - let offset = llargs[1]; + let ptr = args[0].immediate(); + let offset = args[1].immediate(); bcx.gep(ptr, &[offset]) } "copy_nonoverlapping" => { - copy_intrinsic(bcx, false, false, substs.type_at(0), llargs[1], llargs[0], llargs[2]) + copy_intrinsic(bcx, false, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) } "copy" => { - copy_intrinsic(bcx, true, false, substs.type_at(0), llargs[1], llargs[0], llargs[2]) + copy_intrinsic(bcx, true, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) } "write_bytes" => { - memset_intrinsic(bcx, false, substs.type_at(0), llargs[0], llargs[1], llargs[2]) + memset_intrinsic(bcx, false, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) } "volatile_copy_nonoverlapping_memory" => { - copy_intrinsic(bcx, false, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) + copy_intrinsic(bcx, false, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) } "volatile_copy_memory" => { - copy_intrinsic(bcx, true, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) + copy_intrinsic(bcx, true, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) } "volatile_set_memory" => { - memset_intrinsic(bcx, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) + memset_intrinsic(bcx, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) } "volatile_load" => { let tp_ty = substs.type_at(0); - let mut ptr = llargs[0]; - if let Some(ty) = fn_ty.ret.cast { - ptr = bcx.pointercast(ptr, ty.ptr_to()); + let mut ptr = args[0].immediate(); + if let PassMode::Cast(ty) = fn_ty.ret.mode { + ptr = bcx.pointercast(ptr, ty.llvm_type(ccx).ptr_to()); } let load = bcx.volatile_load(ptr); unsafe { - llvm::LLVMSetAlignment(load, ccx.align_of(tp_ty)); + llvm::LLVMSetAlignment(load, ccx.align_of(tp_ty).abi() as u32); } - to_immediate(bcx, load, tp_ty) + to_immediate(bcx, load, ccx.layout_of(tp_ty)) }, "volatile_store" => { let tp_ty = substs.type_at(0); - if type_is_fat_ptr(bcx.ccx, tp_ty) { - bcx.volatile_store(llargs[1], get_dataptr(bcx, llargs[0])); - bcx.volatile_store(llargs[2], get_meta(bcx, llargs[0])); + let dst = args[0].deref(bcx.ccx); + if let OperandValue::Pair(a, b) = args[1].val { + bcx.volatile_store(a, dst.project_field(bcx, 0).llval); + bcx.volatile_store(b, dst.project_field(bcx, 1).llval); } else { - let val = if fn_ty.args[1].is_indirect() { - bcx.load(llargs[1], None) + let val = if let OperandValue::Ref(ptr, align) = args[1].val { + bcx.load(ptr, align.non_abi()) } else { - if !type_is_zero_size(ccx, tp_ty) { - from_immediate(bcx, llargs[1]) - } else { - C_nil(ccx) + if dst.layout.is_zst() { + return; } + from_immediate(bcx, args[1].immediate()) }; - let ptr = bcx.pointercast(llargs[0], val_ty(val).ptr_to()); + let ptr = bcx.pointercast(dst.llval, val_ty(val).ptr_to()); let store = bcx.volatile_store(val, ptr); unsafe { - llvm::LLVMSetAlignment(store, ccx.align_of(tp_ty)); + llvm::LLVMSetAlignment(store, ccx.align_of(tp_ty).abi() as u32); } } - C_nil(ccx) + return; }, "prefetch_read_data" | "prefetch_write_data" | "prefetch_read_instruction" | "prefetch_write_instruction" => { @@ -270,35 +279,40 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, "prefetch_write_instruction" => (1, 0), _ => bug!() }; - bcx.call(expect, &[llargs[0], C_i32(ccx, rw), llargs[1], C_i32(ccx, cache_type)], None) + bcx.call(expect, &[ + args[0].immediate(), + C_i32(ccx, rw), + args[1].immediate(), + C_i32(ccx, cache_type) + ], None) }, "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" => { - let sty = &arg_tys[0].sty; - match int_type_width_signed(sty, ccx) { + let ty = arg_tys[0]; + match int_type_width_signed(ty, ccx) { Some((width, signed)) => match name { "ctlz" | "cttz" => { let y = C_bool(bcx.ccx, false); let llfn = ccx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); - bcx.call(llfn, &[llargs[0], y], None) + bcx.call(llfn, &[args[0].immediate(), y], None) } "ctlz_nonzero" | "cttz_nonzero" => { let y = C_bool(bcx.ccx, true); let llvm_name = &format!("llvm.{}.i{}", &name[..4], width); let llfn = ccx.get_intrinsic(llvm_name); - bcx.call(llfn, &[llargs[0], y], None) + bcx.call(llfn, &[args[0].immediate(), y], None) } "ctpop" => bcx.call(ccx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), - &llargs, None), + &[args[0].immediate()], None), "bswap" => { if width == 8 { - llargs[0] // byte swap a u8/i8 is just a no-op + args[0].immediate() // byte swap a u8/i8 is just a no-op } else { bcx.call(ccx.get_intrinsic(&format!("llvm.bswap.i{}", width)), - &llargs, None) + &[args[0].immediate()], None) } } "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { @@ -308,35 +322,41 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let llfn = bcx.ccx.get_intrinsic(&intrinsic); // Convert `i1` to a `bool`, and write it to the out parameter - let val = bcx.call(llfn, &[llargs[0], llargs[1]], None); - let result = bcx.extract_value(val, 0); - let overflow = bcx.zext(bcx.extract_value(val, 1), Type::bool(ccx)); - bcx.store(result, bcx.struct_gep(llresult, 0), None); - bcx.store(overflow, bcx.struct_gep(llresult, 1), None); - - C_nil(bcx.ccx) + let pair = bcx.call(llfn, &[ + args[0].immediate(), + args[1].immediate() + ], None); + let val = bcx.extract_value(pair, 0); + let overflow = bcx.zext(bcx.extract_value(pair, 1), Type::bool(ccx)); + + let dest = result.project_field(bcx, 0); + bcx.store(val, dest.llval, dest.alignment.non_abi()); + let dest = result.project_field(bcx, 1); + bcx.store(overflow, dest.llval, dest.alignment.non_abi()); + + return; }, - "overflowing_add" => bcx.add(llargs[0], llargs[1]), - "overflowing_sub" => bcx.sub(llargs[0], llargs[1]), - "overflowing_mul" => bcx.mul(llargs[0], llargs[1]), + "overflowing_add" => bcx.add(args[0].immediate(), args[1].immediate()), + "overflowing_sub" => bcx.sub(args[0].immediate(), args[1].immediate()), + "overflowing_mul" => bcx.mul(args[0].immediate(), args[1].immediate()), "unchecked_div" => if signed { - bcx.sdiv(llargs[0], llargs[1]) + bcx.sdiv(args[0].immediate(), args[1].immediate()) } else { - bcx.udiv(llargs[0], llargs[1]) + bcx.udiv(args[0].immediate(), args[1].immediate()) }, "unchecked_rem" => if signed { - bcx.srem(llargs[0], llargs[1]) + bcx.srem(args[0].immediate(), args[1].immediate()) } else { - bcx.urem(llargs[0], llargs[1]) + bcx.urem(args[0].immediate(), args[1].immediate()) }, - "unchecked_shl" => bcx.shl(llargs[0], llargs[1]), + "unchecked_shl" => bcx.shl(args[0].immediate(), args[1].immediate()), "unchecked_shr" => if signed { - bcx.ashr(llargs[0], llargs[1]) + bcx.ashr(args[0].immediate(), args[1].immediate()) } else { - bcx.lshr(llargs[0], llargs[1]) + bcx.lshr(args[0].immediate(), args[1].immediate()) }, _ => bug!(), }, @@ -344,8 +364,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, span_invalid_monomorphization_error( tcx.sess, span, &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); - C_nil(ccx) + expected basic integer type, found `{}`", name, ty)); + return; } } @@ -355,11 +375,11 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, match float_type_width(sty) { Some(_width) => match name { - "fadd_fast" => bcx.fadd_fast(llargs[0], llargs[1]), - "fsub_fast" => bcx.fsub_fast(llargs[0], llargs[1]), - "fmul_fast" => bcx.fmul_fast(llargs[0], llargs[1]), - "fdiv_fast" => bcx.fdiv_fast(llargs[0], llargs[1]), - "frem_fast" => bcx.frem_fast(llargs[0], llargs[1]), + "fadd_fast" => bcx.fadd_fast(args[0].immediate(), args[1].immediate()), + "fsub_fast" => bcx.fsub_fast(args[0].immediate(), args[1].immediate()), + "fmul_fast" => bcx.fmul_fast(args[0].immediate(), args[1].immediate()), + "fdiv_fast" => bcx.fdiv_fast(args[0].immediate(), args[1].immediate()), + "frem_fast" => bcx.frem_fast(args[0].immediate(), args[1].immediate()), _ => bug!(), }, None => { @@ -367,40 +387,37 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, tcx.sess, span, &format!("invalid monomorphization of `{}` intrinsic: \ expected basic float type, found `{}`", name, sty)); - C_nil(ccx) + return; } } }, "discriminant_value" => { - let val_ty = substs.type_at(0); - match val_ty.sty { - ty::TyAdt(adt, ..) if adt.is_enum() => { - adt::trans_get_discr(bcx, val_ty, llargs[0], Alignment::AbiAligned, - Some(llret_ty), true) - } - _ => C_null(llret_ty) - } + args[0].deref(bcx.ccx).trans_get_discr(bcx, ret_ty) } "align_offset" => { // `ptr as usize` - let ptr_val = bcx.ptrtoint(llargs[0], bcx.ccx.isize_ty()); + let ptr_val = bcx.ptrtoint(args[0].immediate(), bcx.ccx.isize_ty()); // `ptr_val % align` - let offset = bcx.urem(ptr_val, llargs[1]); + let align = args[1].immediate(); + let offset = bcx.urem(ptr_val, align); let zero = C_null(bcx.ccx.isize_ty()); // `offset == 0` let is_zero = bcx.icmp(llvm::IntPredicate::IntEQ, offset, zero); // `if offset == 0 { 0 } else { offset - align }` - bcx.select(is_zero, zero, bcx.sub(offset, llargs[1])) + bcx.select(is_zero, zero, bcx.sub(offset, align)) } name if name.starts_with("simd_") => { - generic_simd_intrinsic(bcx, name, - callee_ty, - &llargs, - ret_ty, llret_ty, - span) + match generic_simd_intrinsic(bcx, name, + callee_ty, + args, + ret_ty, llret_ty, + span) { + Ok(llval) => llval, + Err(()) => return + } } // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]", and no ordering means SeqCst @@ -434,57 +451,66 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, _ => ccx.sess().fatal("Atomic intrinsic not in correct format"), }; - let invalid_monomorphization = |sty| { + let invalid_monomorphization = |ty| { span_invalid_monomorphization_error(tcx.sess, span, &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); + expected basic integer type, found `{}`", name, ty)); }; match split[1] { "cxchg" | "cxchgweak" => { - let sty = &substs.type_at(0).sty; - if int_type_width_signed(sty, ccx).is_some() { + let ty = substs.type_at(0); + if int_type_width_signed(ty, ccx).is_some() { let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; - let val = bcx.atomic_cmpxchg(llargs[0], llargs[1], llargs[2], order, - failorder, weak); - let result = bcx.extract_value(val, 0); - let success = bcx.zext(bcx.extract_value(val, 1), Type::bool(bcx.ccx)); - bcx.store(result, bcx.struct_gep(llresult, 0), None); - bcx.store(success, bcx.struct_gep(llresult, 1), None); + let pair = bcx.atomic_cmpxchg( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + order, + failorder, + weak); + let val = bcx.extract_value(pair, 0); + let success = bcx.zext(bcx.extract_value(pair, 1), Type::bool(bcx.ccx)); + + let dest = result.project_field(bcx, 0); + bcx.store(val, dest.llval, dest.alignment.non_abi()); + let dest = result.project_field(bcx, 1); + bcx.store(success, dest.llval, dest.alignment.non_abi()); + return; } else { - invalid_monomorphization(sty); + return invalid_monomorphization(ty); } - C_nil(ccx) } "load" => { - let sty = &substs.type_at(0).sty; - if int_type_width_signed(sty, ccx).is_some() { - bcx.atomic_load(llargs[0], order) + let ty = substs.type_at(0); + if int_type_width_signed(ty, ccx).is_some() { + let align = ccx.align_of(ty); + bcx.atomic_load(args[0].immediate(), order, align) } else { - invalid_monomorphization(sty); - C_nil(ccx) + return invalid_monomorphization(ty); } } "store" => { - let sty = &substs.type_at(0).sty; - if int_type_width_signed(sty, ccx).is_some() { - bcx.atomic_store(llargs[1], llargs[0], order); + let ty = substs.type_at(0); + if int_type_width_signed(ty, ccx).is_some() { + let align = ccx.align_of(ty); + bcx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); + return; } else { - invalid_monomorphization(sty); + return invalid_monomorphization(ty); } - C_nil(ccx) } "fence" => { bcx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); - C_nil(ccx) + return; } "singlethreadfence" => { bcx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); - C_nil(ccx) + return; } // These are all AtomicRMW ops @@ -504,17 +530,32 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, _ => ccx.sess().fatal("unknown atomic operation") }; - let sty = &substs.type_at(0).sty; - if int_type_width_signed(sty, ccx).is_some() { - bcx.atomic_rmw(atom_op, llargs[0], llargs[1], order) + let ty = substs.type_at(0); + if int_type_width_signed(ty, ccx).is_some() { + bcx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) } else { - invalid_monomorphization(sty); - C_nil(ccx) + return invalid_monomorphization(ty); } } } } + "nontemporal_store" => { + let tp_ty = substs.type_at(0); + let dst = args[0].deref(bcx.ccx); + let val = if let OperandValue::Ref(ptr, align) = args[1].val { + bcx.load(ptr, align.non_abi()) + } else { + from_immediate(bcx, args[1].immediate()) + }; + let ptr = bcx.pointercast(dst.llval, val_ty(val).ptr_to()); + let store = bcx.nontemporal_store(val, ptr); + unsafe { + llvm::LLVMSetAlignment(store, ccx.align_of(tp_ty).abi() as u32); + } + return + } + _ => { let intr = match Intrinsic::find(&name) { Some(intr) => intr, @@ -524,13 +565,11 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, assert_eq!(x.len(), 1); x.into_iter().next().unwrap() } - fn ty_to_type(ccx: &CrateContext, t: &intrinsics::Type, - any_changes_needed: &mut bool) -> Vec { + fn ty_to_type(ccx: &CrateContext, t: &intrinsics::Type) -> Vec { use intrinsics::Type::*; match *t { Void => vec![Type::void(ccx)], - Integer(_signed, width, llvm_width) => { - *any_changes_needed |= width != llvm_width; + Integer(_signed, _width, llvm_width) => { vec![Type::ix(ccx, llvm_width as u64)] } Float(x) => { @@ -541,29 +580,24 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } } Pointer(ref t, ref llvm_elem, _const) => { - *any_changes_needed |= llvm_elem.is_some(); - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(ccx, t, any_changes_needed)); + let elem = one(ty_to_type(ccx, t)); vec![elem.ptr_to()] } Vector(ref t, ref llvm_elem, length) => { - *any_changes_needed |= llvm_elem.is_some(); - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(ccx, t, any_changes_needed)); + let elem = one(ty_to_type(ccx, t)); vec![Type::vector(&elem, length as u64)] } Aggregate(false, ref contents) => { let elems = contents.iter() - .map(|t| one(ty_to_type(ccx, t, any_changes_needed))) + .map(|t| one(ty_to_type(ccx, t))) .collect::>(); vec![Type::struct_(ccx, &elems, false)] } Aggregate(true, ref contents) => { - *any_changes_needed = true; contents.iter() - .flat_map(|t| ty_to_type(ccx, t, any_changes_needed)) + .flat_map(|t| ty_to_type(ccx, t)) .collect() } } @@ -575,8 +609,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // cast. fn modify_as_needed<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: &intrinsics::Type, - arg_type: Ty<'tcx>, - llarg: ValueRef) + arg: &OperandRef<'tcx>) -> Vec { match *t { @@ -587,55 +620,44 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // This assumes the type is "simple", i.e. no // destructors, and the contents are SIMD // etc. - assert!(!bcx.ccx.shared().type_needs_drop(arg_type)); - let arg = LvalueRef::new_sized_ty(llarg, arg_type, Alignment::AbiAligned); + assert!(!bcx.ccx.shared().type_needs_drop(arg.layout.ty)); + let (ptr, align) = match arg.val { + OperandValue::Ref(ptr, align) => (ptr, align), + _ => bug!() + }; + let arg = PlaceRef::new_sized(ptr, arg.layout, align); (0..contents.len()).map(|i| { - let (ptr, align) = arg.trans_field_ptr(bcx, i); - bcx.load(ptr, align.to_align()) + arg.project_field(bcx, i).load(bcx).immediate() }).collect() } intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { - let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); - vec![bcx.pointercast(llarg, llvm_elem.ptr_to())] + let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem)); + vec![bcx.pointercast(arg.immediate(), llvm_elem.ptr_to())] } intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { - let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); - vec![bcx.bitcast(llarg, Type::vector(&llvm_elem, length as u64))] + let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem)); + vec![bcx.bitcast(arg.immediate(), Type::vector(&llvm_elem, length as u64))] } intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { // the LLVM intrinsic uses a smaller integer // size than the C intrinsic's signature, so // we have to trim it down here. - vec![bcx.trunc(llarg, Type::ix(bcx.ccx, llvm_width as u64))] + vec![bcx.trunc(arg.immediate(), Type::ix(bcx.ccx, llvm_width as u64))] } - _ => vec![llarg], + _ => vec![arg.immediate()], } } - let mut any_changes_needed = false; let inputs = intr.inputs.iter() - .flat_map(|t| ty_to_type(ccx, t, &mut any_changes_needed)) + .flat_map(|t| ty_to_type(ccx, t)) .collect::>(); - let mut out_changes = false; - let outputs = one(ty_to_type(ccx, &intr.output, &mut out_changes)); - // outputting a flattened aggregate is nonsense - assert!(!out_changes); + let outputs = one(ty_to_type(ccx, &intr.output)); - let llargs = if !any_changes_needed { - // no aggregates to flatten, so no change needed - llargs.to_vec() - } else { - // there are some aggregates that need to be flattened - // in the LLVM call, so we need to run over the types - // again to find them and extract the arguments - intr.inputs.iter() - .zip(llargs) - .zip(arg_tys) - .flat_map(|((t, llarg), ty)| modify_as_needed(bcx, t, ty, *llarg)) - .collect() - }; + let llargs: Vec<_> = intr.inputs.iter().zip(args).flat_map(|(t, arg)| { + modify_as_needed(bcx, t, arg) + }).collect(); assert_eq!(inputs.len(), llargs.len()); let val = match intr.definition { @@ -653,25 +675,24 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, assert!(!flatten); for i in 0..elems.len() { - let val = bcx.extract_value(val, i); - let lval = LvalueRef::new_sized_ty(llresult, ret_ty, - Alignment::AbiAligned); - let (dest, align) = lval.trans_field_ptr(bcx, i); - bcx.store(val, dest, align.to_align()); + let dest = result.project_field(bcx, i); + let val = bcx.extract_value(val, i as u64); + bcx.store(val, dest.llval, dest.alignment.non_abi()); } - C_nil(ccx) + return; } _ => val, } } }; - 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()); + if !fn_ty.ret.is_ignore() { + if let PassMode::Cast(ty) = fn_ty.ret.mode { + let ptr = bcx.pointercast(llresult, ty.llvm_type(ccx).ptr_to()); bcx.store(llval, ptr, Some(ccx.align_of(ret_ty))); } else { - store_ty(bcx, llval, llresult, Alignment::AbiAligned, ret_ty); + OperandRef::from_immediate_or_packed_pair(bcx, llval, result.layout) + .val.store(bcx, result); } } } @@ -679,16 +700,15 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, fn copy_intrinsic<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, allow_overlap: bool, volatile: bool, - tp_ty: Ty<'tcx>, + ty: Ty<'tcx>, dst: ValueRef, src: ValueRef, count: ValueRef) -> ValueRef { let ccx = bcx.ccx; - let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, ccx.align_of(tp_ty) as i32); - let size = machine::llsize_of(ccx, lltp_ty); - let int_size = machine::llbitsize_of_real(ccx, ccx.isize_ty()); + let (size, align) = ccx.size_and_align_of(ty); + let size = C_usize(ccx, size.bytes()); + let align = C_i32(ccx, align.abi() as i32); let operation = if allow_overlap { "memmove" @@ -696,7 +716,8 @@ fn copy_intrinsic<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, "memcpy" }; - let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, int_size); + let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, + ccx.data_layout().pointer_size.bits()); let dst_ptr = bcx.pointercast(dst, Type::i8p(ccx)); let src_ptr = bcx.pointercast(src, Type::i8p(ccx)); @@ -720,9 +741,9 @@ fn memset_intrinsic<'a, 'tcx>( count: ValueRef ) -> ValueRef { let ccx = bcx.ccx; - let align = C_i32(ccx, ccx.align_of(ty) as i32); - let lltp_ty = type_of::type_of(ccx, ty); - let size = machine::llsize_of(ccx, lltp_ty); + let (size, align) = ccx.size_and_align_of(ty); + let size = C_usize(ccx, size.bytes()); + let align = C_i32(ccx, align.abi() as i32); let dst = bcx.pointercast(dst, Type::i8p(ccx)); call_memset(bcx, dst, val, bcx.mul(size, count), align, volatile) } @@ -812,7 +833,7 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // // More information can be found in libstd's seh.rs implementation. let i64p = Type::i64(ccx).ptr_to(); - let slot = bcx.alloca(i64p, "slot", None); + let slot = bcx.alloca(i64p, "slot", ccx.data_layout().pointer_align); bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); @@ -968,11 +989,11 @@ fn generic_simd_intrinsic<'a, 'tcx>( bcx: &Builder<'a, 'tcx>, name: &str, callee_ty: Ty<'tcx>, - llargs: &[ValueRef], + args: &[OperandRef<'tcx>], ret_ty: Ty<'tcx>, llret_ty: Type, span: Span -) -> ValueRef { +) -> Result { // macros for error handling: macro_rules! emit_error { ($msg: tt) => { @@ -990,7 +1011,7 @@ fn generic_simd_intrinsic<'a, 'tcx>( ($cond: expr, $($fmt: tt)*) => { if !$cond { emit_error!($($fmt)*); - return C_nil(bcx.ccx) + return Err(()); } } } @@ -1036,12 +1057,12 @@ fn generic_simd_intrinsic<'a, 'tcx>( ret_ty, ret_ty.simd_type(tcx)); - return compare_simd_types(bcx, - llargs[0], - llargs[1], - in_elem, - llret_ty, - cmp_op) + return Ok(compare_simd_types(bcx, + args[0].immediate(), + args[1].immediate(), + in_elem, + llret_ty, + cmp_op)) } if name.starts_with("simd_shuffle") { @@ -1065,12 +1086,12 @@ fn generic_simd_intrinsic<'a, 'tcx>( let total_len = in_len as u128 * 2; - let vector = llargs[2]; + let vector = args[2].immediate(); let indices: Option> = (0..n) .map(|i| { let arg_idx = i; - let val = const_get_elt(vector, &[i as libc::c_uint]); + let val = const_get_elt(vector, i as u64); match const_to_opt_u128(val, true) { None => { emit_error!("shuffle index #{} is not a constant", arg_idx); @@ -1087,23 +1108,27 @@ fn generic_simd_intrinsic<'a, 'tcx>( .collect(); let indices = match indices { Some(i) => i, - None => return C_null(llret_ty) + None => return Ok(C_null(llret_ty)) }; - return bcx.shuffle_vector(llargs[0], llargs[1], C_vector(&indices)) + return Ok(bcx.shuffle_vector(args[0].immediate(), + args[1].immediate(), + C_vector(&indices))) } if name == "simd_insert" { require!(in_elem == arg_tys[2], "expected inserted type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, arg_tys[2]); - return bcx.insert_element(llargs[0], llargs[2], llargs[1]) + return Ok(bcx.insert_element(args[0].immediate(), + args[2].immediate(), + args[1].immediate())) } if name == "simd_extract" { require!(ret_ty == in_elem, "expected return type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, ret_ty); - return bcx.extract_element(llargs[0], llargs[1]) + return Ok(bcx.extract_element(args[0].immediate(), args[1].immediate())) } if name == "simd_cast" { @@ -1117,7 +1142,7 @@ fn generic_simd_intrinsic<'a, 'tcx>( // casting cares about nominal type, not just structural type let out_elem = ret_ty.simd_type(tcx); - if in_elem == out_elem { return llargs[0]; } + if in_elem == out_elem { return Ok(args[0].immediate()); } enum Style { Float, Int(/* is signed? */ bool), Unsupported } @@ -1138,36 +1163,36 @@ fn generic_simd_intrinsic<'a, 'tcx>( match (in_style, out_style) { (Style::Int(in_is_signed), Style::Int(_)) => { - return match in_width.cmp(&out_width) { - Ordering::Greater => bcx.trunc(llargs[0], llret_ty), - Ordering::Equal => llargs[0], + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bcx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), Ordering::Less => if in_is_signed { - bcx.sext(llargs[0], llret_ty) + bcx.sext(args[0].immediate(), llret_ty) } else { - bcx.zext(llargs[0], llret_ty) + bcx.zext(args[0].immediate(), llret_ty) } - } + }) } (Style::Int(in_is_signed), Style::Float) => { - return if in_is_signed { - bcx.sitofp(llargs[0], llret_ty) + return Ok(if in_is_signed { + bcx.sitofp(args[0].immediate(), llret_ty) } else { - bcx.uitofp(llargs[0], llret_ty) - } + bcx.uitofp(args[0].immediate(), llret_ty) + }) } (Style::Float, Style::Int(out_is_signed)) => { - return if out_is_signed { - bcx.fptosi(llargs[0], llret_ty) + return Ok(if out_is_signed { + bcx.fptosi(args[0].immediate(), llret_ty) } else { - bcx.fptoui(llargs[0], llret_ty) - } + bcx.fptoui(args[0].immediate(), llret_ty) + }) } (Style::Float, Style::Float) => { - return match in_width.cmp(&out_width) { - Ordering::Greater => bcx.fptrunc(llargs[0], llret_ty), - Ordering::Equal => llargs[0], - Ordering::Less => bcx.fpext(llargs[0], llret_ty) - } + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bcx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bcx.fpext(args[0].immediate(), llret_ty) + }) } _ => {/* Unsupported. Fallthrough. */} } @@ -1178,28 +1203,26 @@ fn generic_simd_intrinsic<'a, 'tcx>( } macro_rules! arith { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { - $( - if name == stringify!($name) { - match in_elem.sty { - $( - $(ty::$p(_))|* => { - return bcx.$call(llargs[0], llargs[1]) - } - )* - _ => {}, - } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) - })* + $(if name == stringify!($name) { + match in_elem.sty { + $($(ty::$p(_))|* => { + return Ok(bcx.$call(args[0].immediate(), args[1].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* } } arith! { simd_add: TyUint, TyInt => add, TyFloat => fadd; simd_sub: TyUint, TyInt => sub, TyFloat => fsub; simd_mul: TyUint, TyInt => mul, TyFloat => fmul; - simd_div: TyFloat => fdiv; + simd_div: TyUint => udiv, TyInt => sdiv, TyFloat => fdiv; + simd_rem: TyUint => urem, TyInt => srem, TyFloat => frem; simd_shl: TyUint, TyInt => shl; simd_shr: TyUint => lshr, TyInt => ashr; simd_and: TyUint, TyInt => and; @@ -1209,15 +1232,13 @@ fn generic_simd_intrinsic<'a, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } -// Returns the width of an int TypeVariant, and if it's signed or not +// Returns the width of an int Ty, and if it's signed or not // Returns None if the type is not an integer // FIXME: there’s multiple of this functions, investigate using some of the already existing // stuffs. -fn int_type_width_signed<'tcx>(sty: &ty::TypeVariants<'tcx>, ccx: &CrateContext) - -> Option<(u64, bool)> { - use rustc::ty::{TyInt, TyUint}; - match *sty { - TyInt(t) => Some((match t { +fn int_type_width_signed(ty: Ty, ccx: &CrateContext) -> Option<(u64, bool)> { + match ty.sty { + ty::TyInt(t) => Some((match t { ast::IntTy::Is => { match &ccx.tcx().sess.target.target.target_pointer_width[..] { "16" => 16, @@ -1232,7 +1253,7 @@ fn int_type_width_signed<'tcx>(sty: &ty::TypeVariants<'tcx>, ccx: &CrateContext) ast::IntTy::I64 => 64, ast::IntTy::I128 => 128, }, true)), - TyUint(t) => Some((match t { + ty::TyUint(t) => Some((match t { ast::UintTy::Us => { match &ccx.tcx().sess.target.target.target_pointer_width[..] { "16" => 16, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 796dfd4417c6a..3c2e56bf2a127 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -24,16 +24,15 @@ #![feature(custom_attribute)] #![allow(unused_attributes)] #![feature(i128_type)] +#![feature(i128)] +#![feature(inclusive_range)] +#![feature(inclusive_range_syntax)] #![feature(libc)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] #![feature(conservative_impl_trait)] -#![cfg_attr(stage0, feature(const_fn))] -#![cfg_attr(not(stage0), feature(const_atomic_bool_new))] -#![cfg_attr(not(stage0), feature(const_once_new))] - use rustc::dep_graph::WorkProduct; use syntax_pos::symbol::Symbol; @@ -43,17 +42,19 @@ extern crate flate2; extern crate libc; extern crate owning_ref; #[macro_use] extern crate rustc; +extern crate jobserver; +extern crate num_cpus; extern crate rustc_allocator; +extern crate rustc_apfloat; extern crate rustc_back; +extern crate rustc_binaryen; +extern crate rustc_const_math; extern crate rustc_data_structures; +extern crate rustc_demangle; extern crate rustc_incremental; extern crate rustc_llvm as llvm; extern crate rustc_platform_intrinsics as intrinsics; -extern crate rustc_const_math; extern crate rustc_trans_utils; -extern crate rustc_demangle; -extern crate jobserver; -extern crate num_cpus; #[macro_use] extern crate log; #[macro_use] extern crate syntax; @@ -62,23 +63,36 @@ extern crate rustc_errors as errors; extern crate serialize; #[cfg(windows)] extern crate cc; // Used to locate MSVC +extern crate tempdir; pub use base::trans_crate; +use back::bytecode::RLIB_BYTECODE_EXTENSION; pub use metadata::LlvmMetadataLoader; pub use llvm_util::{init, target_features, print_version, print_passes, print, enable_llvm_debug}; +use std::any::Any; +use std::path::PathBuf; use std::rc::Rc; +use std::sync::mpsc; +use rustc::dep_graph::DepGraph; use rustc::hir::def_id::CrateNum; +use rustc::middle::cstore::MetadataLoader; use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource}; -use rustc::ty::maps::Providers; +use rustc::session::Session; +use rustc::session::config::{OutputFilenames, OutputType}; +use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc_trans_utils::collector; +use rustc_trans_utils::monomorphize; + mod diagnostics; pub mod back { mod archive; + pub mod bytecode; mod command; pub(crate) mod linker; pub mod link; @@ -90,7 +104,6 @@ pub mod back { } mod abi; -mod adt; mod allocator; mod asm; mod assert_module_sources; @@ -115,7 +128,6 @@ mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64; mod callee; -mod collector; mod common; mod consts; mod context; @@ -124,28 +136,17 @@ mod declare; mod glue; mod intrinsic; mod llvm_util; -mod machine; mod metadata; mod meth; mod mir; -mod monomorphize; mod partitioning; mod symbol_names_test; mod time_graph; mod trans_item; -mod tvec; mod type_; mod type_of; mod value; -use std::sync::mpsc; -use std::any::Any; -use rustc::ty::{self, TyCtxt}; -use rustc::session::Session; -use rustc::session::config::OutputFilenames; -use rustc::middle::cstore::MetadataLoader; -use rustc::dep_graph::DepGraph; - pub struct LlvmTransCrate(()); impl LlvmTransCrate { @@ -163,12 +164,14 @@ impl rustc_trans_utils::trans_crate::TransCrate for LlvmTransCrate { box metadata::LlvmMetadataLoader } - fn provide_local(providers: &mut ty::maps::Providers) { - provide_local(providers); + fn provide(providers: &mut ty::maps::Providers) { + back::symbol_names::provide(providers); + back::symbol_export::provide(providers); + base::provide(providers); } fn provide_extern(providers: &mut ty::maps::Providers) { - provide_extern(providers); + back::symbol_export::provide_extern(providers); } fn trans_crate<'a, 'tcx>( @@ -202,12 +205,12 @@ pub struct ModuleTranslation { /// something unique to this crate (e.g., a module path) as well /// as the crate name and disambiguator. name: String, - symbol_name_hash: u64, + llmod_id: String, pub source: ModuleSource, pub kind: ModuleKind, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ModuleKind { Regular, Metadata, @@ -215,35 +218,47 @@ pub enum ModuleKind { } impl ModuleTranslation { - pub fn into_compiled_module(self, emit_obj: bool, emit_bc: bool) -> CompiledModule { + pub fn llvm(&self) -> Option<&ModuleLlvm> { + match self.source { + ModuleSource::Translated(ref llvm) => Some(llvm), + ModuleSource::Preexisting(_) => None, + } + } + + pub fn into_compiled_module(self, + emit_obj: bool, + emit_bc: bool, + emit_bc_compressed: bool, + outputs: &OutputFilenames) -> CompiledModule { let pre_existing = match self.source { ModuleSource::Preexisting(_) => true, ModuleSource::Translated(_) => false, }; + let object = if emit_obj { + Some(outputs.temp_path(OutputType::Object, Some(&self.name))) + } else { + None + }; + let bytecode = if emit_bc { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name))) + } else { + None + }; + let bytecode_compressed = if emit_bc_compressed { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name)) + .with_extension(RLIB_BYTECODE_EXTENSION)) + } else { + None + }; CompiledModule { + llmod_id: self.llmod_id, name: self.name.clone(), kind: self.kind, - symbol_name_hash: self.symbol_name_hash, pre_existing, - emit_obj, - emit_bc, - } - } -} - -impl Drop for ModuleTranslation { - fn drop(&mut self) { - match self.source { - ModuleSource::Preexisting(_) => { - // Nothing to dispose. - }, - ModuleSource::Translated(llvm) => { - unsafe { - llvm::LLVMDisposeModule(llvm.llmod); - llvm::LLVMContextDispose(llvm.llcx); - } - }, + object, + bytecode, + bytecode_compressed, } } } @@ -251,14 +266,14 @@ impl Drop for ModuleTranslation { #[derive(Debug)] pub struct CompiledModule { pub name: String, + pub llmod_id: String, pub kind: ModuleKind, - pub symbol_name_hash: u64, pub pre_existing: bool, - pub emit_obj: bool, - pub emit_bc: bool, + pub object: Option, + pub bytecode: Option, + pub bytecode_compressed: Option, } -#[derive(Clone)] pub enum ModuleSource { /// Copy the `.o` files or whatever from the incr. comp. directory. Preexisting(WorkProduct), @@ -267,19 +282,31 @@ pub enum ModuleSource { Translated(ModuleLlvm), } -#[derive(Copy, Clone, Debug)] +#[derive(Debug)] pub struct ModuleLlvm { llcx: llvm::ContextRef, pub llmod: llvm::ModuleRef, + tm: llvm::TargetMachineRef, } -unsafe impl Send for ModuleTranslation { } -unsafe impl Sync for ModuleTranslation { } +unsafe impl Send for ModuleLlvm { } +unsafe impl Sync for ModuleLlvm { } + +impl Drop for ModuleLlvm { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeModule(self.llmod); + llvm::LLVMContextDispose(self.llcx); + llvm::LLVMRustDisposeTargetMachine(self.tm); + } + } +} pub struct CrateTranslation { pub crate_name: Symbol, pub modules: Vec, allocator_module: Option, + metadata_module: CompiledModule, pub link: rustc::middle::cstore::LinkMeta, pub metadata: rustc::middle::cstore::EncodedMetadata, windows_subsystem: Option, @@ -304,15 +331,3 @@ pub struct CrateInfo { } __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } - -pub fn provide_local(providers: &mut Providers) { - back::symbol_names::provide(providers); - back::symbol_export::provide_local(providers); - base::provide_local(providers); -} - -pub fn provide_extern(providers: &mut Providers) { - back::symbol_names::provide(providers); - back::symbol_export::provide_extern(providers); - base::provide_extern(providers); -} diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index 448feb5259ddd..a9ea96134faf2 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -73,10 +73,19 @@ unsafe fn configure_llvm(sess: &Session) { const ARM_WHITELIST: &'static [&'static str] = &["neon\0", "vfp2\0", "vfp3\0", "vfp4\0"]; +const AARCH64_WHITELIST: &'static [&'static str] = &["neon\0"]; + const X86_WHITELIST: &'static [&'static str] = &["avx\0", "avx2\0", "bmi\0", "bmi2\0", "sse\0", "sse2\0", "sse3\0", "sse4.1\0", "sse4.2\0", "ssse3\0", "tbm\0", "lzcnt\0", "popcnt\0", - "sse4a\0", "rdrnd\0", "rdseed\0", "fma\0"]; + "sse4a\0", "rdrnd\0", "rdseed\0", "fma\0", + "xsave\0", "xsaveopt\0", "xsavec\0", + "xsaves\0", + "avx512bw\0", "avx512cd\0", + "avx512dq\0", "avx512er\0", + "avx512f\0", "avx512ifma\0", + "avx512pf\0", "avx512vbmi\0", + "avx512vl\0", "avx512vpopcntdq\0", "mmx\0"]; const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx\0", "hvx-double\0"]; @@ -85,13 +94,17 @@ const POWERPC_WHITELIST: &'static [&'static str] = &["altivec\0", "power8-vector\0", "power9-vector\0", "vsx\0"]; +const MIPS_WHITELIST: &'static [&'static str] = &["msa\0"]; + pub fn target_features(sess: &Session) -> Vec { let target_machine = create_target_machine(sess); let whitelist = match &*sess.target.target.arch { "arm" => ARM_WHITELIST, + "aarch64" => AARCH64_WHITELIST, "x86" | "x86_64" => X86_WHITELIST, "hexagon" => HEXAGON_WHITELIST, + "mips" | "mips64" => MIPS_WHITELIST, "powerpc" | "powerpc64" => POWERPC_WHITELIST, _ => &[], }; diff --git a/src/librustc_trans/machine.rs b/src/librustc_trans/machine.rs deleted file mode 100644 index bc383abc7e0ec..0000000000000 --- a/src/librustc_trans/machine.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Information concerning the machine representation of various types. - -#![allow(non_camel_case_types)] - -use llvm::{self, ValueRef}; -use common::*; - -use type_::Type; - -pub type llbits = u64; -pub type llsize = u64; -pub type llalign = u32; - -// ______________________________________________________________________ -// compute sizeof / alignof - -// Returns the number of bytes between successive elements of type T in an -// array of T. This is the "ABI" size. It includes any ABI-mandated padding. -pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> llsize { - unsafe { - return llvm::LLVMABISizeOfType(cx.td(), ty.to_ref()); - } -} - -/// Returns the "real" size of the type in bits. -pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> llbits { - unsafe { - llvm::LLVMSizeOfTypeInBits(cx.td(), ty.to_ref()) - } -} - -/// Returns the size of the type as an LLVM constant integer value. -pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { - // Once upon a time, this called LLVMSizeOf, which does a - // getelementptr(1) on a null pointer and casts to an int, in - // order to obtain the type size as a value without requiring the - // target data layout. But we have the target data layout, so - // there's no need for that contrivance. The instruction - // selection DAG generator would flatten that GEP(1) node into a - // constant of the type's alloc size, so let's save it some work. - return C_usize(cx, llsize_of_alloc(cx, ty)); -} - -// Returns the preferred alignment of the given type for the current target. -// The preferred alignment may be larger than the alignment used when -// packing the type into structs. This will be used for things like -// allocations inside a stack frame, which LLVM has a free hand in. -pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> llalign { - unsafe { - return llvm::LLVMPreferredAlignmentOfType(cx.td(), ty.to_ref()); - } -} - -// Returns the minimum alignment of a type required by the platform. -// This is the alignment that will be used for struct fields, arrays, -// and similar ABI-mandated things. -pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> llalign { - unsafe { - return llvm::LLVMABIAlignmentOfType(cx.td(), ty.to_ref()); - } -} - -pub fn llelement_offset(cx: &CrateContext, struct_ty: Type, element: usize) -> u64 { - unsafe { - return llvm::LLVMOffsetOfElement(cx.td(), - struct_ty.to_ref(), - element as u32); - } -} diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index 88407947f0ef4..a7d467f1cc5f3 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -9,19 +9,20 @@ // except according to those terms. use llvm::ValueRef; -use rustc::traits; +use abi::FnType; use callee; use common::*; use builder::Builder; use consts; -use machine; use monomorphize; use type_::Type; use value::Value; use rustc::ty::{self, Ty}; +use rustc::ty::layout::HasDataLayout; +use debuginfo; #[derive(Copy, Clone, Debug)] -pub struct VirtualIndex(usize); +pub struct VirtualIndex(u64); pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); pub const SIZE: VirtualIndex = VirtualIndex(1); @@ -29,14 +30,18 @@ pub const ALIGN: VirtualIndex = VirtualIndex(2); impl<'a, 'tcx> VirtualIndex { pub fn from_index(index: usize) -> Self { - VirtualIndex(index + 3) + VirtualIndex(index as u64 + 3) } - pub fn get_fn(self, bcx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + pub fn get_fn(self, bcx: &Builder<'a, 'tcx>, + llvtable: ValueRef, + fn_ty: &FnType<'tcx>) -> ValueRef { // Load the data pointer from the object. debug!("get_fn({:?}, {:?})", Value(llvtable), self); - let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[self.0]), None); + let llvtable = bcx.pointercast(llvtable, fn_ty.llvm_type(bcx.ccx).ptr_to().ptr_to()); + let ptr = bcx.load(bcx.inbounds_gep(llvtable, &[C_usize(bcx.ccx, self.0)]), None); + bcx.nonnull_metadata(ptr); // Vtable loads are invariant bcx.set_invariant_load(ptr); ptr @@ -47,7 +52,7 @@ impl<'a, 'tcx> VirtualIndex { debug!("get_int({:?}, {:?})", Value(llvtable), self); let llvtable = bcx.pointercast(llvtable, Type::isize(bcx.ccx).ptr_to()); - let ptr = bcx.load(bcx.gepi(llvtable, &[self.0]), None); + let ptr = bcx.load(bcx.inbounds_gep(llvtable, &[C_usize(bcx.ccx, self.0)]), None); // Vtable loads are invariant bcx.set_invariant_load(ptr); ptr @@ -77,17 +82,19 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } // Not in the cache. Build it. - let nullptr = C_null(Type::nil(ccx).ptr_to()); + let nullptr = C_null(Type::i8p(ccx)); + let (size, align) = ccx.size_and_align_of(ty); let mut components: Vec<_> = [ callee::get_fn(ccx, monomorphize::resolve_drop_in_place(ccx.tcx(), ty)), - C_usize(ccx, ccx.size_of(ty)), - C_usize(ccx, ccx.align_of(ty) as u64) + C_usize(ccx, size.bytes()), + C_usize(ccx, align.abi()) ].iter().cloned().collect(); if let Some(trait_ref) = trait_ref { let trait_ref = trait_ref.with_self_ty(tcx, ty); - let methods = traits::get_vtable_methods(tcx, trait_ref).map(|opt_mth| { + let methods = tcx.vtable_methods(trait_ref); + let methods = methods.iter().cloned().map(|opt_mth| { opt_mth.map_or(nullptr, |(def_id, substs)| { callee::resolve_and_get_fn(ccx, def_id, substs) }) @@ -96,9 +103,11 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } let vtable_const = C_struct(ccx, &components, false); - let align = machine::llalign_of_pref(ccx, val_ty(vtable_const)); + let align = ccx.data_layout().pointer_align; let vtable = consts::addr_of(ccx, vtable_const, align, "vtable"); + debuginfo::create_vtable_metadata(ccx, ty, vtable); + ccx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable } diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 1017ec6b3c3f8..b5e5dd3b9ce16 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -15,13 +15,14 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::middle::const_val::ConstVal; use rustc::mir::{self, Location, TerminatorKind, Literal}; -use rustc::mir::visit::{Visitor, LvalueContext}; +use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::mir::traversal; use rustc::ty; -use common; +use rustc::ty::layout::LayoutOf; +use type_of::LayoutLlvmExt; use super::MirContext; -pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector { +pub fn memory_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector { let mir = mircx.mir; let mut analyzer = LocalAnalyzer::new(mircx); @@ -30,55 +31,56 @@ pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector { for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { let ty = mircx.monomorphize(&ty); debug!("local {} has type {:?}", index, ty); - if ty.is_scalar() || - ty.is_box() || - ty.is_region_ptr() || - ty.is_simd() || - common::type_is_zero_size(mircx.ccx, ty) - { + let layout = mircx.ccx.layout_of(ty); + if layout.is_llvm_immediate() { // These sorts of types are immediates that we can store // in an ValueRef without an alloca. - assert!(common::type_is_immediate(mircx.ccx, ty) || - common::type_is_fat_ptr(mircx.ccx, ty)); - } else if common::type_is_imm_pair(mircx.ccx, ty) { + } else if layout.is_llvm_scalar_pair() { // We allow pairs and uses of any of their 2 fields. } else { // These sorts of types require an alloca. Note that - // type_is_immediate() may *still* be true, particularly + // is_llvm_immediate() may *still* be true, particularly // for newtypes, but we currently force some types // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.mark_as_lvalue(mir::Local::new(index)); + analyzer.mark_as_memory(mir::Local::new(index)); } } - analyzer.lvalue_locals + analyzer.memory_locals } struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { cx: &'mir MirContext<'a, 'tcx>, - lvalue_locals: BitVector, + memory_locals: BitVector, seen_assigned: BitVector } impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { fn new(mircx: &'mir MirContext<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { - LocalAnalyzer { + let mut analyzer = LocalAnalyzer { cx: mircx, - lvalue_locals: BitVector::new(mircx.mir.local_decls.len()), + memory_locals: BitVector::new(mircx.mir.local_decls.len()), seen_assigned: BitVector::new(mircx.mir.local_decls.len()) + }; + + // Arguments get assigned to by means of the function being called + for idx in 0..mircx.mir.arg_count { + analyzer.seen_assigned.insert(idx + 1); } + + analyzer } - fn mark_as_lvalue(&mut self, local: mir::Local) { - debug!("marking {:?} as lvalue", local); - self.lvalue_locals.insert(local.index()); + fn mark_as_memory(&mut self, local: mir::Local) { + debug!("marking {:?} as memory", local); + self.memory_locals.insert(local.index()); } fn mark_assigned(&mut self, local: mir::Local) { if !self.seen_assigned.insert(local.index()) { - self.mark_as_lvalue(local); + self.mark_as_memory(local); } } } @@ -86,18 +88,18 @@ impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { fn visit_assign(&mut self, block: mir::BasicBlock, - lvalue: &mir::Lvalue<'tcx>, + place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'tcx>, location: Location) { - debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); + debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); - if let mir::Lvalue::Local(index) = *lvalue { + if let mir::Place::Local(index) = *place { self.mark_assigned(index); if !self.cx.rvalue_creates_operand(rvalue) { - self.mark_as_lvalue(index); + self.mark_as_memory(index); } } else { - self.visit_lvalue(lvalue, LvalueContext::Store, location); + self.visit_place(place, PlaceContext::Store, location); } self.visit_rvalue(rvalue, location); @@ -119,8 +121,8 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { // box_free(x) shares with `drop x` the property that it // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. - if let mir::Operand::Consume(ref lvalue) = args[0] { - self.visit_lvalue(lvalue, LvalueContext::Drop, location); + if let mir::Operand::Move(ref place) = args[0] { + self.visit_place(place, PlaceContext::Drop, location); } } _ => {} @@ -129,64 +131,80 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { self.super_terminator_kind(block, kind, location); } - fn visit_lvalue(&mut self, - lvalue: &mir::Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: &mir::Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context); - - if let mir::Lvalue::Projection(ref proj) = *lvalue { - // Allow uses of projections of immediate pair fields. - if let LvalueContext::Consume = context { - if let mir::Lvalue::Local(_) = proj.base { - if let mir::ProjectionElem::Field(..) = proj.elem { - let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx()); - - let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx())); - if common::type_is_imm_pair(self.cx.ccx, ty) { - return; - } + debug!("visit_place(place={:?}, context={:?})", place, context); + let ccx = self.cx.ccx; + + if let mir::Place::Projection(ref proj) = *place { + // Allow uses of projections that are ZSTs or from scalar fields. + let is_consume = match context { + PlaceContext::Copy | PlaceContext::Move => true, + _ => false + }; + if is_consume { + let base_ty = proj.base.ty(self.cx.mir, ccx.tcx()); + let base_ty = self.cx.monomorphize(&base_ty); + + // ZSTs don't require any actual memory access. + let elem_ty = base_ty.projection_ty(ccx.tcx(), &proj.elem).to_ty(ccx.tcx()); + let elem_ty = self.cx.monomorphize(&elem_ty); + if ccx.layout_of(elem_ty).is_zst() { + return; + } + + if let mir::ProjectionElem::Field(..) = proj.elem { + let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx())); + if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { + // Recurse with the same context, instead of `Projection`, + // potentially stopping at non-operand projections, + // which would trigger `mark_as_memory` on locals. + self.visit_place(&proj.base, context, location); + return; } } } - // A deref projection only reads the pointer, never needs the lvalue. + // A deref projection only reads the pointer, never needs the place. if let mir::ProjectionElem::Deref = proj.elem { - return self.visit_lvalue(&proj.base, LvalueContext::Consume, location); + return self.visit_place(&proj.base, PlaceContext::Copy, location); } } - self.super_lvalue(lvalue, context, location); + self.super_place(place, context, location); } fn visit_local(&mut self, &index: &mir::Local, - context: LvalueContext<'tcx>, + context: PlaceContext<'tcx>, _: Location) { match context { - LvalueContext::Call => { + PlaceContext::Call => { self.mark_assigned(index); } - LvalueContext::StorageLive | - LvalueContext::StorageDead | - LvalueContext::Validate | - LvalueContext::Inspect | - LvalueContext::Consume => {} - - LvalueContext::Store | - LvalueContext::Borrow { .. } | - LvalueContext::Projection(..) => { - self.mark_as_lvalue(index); + PlaceContext::StorageLive | + PlaceContext::StorageDead | + PlaceContext::Validate | + PlaceContext::Copy | + PlaceContext::Move => {} + + PlaceContext::Inspect | + PlaceContext::Store | + PlaceContext::Borrow { .. } | + PlaceContext::Projection(..) => { + self.mark_as_memory(index); } - LvalueContext::Drop => { - let ty = mir::Lvalue::Local(index).ty(self.cx.mir, self.cx.ccx.tcx()); + PlaceContext::Drop => { + let ty = mir::Place::Local(index).ty(self.cx.mir, self.cx.ccx.tcx()); let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx())); - // Only need the lvalue if we're actually dropping it. + // Only need the place if we're actually dropping it. if self.cx.ccx.shared().type_needs_drop(ty) { - self.mark_as_lvalue(index); + self.mark_as_memory(index); } } } @@ -221,7 +239,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { + TerminatorKind::Yield { .. } | + TerminatorKind::FalseEdges { .. } => { /* nothing to do */ } TerminatorKind::Call { cleanup: unwind, .. } | diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 9246822b33920..94c8d469c642d 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -11,30 +11,27 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc::middle::lang_items; use rustc::middle::const_val::{ConstEvalErr, ConstInt, ErrKind}; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, LayoutTyper}; +use rustc::ty::{self, TypeFoldable}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::traits; use rustc::mir; -use abi::{Abi, FnType, ArgType}; -use adt; -use base::{self, Lifetime}; +use abi::{Abi, FnType, ArgType, PassMode}; +use base; use callee; use builder::Builder; use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; -use machine::llalign_of_min; use meth; use monomorphize; -use type_of; +use type_of::LayoutLlvmExt; use type_::Type; use syntax::symbol::Symbol; use syntax_pos::Pos; -use std::cmp; - use super::{MirContext, LocalRef}; use super::constant::Const; -use super::lvalue::{Alignment, LvalueRef}; +use super::place::{Alignment, PlaceRef}; use super::operand::OperandRef; use super::operand::OperandValue::{Pair, Ref, Immediate}; @@ -119,11 +116,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { fn_ty: FnType<'tcx>, fn_ptr: ValueRef, llargs: &[ValueRef], - destination: Option<(ReturnDest, Ty<'tcx>, mir::BasicBlock)>, + destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>, cleanup: Option | { if let Some(cleanup) = cleanup { - let ret_bcx = if let Some((_, _, target)) = destination { + let ret_bcx = if let Some((_, target)) = destination { this.blocks[target] } else { this.unreachable_block() @@ -135,14 +132,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { cleanup_bundle); fn_ty.apply_attrs_callsite(invokeret); - if let Some((ret_dest, ret_ty, target)) = destination { + if let Some((ret_dest, target)) = destination { let ret_bcx = this.get_builder(target); this.set_debug_loc(&ret_bcx, terminator.source_info); - let op = OperandRef { - val: Immediate(invokeret), - ty: ret_ty, - }; - this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, op); + this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, invokeret); } } else { let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle); @@ -155,12 +148,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); } - if let Some((ret_dest, ret_ty, target)) = destination { - let op = OperandRef { - val: Immediate(llret), - ty: ret_ty, - }; - this.store_return(&bcx, ret_dest, &fn_ty.ret, op); + if let Some((ret_dest, target)) = destination { + this.store_return(&bcx, ret_dest, &fn_ty.ret, llret); funclet_br(this, bcx, target); } else { bcx.unreachable(); @@ -174,14 +163,18 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { if let Some(cleanup_pad) = cleanup_pad { bcx.cleanup_ret(cleanup_pad, None); } else { - let ps = self.get_personality_slot(&bcx); - let lp = bcx.load(ps, None); - Lifetime::End.call(&bcx, ps); + let slot = self.get_personality_slot(&bcx); + let lp0 = slot.project_field(&bcx, 0).load(&bcx).immediate(); + let lp1 = slot.project_field(&bcx, 1).load(&bcx).immediate(); + slot.storage_dead(&bcx); + if !bcx.sess().target.target.options.custom_unwind_resume { + let mut lp = C_undef(self.landing_pad_type()); + lp = bcx.insert_value(lp, lp0, 0); + lp = bcx.insert_value(lp, lp1, 1); bcx.resume(lp); } else { - let exc_ptr = bcx.extract_value(lp, 0); - bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], cleanup_bundle); + bcx.call(bcx.ccx.eh_unwind_resume(), &[lp0], cleanup_bundle); bcx.unreachable(); } } @@ -214,45 +207,47 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } mir::TerminatorKind::Return => { - let ret = self.fn_ty.ret; - if ret.is_ignore() || ret.is_indirect() { - bcx.ret_void(); - return; - } + let llval = match self.fn_ty.ret.mode { + PassMode::Ignore | PassMode::Indirect(_) => { + bcx.ret_void(); + return; + } - let llval = if let Some(cast_ty) = ret.cast { - let op = match self.locals[mir::RETURN_POINTER] { - LocalRef::Operand(Some(op)) => op, - LocalRef::Operand(None) => bug!("use of return before def"), - LocalRef::Lvalue(tr_lvalue) => { - OperandRef { - val: Ref(tr_lvalue.llval, tr_lvalue.alignment), - ty: tr_lvalue.ty.to_ty(bcx.tcx()) - } - } - }; - let llslot = match op.val { - Immediate(_) | Pair(..) => { - let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret", None); - self.store_operand(&bcx, llscratch, None, op); - llscratch - } - Ref(llval, align) => { - assert_eq!(align, Alignment::AbiAligned, - "return pointer is unaligned!"); - llval + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = self.trans_consume(&bcx, &mir::Place::Local(mir::RETURN_PLACE)); + if let Ref(llval, align) = op.val { + bcx.load(llval, align.non_abi()) + } else { + op.immediate_or_packed_pair(&bcx) } - }; - let load = bcx.load( - bcx.pointercast(llslot, cast_ty.ptr_to()), - Some(ret.layout.align(bcx.ccx).abi() as u32)); - load - } else { - let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); - if let Ref(llval, align) = op.val { - base::load_ty(&bcx, llval, align, op.ty) - } else { - op.pack_if_pair(&bcx).immediate() + } + + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_PLACE] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Place(tr_place) => { + OperandRef { + val: Ref(tr_place.llval, tr_place.alignment), + layout: tr_place.layout + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = PlaceRef::alloca(&bcx, self.fn_ty.ret.layout, "ret"); + op.val.store(&bcx, scratch); + scratch.llval + } + Ref(llval, align) => { + assert_eq!(align, Alignment::AbiAligned, + "return place is unaligned!"); + llval + } + }; + bcx.load( + bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()), + Some(self.fn_ty.ret.layout.align)) } }; bcx.ret(llval); @@ -273,16 +268,25 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return } - let lvalue = self.trans_lvalue(&bcx, location); - let fn_ty = FnType::of_instance(bcx.ccx, &drop_fn); - let (drop_fn, need_extra) = match ty.sty { - ty::TyDynamic(..) => (meth::DESTRUCTOR.get_fn(&bcx, lvalue.llextra), - false), - _ => (callee::get_fn(bcx.ccx, drop_fn), lvalue.has_extra()) + let place = self.trans_place(&bcx, location); + let mut args: &[_] = &[place.llval, place.llextra]; + args = &args[..1 + place.has_extra() as usize]; + let (drop_fn, fn_ty) = match ty.sty { + ty::TyDynamic(..) => { + let fn_ty = common::instance_ty(bcx.ccx.tcx(), &drop_fn); + let sig = common::ty_fn_sig(bcx.ccx, fn_ty); + let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig); + let fn_ty = FnType::new_vtable(bcx.ccx, sig, &[]); + args = &args[..1]; + (meth::DESTRUCTOR.get_fn(&bcx, place.llextra, &fn_ty), fn_ty) + } + _ => { + (callee::get_fn(bcx.ccx, drop_fn), + FnType::of_instance(bcx.ccx, &drop_fn)) + } }; - let args = &[lvalue.llval, lvalue.llextra][..1 + need_extra as usize]; do_call(self, bcx, fn_ty, drop_fn, args, - Some((ReturnDest::Nothing, tcx.mk_nil(), target)), + Some((ReturnDest::Nothing, target)), unwind); } @@ -335,6 +339,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let filename = C_str_slice(bcx.ccx, filename); let line = C_u32(bcx.ccx, loc.line as u32); let col = C_u32(bcx.ccx, loc.col.to_usize() as u32 + 1); + let align = tcx.data_layout.aggregate_align + .max(tcx.data_layout.i32_align) + .max(tcx.data_layout.pointer_align); // Put together the arguments to the panic entry point. let (lang_item, args, const_err) = match *msg { @@ -350,7 +357,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { })); let file_line_col = C_struct(bcx.ccx, &[filename, line, col], false); - let align = llalign_of_min(bcx.ccx, common::val_ty(file_line_col)); let file_line_col = consts::addr_of(bcx.ccx, file_line_col, align, @@ -365,7 +371,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let msg_file_line_col = C_struct(bcx.ccx, &[msg_str, filename, line, col], false); - let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line_col)); let msg_file_line_col = consts::addr_of(bcx.ccx, msg_file_line_col, align, @@ -386,7 +391,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let msg_file_line_col = C_struct(bcx.ccx, &[msg_str, filename, line, col], false); - let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line_col)); let msg_file_line_col = consts::addr_of(bcx.ccx, msg_file_line_col, align, @@ -427,18 +431,21 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); - let (instance, mut llfn) = match callee.ty.sty { + let (instance, mut llfn) = match callee.layout.ty.sty { ty::TyFnDef(def_id, substs) => { - (Some(monomorphize::resolve(bcx.ccx.tcx(), def_id, substs)), + (Some(ty::Instance::resolve(bcx.ccx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap()), None) } ty::TyFnPtr(_) => { (None, Some(callee.immediate())) } - _ => bug!("{} is not callable", callee.ty) + _ => bug!("{} is not callable", callee.layout.ty) }; let def = instance.map(|i| i.def); - let sig = callee.ty.fn_sig(bcx.tcx()); + let sig = callee.layout.ty.fn_sig(bcx.tcx()); let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig); let abi = sig.abi; @@ -489,74 +496,52 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { ReturnDest::Nothing }; - // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { - let (tup, args) = args.split_last().unwrap(); - (args, Some(tup)) - } else { - (&args[..], None) - }; - - let is_shuffle = intrinsic.map_or(false, |name| { - name.starts_with("simd_shuffle") - }); - let mut idx = 0; - for arg in first_args { - // The indices passed to simd_shuffle* in the - // third argument must be constant. This is - // checked by const-qualification, which also - // promotes any complex rvalues to constants. - if is_shuffle && idx == 2 { - match *arg { - mir::Operand::Consume(_) => { - span_bug!(span, "shuffle indices must be constant"); - } - mir::Operand::Constant(ref constant) => { - let val = self.trans_constant(&bcx, constant); - llargs.push(val.llval); - idx += 1; - continue; - } - } - } - - let op = self.trans_operand(&bcx, arg); - self.trans_argument(&bcx, op, &mut llargs, &fn_ty, - &mut idx, &mut llfn, &def); - } - if let Some(tup) = untuple { - self.trans_arguments_untupled(&bcx, tup, &mut llargs, &fn_ty, - &mut idx, &mut llfn, &def) - } - if intrinsic.is_some() && intrinsic != Some("drop_in_place") { use intrinsic::trans_intrinsic_call; - let (dest, llargs) = match ret_dest { - _ if fn_ty.ret.is_indirect() => { - (llargs[0], &llargs[1..]) - } + let dest = match ret_dest { + _ if fn_ty.ret.is_indirect() => llargs[0], ReturnDest::Nothing => { - (C_undef(fn_ty.ret.memory_ty(bcx.ccx).ptr_to()), &llargs[..]) + C_undef(fn_ty.ret.memory_ty(bcx.ccx).ptr_to()) } ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => (dst, &llargs[..]), + ReturnDest::Store(dst) => dst.llval, ReturnDest::DirectOperand(_) => bug!("Cannot use direct operand with an intrinsic call") }; + let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { + match *arg { + mir::Operand::Copy(_) | + mir::Operand::Move(_) => { + span_bug!(span, "shuffle indices must be constant"); + } + mir::Operand::Constant(ref constant) => { + let val = self.trans_constant(&bcx, constant); + return OperandRef { + val: Immediate(val.llval), + layout: bcx.ccx.layout_of(val.ty) + }; + } + } + } + + self.trans_operand(&bcx, arg) + }).collect(); + + let callee_ty = common::instance_ty( bcx.ccx.tcx(), instance.as_ref().unwrap()); - trans_intrinsic_call(&bcx, callee_ty, &fn_ty, &llargs, dest, + trans_intrinsic_call(&bcx, callee_ty, &fn_ty, &args, dest, terminator.source_info.span); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - // Make a fake operand for store_return - let op = OperandRef { - val: Ref(dst, Alignment::AbiAligned), - ty: sig.output(), - }; - self.store_return(&bcx, ret_dest, &fn_ty.ret, op); + self.store_return(&bcx, ret_dest, &fn_ty.ret, dst.llval); } if let Some((_, target)) = *destination { @@ -568,6 +553,44 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return; } + // Split the rust-call tupled arguments off. + let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { + let (tup, args) = args.split_last().unwrap(); + (args, Some(tup)) + } else { + (&args[..], None) + }; + + for (i, arg) in first_args.iter().enumerate() { + let mut op = self.trans_operand(&bcx, arg); + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(data_ptr, meta) = op.val { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&bcx, meta, &fn_ty)); + llargs.push(data_ptr); + continue; + } + } + + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + match (arg, op.val) { + (&mir::Operand::Copy(_), Ref(..)) | + (&mir::Operand::Constant(_), Ref(..)) => { + let tmp = PlaceRef::alloca(&bcx, op.layout, "const"); + op.val.store(&bcx, tmp); + op.val = Ref(tmp.llval, tmp.alignment); + } + _ => {} + } + + self.trans_argument(&bcx, op, &mut llargs, &fn_ty.args[i]); + } + if let Some(tup) = untuple { + self.trans_arguments_untupled(&bcx, tup, &mut llargs, + &fn_ty.args[first_args.len()..]) + } + let fn_ptr = match (llfn, instance) { (Some(llfn), _) => llfn, (None, Some(instance)) => callee::get_fn(bcx.ccx, instance), @@ -575,11 +598,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; do_call(self, bcx, fn_ty, fn_ptr, &llargs, - destination.as_ref().map(|&(_, target)| (ret_dest, sig.output(), target)), + destination.as_ref().map(|&(_, target)| (ret_dest, target)), cleanup); } mir::TerminatorKind::GeneratorDrop | - mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"), + mir::TerminatorKind::Yield { .. } | + mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"), } } @@ -587,79 +611,73 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx: &Builder<'a, 'tcx>, op: OperandRef<'tcx>, llargs: &mut Vec, - fn_ty: &FnType<'tcx>, - next_idx: &mut usize, - llfn: &mut Option, - def: &Option>) { - if let Pair(a, b) = op.val { - // Treat the values in a fat pointer separately. - if common::type_is_fat_ptr(bcx.ccx, op.ty) { - let (ptr, meta) = (a, b); - if *next_idx == 0 { - if let Some(ty::InstanceDef::Virtual(_, idx)) = *def { - let llmeth = meth::VirtualIndex::from_index(idx).get_fn(bcx, meta); - let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); - *llfn = Some(bcx.pointercast(llmeth, llty)); - } - } - - let imm_op = |x| OperandRef { - val: Immediate(x), - // We won't be checking the type again. - ty: bcx.tcx().types.err - }; - self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, llfn, def); - self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, llfn, def); - return; - } - } - - let arg = &fn_ty.args[*next_idx]; - *next_idx += 1; - + arg: &ArgType<'tcx>) { // Fill padding with undef value, where applicable. if let Some(ty) = arg.pad { - llargs.push(C_undef(ty)); + llargs.push(C_undef(ty.llvm_type(bcx.ccx))); } if arg.is_ignore() { return; } + if let PassMode::Pair(..) = arg.mode { + match op.val { + Pair(a, b) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("trans_argument: {:?} invalid for pair arugment", op) + } + } + // Force by-ref if we have to load through a cast pointer. let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => { - if arg.is_indirect() || arg.cast.is_some() { - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); - self.store_operand(bcx, llscratch, None, op); - (llscratch, Alignment::AbiAligned, true) - } else { - (op.pack_if_pair(bcx).immediate(), Alignment::AbiAligned, false) + match arg.mode { + PassMode::Indirect(_) | PassMode::Cast(_) => { + let scratch = PlaceRef::alloca(bcx, arg.layout, "arg"); + op.val.store(bcx, scratch); + (scratch.llval, Alignment::AbiAligned, true) + } + _ => { + (op.immediate_or_packed_pair(bcx), Alignment::AbiAligned, false) + } } } - Ref(llval, Alignment::Packed) if arg.is_indirect() => { + Ref(llval, align @ Alignment::Packed(_)) if arg.is_indirect() => { // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't // have scary latent bugs around. - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); - base::memcpy_ty(bcx, llscratch, llval, op.ty, Some(1)); - (llscratch, Alignment::AbiAligned, true) + let scratch = PlaceRef::alloca(bcx, arg.layout, "arg"); + base::memcpy_ty(bcx, scratch.llval, llval, op.layout, align.non_abi()); + (scratch.llval, Alignment::AbiAligned, true) } Ref(llval, align) => (llval, align, true) }; if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if arg.layout.ty == bcx.tcx().types.bool { - // We store bools as i8 so we need to truncate to i1. - llval = bcx.load_range_assert(llval, 0, 2, llvm::False, None); - llval = bcx.trunc(llval, Type::i1(bcx.ccx)); - } else if let Some(ty) = arg.cast { - llval = bcx.load(bcx.pointercast(llval, ty.ptr_to()), - align.min_with(arg.layout.align(bcx.ccx).abi() as u32)); + if let PassMode::Cast(ty) = arg.mode { + llval = bcx.load(bcx.pointercast(llval, ty.llvm_type(bcx.ccx).ptr_to()), + (align | Alignment::Packed(arg.layout.align)) + .non_abi()); } else { - llval = bcx.load(llval, align.to_align()); + // We can't use `PlaceRef::load` here because the argument + // may have a type we don't treat as immediate, but the ABI + // used for this call is passing it by-value. In that case, + // the load would just produce `OperandValue::Ref` instead + // of the `OperandValue::Immediate` we need for the call. + llval = bcx.load(llval, align.non_abi()); + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if scalar.is_bool() { + bcx.range_metadata(llval, 0..2); + } + } + // We store bools as i8 so we need to truncate to i1. + llval = base::to_immediate(bcx, llval, arg.layout); } } @@ -670,89 +688,36 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx: &Builder<'a, 'tcx>, operand: &mir::Operand<'tcx>, llargs: &mut Vec, - fn_ty: &FnType<'tcx>, - next_idx: &mut usize, - llfn: &mut Option, - def: &Option>) { + args: &[ArgType<'tcx>]) { let tuple = self.trans_operand(bcx, operand); - let arg_types = match tuple.ty.sty { - ty::TyTuple(ref tys, _) => tys, - _ => span_bug!(self.mir.span, - "bad final argument to \"rust-call\" fn {:?}", tuple.ty) - }; - // Handle both by-ref and immediate tuples. - match tuple.val { - Ref(llval, align) => { - for (n, &ty) in arg_types.iter().enumerate() { - let ptr = LvalueRef::new_sized_ty(llval, tuple.ty, align); - let (ptr, align) = 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, align, ty); - Pair(lldata, llextra) - } else { - // trans_argument will load this if it needs to - Ref(ptr, align) - }; - let op = OperandRef { - val, - ty, - }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); - } - + if let Ref(llval, align) = tuple.val { + let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align); + for i in 0..tuple.layout.fields.count() { + let field_ptr = tuple_ptr.project_field(bcx, i); + self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[i]); } - Immediate(llval) => { - let l = bcx.ccx.layout_of(tuple.ty); - let v = if let layout::Univariant { ref variant, .. } = *l { - variant - } else { - bug!("Not a tuple."); - }; - for (n, &ty) in arg_types.iter().enumerate() { - let mut elem = bcx.extract_value( - llval, adt::struct_llfields_index(v, n)); - // Truncate bools to i1, if needed - if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { - elem = bcx.trunc(elem, Type::i1(bcx.ccx)); - } - // If the tuple is immediate, the elements are as well - let op = OperandRef { - val: Immediate(elem), - ty, - }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); - } - } - Pair(a, b) => { - let elems = [a, b]; - for (n, &ty) in arg_types.iter().enumerate() { - let mut elem = elems[n]; - // Truncate bools to i1, if needed - if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { - elem = bcx.trunc(elem, Type::i1(bcx.ccx)); - } - // Pair is always made up of immediates - let op = OperandRef { - val: Immediate(elem), - ty, - }; - self.trans_argument(bcx, op, llargs, fn_ty, next_idx, llfn, def); - } + } else { + // If the tuple is immediate, the elements are as well. + for i in 0..tuple.layout.fields.count() { + let op = tuple.extract_field(bcx, i); + self.trans_argument(bcx, op, llargs, &args[i]); } } - } - fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> ValueRef { + fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> { let ccx = bcx.ccx; - if let Some(slot) = self.llpersonalityslot { + if let Some(slot) = self.personality_slot { slot } else { - let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let slot = bcx.alloca(llretty, "personalityslot", None); - self.llpersonalityslot = Some(slot); + let layout = ccx.layout_of(ccx.tcx().intern_tup(&[ + ccx.tcx().mk_mut_ptr(ccx.tcx().types.u8), + ccx.tcx().types.i32 + ], false)); + let slot = PlaceRef::alloca(bcx, layout, "personalityslot"); + self.personality_slot = Some(slot); slot } } @@ -778,18 +743,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let bcx = self.new_block("cleanup"); - 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.llfn); - bcx.set_cleanup(llretval); + let llretty = self.landing_pad_type(); + let lp = bcx.landing_pad(llretty, llpersonality, 1, self.llfn); + bcx.set_cleanup(lp); + let slot = self.get_personality_slot(&bcx); - Lifetime::Start.call(&bcx, slot); - bcx.store(llretval, slot, None); + slot.storage_live(&bcx); + Pair(bcx.extract_value(lp, 0), bcx.extract_value(lp, 1)).store(&bcx, slot); + bcx.br(target_bb); bcx.llbb() } + fn landing_pad_type(&self) -> Type { + let ccx = self.ccx; + Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false) + } + fn unreachable_block(&mut self) -> BasicBlockRef { self.unreachable_block.unwrap_or_else(|| { let bl = self.new_block("unreachable"); @@ -810,49 +781,51 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } 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 { + dest: &mir::Place<'tcx>, fn_ret: &ArgType<'tcx>, + llargs: &mut Vec, is_intrinsic: bool) + -> ReturnDest<'tcx> { // If the return is ignored, we can just return a do-nothing ReturnDest - if fn_ret_ty.is_ignore() { + if fn_ret.is_ignore() { return ReturnDest::Nothing; } - let dest = if let mir::Lvalue::Local(index) = *dest { - let ret_ty = self.monomorphized_lvalue_ty(dest); + let dest = if let mir::Place::Local(index) = *dest { match self.locals[index] { - LocalRef::Lvalue(dest) => dest, + LocalRef::Place(dest) => dest, LocalRef::Operand(None) => { - // Handle temporary lvalues, specifically Operand ones, as + // Handle temporary places, specifically Operand ones, as // they don't have allocas - return if fn_ret_ty.is_indirect() { + return if fn_ret.is_indirect() { // Odd, but possible, case, we have an operand temporary, // but the calling convention has an indirect return. - let tmp = LvalueRef::alloca(bcx, ret_ty, "tmp_ret"); + let tmp = PlaceRef::alloca(bcx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bcx); llargs.push(tmp.llval); - ReturnDest::IndirectOperand(tmp.llval, index) + 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 = LvalueRef::alloca(bcx, ret_ty, "tmp_ret"); - ReturnDest::IndirectOperand(tmp.llval, index) + let tmp = PlaceRef::alloca(bcx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bcx); + ReturnDest::IndirectOperand(tmp, index) } else { ReturnDest::DirectOperand(index) }; } LocalRef::Operand(Some(_)) => { - bug!("lvalue local already assigned to"); + bug!("place local already assigned to"); } } } else { - self.trans_lvalue(bcx, dest) + self.trans_place(bcx, dest) }; - if fn_ret_ty.is_indirect() { + if fn_ret.is_indirect() { match dest.alignment { Alignment::AbiAligned => { llargs.push(dest.llval); ReturnDest::Nothing }, - Alignment::Packed => { + Alignment::Packed(_) => { // Currently, MIR code generation does not create calls // that store directly to fields of packed structs (in // fact, the calls it creates write only to temps), @@ -863,72 +836,76 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } } else { - ReturnDest::Store(dest.llval) + ReturnDest::Store(dest) } } fn trans_transmute(&mut self, bcx: &Builder<'a, 'tcx>, src: &mir::Operand<'tcx>, - dst: &mir::Lvalue<'tcx>) { - if let mir::Lvalue::Local(index) = *dst { + dst: &mir::Place<'tcx>) { + if let mir::Place::Local(index) = *dst { match self.locals[index] { - LocalRef::Lvalue(lvalue) => self.trans_transmute_into(bcx, src, &lvalue), + LocalRef::Place(place) => self.trans_transmute_into(bcx, src, place), LocalRef::Operand(None) => { - let lvalue_ty = self.monomorphized_lvalue_ty(dst); - assert!(!lvalue_ty.has_erasable_regions()); - let lvalue = LvalueRef::alloca(bcx, lvalue_ty, "transmute_temp"); - self.trans_transmute_into(bcx, src, &lvalue); - let op = self.trans_load(bcx, lvalue.llval, lvalue.alignment, lvalue_ty); + let dst_layout = bcx.ccx.layout_of(self.monomorphized_place_ty(dst)); + assert!(!dst_layout.ty.has_erasable_regions()); + let place = PlaceRef::alloca(bcx, dst_layout, "transmute_temp"); + place.storage_live(bcx); + self.trans_transmute_into(bcx, src, place); + let op = place.load(bcx); + place.storage_dead(bcx); self.locals[index] = LocalRef::Operand(Some(op)); } - LocalRef::Operand(Some(_)) => { - let ty = self.monomorphized_lvalue_ty(dst); - assert!(common::type_is_zero_size(bcx.ccx, ty), + LocalRef::Operand(Some(op)) => { + assert!(op.layout.is_zst(), "assigning to initialized SSAtemp"); } } } else { - let dst = self.trans_lvalue(bcx, dst); - self.trans_transmute_into(bcx, src, &dst); + let dst = self.trans_place(bcx, dst); + self.trans_transmute_into(bcx, src, dst); } } fn trans_transmute_into(&mut self, bcx: &Builder<'a, 'tcx>, src: &mir::Operand<'tcx>, - dst: &LvalueRef<'tcx>) { - let val = self.trans_operand(bcx, src); - let llty = type_of::type_of(bcx.ccx, val.ty); + dst: PlaceRef<'tcx>) { + let src = self.trans_operand(bcx, src); + let llty = src.layout.llvm_type(bcx.ccx); let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to()); - let in_type = val.ty; - let out_type = dst.ty.to_ty(bcx.tcx()); - let llalign = cmp::min(bcx.ccx.align_of(in_type), bcx.ccx.align_of(out_type)); - self.store_operand(bcx, cast_ptr, Some(llalign), val); + let align = src.layout.align.min(dst.layout.align); + src.val.store(bcx, + PlaceRef::new_sized(cast_ptr, src.layout, Alignment::Packed(align))); } // Stores the return value of a function call into it's final location. fn store_return(&mut self, bcx: &Builder<'a, 'tcx>, - dest: ReturnDest, + dest: ReturnDest<'tcx>, ret_ty: &ArgType<'tcx>, - op: OperandRef<'tcx>) { + llval: ValueRef) { use self::ReturnDest::*; match dest { Nothing => (), - Store(dst) => ret_ty.store(bcx, op.immediate(), dst), + Store(dst) => ret_ty.store(bcx, llval, dst), IndirectOperand(tmp, index) => { - let op = self.trans_load(bcx, tmp, Alignment::AbiAligned, op.ty); + let op = tmp.load(bcx); + tmp.storage_dead(bcx); self.locals[index] = LocalRef::Operand(Some(op)); } DirectOperand(index) => { // If there is a cast, we have to store and reload. - let op = if ret_ty.cast.is_some() { - let tmp = LvalueRef::alloca(bcx, op.ty, "tmp_ret"); - ret_ty.store(bcx, op.immediate(), tmp.llval); - self.trans_load(bcx, tmp.llval, tmp.alignment, op.ty) + let op = if let PassMode::Cast(_) = ret_ty.mode { + let tmp = PlaceRef::alloca(bcx, ret_ty.layout, "tmp_ret"); + tmp.storage_live(bcx); + ret_ty.store(bcx, llval, tmp); + let op = tmp.load(bcx); + tmp.storage_dead(bcx); + op } else { - op.unpack_if_pair(bcx) + OperandRef::from_immediate_or_packed_pair(bcx, llval, ret_ty.layout) }; self.locals[index] = LocalRef::Operand(Some(op)); } @@ -936,13 +913,13 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } -enum ReturnDest { +enum ReturnDest<'tcx> { // Do nothing, the return value is indirect or ignored Nothing, // Store the return value to the pointer - Store(ValueRef), - // Stores an indirect return value to an operand local lvalue - IndirectOperand(ValueRef, mir::Local), - // Stores a direct return value to an operand local lvalue + Store(PlaceRef<'tcx>), + // Stores an indirect return value to an operand local place + IndirectOperand(PlaceRef<'tcx>, mir::Local), + // Stores a direct return value to an operand local place DirectOperand(mir::Local) } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 9232d73f832e7..764021983e99c 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -11,27 +11,28 @@ use llvm::{self, ValueRef}; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; use rustc_const_math::ConstInt::*; -use rustc_const_math::{ConstInt, ConstMathErr}; +use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP}; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; +use rustc::traits; use rustc::mir; -use rustc::mir::tcx::LvalueTy; +use rustc::mir::tcx::PlaceTy; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::layout::{self, LayoutTyper}; +use rustc::ty::layout::{self, LayoutOf, Size}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::{Kind, Substs, Subst}; +use rustc_apfloat::{ieee, Float, Status}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use {adt, base, machine}; +use base; use abi::{self, Abi}; use callee; use builder::Builder; use common::{self, CrateContext, const_get_elt, val_ty}; -use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_big_integral, C_u32, C_u64}; -use common::{C_null, C_struct, C_str_slice, C_undef, C_usize, C_vector, is_undef}; +use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_uint_big, C_u32, C_u64}; +use common::{C_null, C_struct, C_str_slice, C_undef, C_usize, C_vector, C_fat_ptr}; use common::const_to_opt_u128; use consts; -use monomorphize; -use type_of; +use type_of::LayoutLlvmExt; use type_::Type; use value::Value; @@ -41,7 +42,7 @@ use syntax::ast; use std::fmt; use std::ptr; -use super::lvalue::Alignment; +use super::place::Alignment; use super::operand::{OperandRef, OperandValue}; use super::MirContext; @@ -54,7 +55,7 @@ pub struct Const<'tcx> { pub ty: Ty<'tcx> } -impl<'tcx> Const<'tcx> { +impl<'a, 'tcx> Const<'tcx> { pub fn new(llval: ValueRef, ty: Ty<'tcx>) -> Const<'tcx> { Const { llval, @@ -62,32 +63,31 @@ impl<'tcx> Const<'tcx> { } } - pub fn from_constint<'a>(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt) - -> Const<'tcx> { + pub fn from_constint(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt) -> Const<'tcx> { let tcx = ccx.tcx(); let (llval, ty) = match *ci { I8(v) => (C_int(Type::i8(ccx), v as i64), tcx.types.i8), I16(v) => (C_int(Type::i16(ccx), v as i64), tcx.types.i16), I32(v) => (C_int(Type::i32(ccx), v as i64), tcx.types.i32), I64(v) => (C_int(Type::i64(ccx), v as i64), tcx.types.i64), - I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128), + I128(v) => (C_uint_big(Type::i128(ccx), v as u128), tcx.types.i128), Isize(v) => (C_int(Type::isize(ccx), v.as_i64()), tcx.types.isize), U8(v) => (C_uint(Type::i8(ccx), v as u64), tcx.types.u8), U16(v) => (C_uint(Type::i16(ccx), v as u64), tcx.types.u16), U32(v) => (C_uint(Type::i32(ccx), v as u64), tcx.types.u32), U64(v) => (C_uint(Type::i64(ccx), v), tcx.types.u64), - U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128), + U128(v) => (C_uint_big(Type::i128(ccx), v), tcx.types.u128), Usize(v) => (C_uint(Type::isize(ccx), v.as_u64()), tcx.types.usize), }; Const { llval: llval, ty: ty } } /// Translate ConstVal into a LLVM constant value. - pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>, - cv: &ConstVal, - ty: Ty<'tcx>) - -> Const<'tcx> { - let llty = type_of::type_of(ccx, ty); + pub fn from_constval(ccx: &CrateContext<'a, 'tcx>, + cv: &ConstVal, + ty: Ty<'tcx>) + -> Const<'tcx> { + let llty = ccx.layout_of(ty).llvm_type(ccx); let val = match *cv { ConstVal::Float(v) => { let bits = match v.ty { @@ -99,9 +99,11 @@ impl<'tcx> Const<'tcx> { ConstVal::Bool(v) => C_bool(ccx, v), ConstVal::Integral(ref i) => return Const::from_constint(ccx, i), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), - ConstVal::ByteStr(v) => consts::addr_of(ccx, C_bytes(ccx, v.data), 1, "byte_str"), + ConstVal::ByteStr(v) => { + consts::addr_of(ccx, C_bytes(ccx, v.data), ccx.align_of(ty), "byte_str") + } ConstVal::Char(c) => C_uint(Type::char(ccx), c as u64), - ConstVal::Function(..) => C_null(type_of::type_of(ccx, ty)), + ConstVal::Function(..) => C_undef(llty), ConstVal::Variant(_) | ConstVal::Aggregate(..) | ConstVal::Unevaluated(..) => { @@ -114,33 +116,64 @@ impl<'tcx> Const<'tcx> { Const::new(val, ty) } - fn get_pair(&self) -> (ValueRef, ValueRef) { - (const_get_elt(self.llval, &[0]), - const_get_elt(self.llval, &[1])) + fn get_field(&self, ccx: &CrateContext<'a, 'tcx>, i: usize) -> ValueRef { + let layout = ccx.layout_of(self.ty); + let field = layout.field(ccx, i); + if field.is_zst() { + return C_undef(field.immediate_llvm_type(ccx)); + } + match layout.abi { + layout::Abi::Scalar(_) => self.llval, + layout::Abi::ScalarPair(ref a, ref b) => { + let offset = layout.fields.offset(i); + if offset.bytes() == 0 { + if field.size == layout.size { + self.llval + } else { + assert_eq!(field.size, a.value.size(ccx)); + const_get_elt(self.llval, 0) + } + } else { + assert_eq!(offset, a.value.size(ccx) + .abi_align(b.value.align(ccx))); + assert_eq!(field.size, b.value.size(ccx)); + const_get_elt(self.llval, 1) + } + } + _ => { + const_get_elt(self.llval, layout.llvm_field_index(i)) + } + } } - fn get_fat_ptr(&self) -> (ValueRef, ValueRef) { + fn get_pair(&self, ccx: &CrateContext<'a, 'tcx>) -> (ValueRef, ValueRef) { + (self.get_field(ccx, 0), self.get_field(ccx, 1)) + } + + fn get_fat_ptr(&self, ccx: &CrateContext<'a, 'tcx>) -> (ValueRef, ValueRef) { assert_eq!(abi::FAT_PTR_ADDR, 0); assert_eq!(abi::FAT_PTR_EXTRA, 1); - self.get_pair() + self.get_pair(ccx) } - fn as_lvalue(&self) -> ConstLvalue<'tcx> { - ConstLvalue { + fn as_place(&self) -> ConstPlace<'tcx> { + ConstPlace { base: Base::Value(self.llval), llextra: ptr::null_mut(), ty: self.ty } } - pub fn to_operand<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> OperandRef<'tcx> { - let llty = type_of::immediate_type_of(ccx, self.ty); + pub fn to_operand(&self, ccx: &CrateContext<'a, 'tcx>) -> OperandRef<'tcx> { + let layout = ccx.layout_of(self.ty); + let llty = layout.immediate_llvm_type(ccx); let llvalty = val_ty(self.llval); - let val = if llty == llvalty && common::type_is_imm_pair(ccx, self.ty) { - let (a, b) = self.get_pair(); - OperandValue::Pair(a, b) - } else if llty == llvalty && common::type_is_immediate(ccx, self.ty) { + let val = if llty == llvalty && layout.is_llvm_scalar_pair() { + OperandValue::Pair( + const_get_elt(self.llval, 0), + const_get_elt(self.llval, 1)) + } else if llty == llvalty && layout.is_llvm_immediate() { // If the types match, we can use the value directly. OperandValue::Immediate(self.llval) } else { @@ -148,12 +181,13 @@ impl<'tcx> Const<'tcx> { // a constant LLVM global and cast its address if necessary. let align = ccx.align_of(self.ty); let ptr = consts::addr_of(ccx, self.llval, align, "const"); - OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()), Alignment::AbiAligned) + OperandValue::Ref(consts::ptrcast(ptr, layout.llvm_type(ccx).ptr_to()), + Alignment::AbiAligned) }; OperandRef { val, - ty: self.ty + layout: ccx.layout_of(self.ty) } } } @@ -176,15 +210,15 @@ enum Base { Static(ValueRef) } -/// An lvalue as seen from a constant. +/// An place as seen from a constant. #[derive(Copy, Clone)] -struct ConstLvalue<'tcx> { +struct ConstPlace<'tcx> { base: Base, llextra: ValueRef, ty: Ty<'tcx> } -impl<'tcx> ConstLvalue<'tcx> { +impl<'tcx> ConstPlace<'tcx> { fn to_const(&self, span: Span) -> Const<'tcx> { match self.base { Base::Value(val) => Const::new(val, self.ty), @@ -208,7 +242,7 @@ impl<'tcx> ConstLvalue<'tcx> { assert!(self.llextra != ptr::null_mut()); self.llextra } - _ => bug!("unexpected type `{}` in ConstLvalue::len", self.ty) + _ => bug!("unexpected type `{}` in ConstPlace::len", self.ty) } } } @@ -261,7 +295,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: IndexVec, ConstEvalErr<'tcx>>>) -> Result, ConstEvalErr<'tcx>> { - let instance = monomorphize::resolve(ccx.tcx(), def_id, substs); + let instance = ty::Instance::resolve(ccx.tcx(), + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); let mir = ccx.tcx().instance_mir(instance.def); MirConstContext::new(ccx, &mir, instance.substs, args).trans() } @@ -311,7 +348,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::TerminatorKind::Goto { target } => target, mir::TerminatorKind::Return => { failure?; - return self.locals[mir::RETURN_POINTER].clone().unwrap_or_else(|| { + return self.locals[mir::RETURN_PLACE].clone().unwrap_or_else(|| { span_bug!(span, "no returned value in constant"); }); } @@ -364,12 +401,12 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { match &tcx.item_name(def_id)[..] { "size_of" => { let llval = C_usize(self.ccx, - self.ccx.size_of(substs.type_at(0))); + self.ccx.size_of(substs.type_at(0)).bytes()); Ok(Const::new(llval, tcx.types.usize)) } "min_align_of" => { let llval = C_usize(self.ccx, - self.ccx.align_of(substs.type_at(0)) as u64); + self.ccx.align_of(substs.type_at(0)).abi()); Ok(Const::new(llval, tcx.types.usize)) } _ => span_bug!(span, "{:?} in constant", terminator.kind) @@ -390,49 +427,49 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn store(&mut self, - dest: &mir::Lvalue<'tcx>, + dest: &mir::Place<'tcx>, value: Result, ConstEvalErr<'tcx>>, span: Span) { - if let mir::Lvalue::Local(index) = *dest { + if let mir::Place::Local(index) = *dest { self.locals[index] = Some(value); } else { span_bug!(span, "assignment to {:?} in constant", dest); } } - fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span) - -> Result, ConstEvalErr<'tcx>> { + fn const_place(&self, place: &mir::Place<'tcx>, span: Span) + -> Result, ConstEvalErr<'tcx>> { let tcx = self.ccx.tcx(); - if let mir::Lvalue::Local(index) = *lvalue { + if let mir::Place::Local(index) = *place { return self.locals[index].clone().unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", lvalue) - }).map(|v| v.as_lvalue()); + span_bug!(span, "{:?} not initialized", place) + }).map(|v| v.as_place()); } - let lvalue = match *lvalue { - mir::Lvalue::Local(_) => bug!(), // handled above - mir::Lvalue::Static(box mir::Static { def_id, ty }) => { - ConstLvalue { + let place = match *place { + mir::Place::Local(_) => bug!(), // handled above + mir::Place::Static(box mir::Static { def_id, ty }) => { + ConstPlace { base: Base::Static(consts::get_static(self.ccx, def_id)), llextra: ptr::null_mut(), ty: self.monomorphize(&ty), } } - mir::Lvalue::Projection(ref projection) => { - let tr_base = self.const_lvalue(&projection.base, span)?; - let projected_ty = LvalueTy::Ty { ty: tr_base.ty } + mir::Place::Projection(ref projection) => { + let tr_base = self.const_place(&projection.base, span)?; + let projected_ty = PlaceTy::Ty { ty: tr_base.ty } .projection_ty(tcx, &projection.elem); let base = tr_base.to_const(span); let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx); - let is_sized = self.ccx.shared().type_is_sized(projected_ty); + let has_metadata = self.ccx.shared().type_has_metadata(projected_ty); let (projected, llextra) = match projection.elem { mir::ProjectionElem::Deref => { - let (base, extra) = if is_sized { + let (base, extra) = if !has_metadata { (base.llval, ptr::null_mut()) } else { - base.get_fat_ptr() + base.get_fat_ptr(self.ccx) }; if self.ccx.statics().borrow().contains_key(&base) { (Base::Static(base), extra) @@ -446,9 +483,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { span_bug!(span, "dereference of non-constant pointer `{:?}`", Value(base)); } - if projected_ty.is_bool() { + let layout = self.ccx.layout_of(projected_ty); + if let layout::Abi::Scalar(ref scalar) = layout.abi { let i1_type = Type::i1(self.ccx); - if val_ty(val) != i1_type { + if scalar.is_bool() && val_ty(val) != i1_type { unsafe { val = llvm::LLVMConstTrunc(val, i1_type.to_ref()); } @@ -458,9 +496,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } mir::ProjectionElem::Field(ref field, _) => { - let llprojected = adt::const_get_field(self.ccx, tr_base.ty, base.llval, - field.index()); - let llextra = if is_sized { + let llprojected = base.get_field(self.ccx, field.index()); + let llextra = if !has_metadata { ptr::null_mut() } else { tr_base.llextra @@ -468,7 +505,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { (Base::Value(llprojected), llextra) } mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Consume(mir::Lvalue::Local(index)); + let index = &mir::Operand::Copy(mir::Place::Local(index)); let llindex = self.const_operand(index, span)?.llval; let iv = if let Some(iv) = common::const_to_opt_u128(llindex, false) { @@ -480,31 +517,32 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { // Produce an undef instead of a LLVM assertion on OOB. let len = common::const_to_uint(tr_base.len(self.ccx)); let llelem = if iv < len as u128 { - const_get_elt(base.llval, &[iv as u32]) + const_get_elt(base.llval, iv as u64) } else { - C_undef(type_of::type_of(self.ccx, projected_ty)) + C_undef(self.ccx.layout_of(projected_ty).llvm_type(self.ccx)) }; (Base::Value(llelem), ptr::null_mut()) } _ => span_bug!(span, "{:?} in constant", projection.elem) }; - ConstLvalue { + ConstPlace { base: projected, llextra, ty: projected_ty } } }; - Ok(lvalue) + Ok(place) } fn const_operand(&self, operand: &mir::Operand<'tcx>, span: Span) -> Result, ConstEvalErr<'tcx>> { debug!("const_operand({:?} @ {:?})", operand, span); let result = match *operand { - mir::Operand::Consume(ref lvalue) => { - Ok(self.const_lvalue(lvalue, span)?.to_const(span)) + mir::Operand::Copy(ref place) | + mir::Operand::Move(ref place) => { + Ok(self.const_place(place, span)?.to_const(span)) } mir::Operand::Constant(ref constant) => { @@ -536,7 +574,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let elem_ty = array_ty.builtin_index().unwrap_or_else(|| { bug!("bad array type {:?}", array_ty) }); - let llunitty = type_of::type_of(self.ccx, elem_ty); + let llunitty = self.ccx.layout_of(elem_ty).llvm_type(self.ccx); // If the array contains enums, an LLVM array won't work. let val = if fields.iter().all(|&f| val_ty(f) == llunitty) { C_array(llunitty, fields) @@ -562,7 +600,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { self.const_array(dest_ty, &fields) } - mir::Rvalue::Aggregate(ref kind, ref operands) => { + mir::Rvalue::Aggregate(box mir::AggregateKind::Array(_), ref operands) => { // Make sure to evaluate all operands to // report as many errors as we possibly can. let mut fields = Vec::with_capacity(operands.len()); @@ -575,17 +613,23 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } failure?; - match **kind { - mir::AggregateKind::Array(_) => { - self.const_array(dest_ty, &fields) - } - mir::AggregateKind::Adt(..) | - mir::AggregateKind::Closure(..) | - mir::AggregateKind::Generator(..) | - mir::AggregateKind::Tuple => { - Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty) + self.const_array(dest_ty, &fields) + } + + mir::Rvalue::Aggregate(ref kind, ref operands) => { + // Make sure to evaluate all operands to + // report as many errors as we possibly can. + let mut fields = Vec::with_capacity(operands.len()); + let mut failure = Ok(()); + for operand in operands { + match self.const_operand(operand, span) { + Ok(val) => fields.push(val), + Err(err) => if failure.is_ok() { failure = Err(err); } } } + failure?; + + trans_const_adt(self.ccx, dest_ty, kind, &fields) } mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { @@ -631,10 +675,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { operand.llval } mir::CastKind::Unsize => { - // unsize targets other than to a fat pointer currently - // can't be in constants. - assert!(common::type_is_fat_ptr(self.ccx, cast_ty)); - let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference) .expect("consts: unsizing got non-pointer type").ty; let (base, old_info) = if !self.ccx.shared().type_is_sized(pointee_ty) { @@ -644,7 +684,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { // to use a different vtable. In that case, we want to // load out the original data pointer so we can repackage // it. - let (base, extra) = operand.get_fat_ptr(); + let (base, extra) = operand.get_fat_ptr(self.ccx); (base, Some(extra)) } else { (operand.llval, None) @@ -652,7 +692,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let unsized_ty = cast_ty.builtin_deref(true, ty::NoPreference) .expect("consts: unsizing got non-pointer target type").ty; - let ptr_ty = type_of::in_memory_type_of(self.ccx, unsized_ty).ptr_to(); + let ptr_ty = self.ccx.layout_of(unsized_ty).llvm_type(self.ccx).ptr_to(); let base = consts::ptrcast(base, ptr_ty); let info = base::unsized_info(self.ccx, pointee_ty, unsized_ty, old_info); @@ -662,22 +702,23 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { .insert(base, operand.llval); assert!(prev_const.is_none() || prev_const == Some(operand.llval)); } - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - C_struct(self.ccx, &[base, info], false) + C_fat_ptr(self.ccx, base, info) } - mir::CastKind::Misc if common::type_is_immediate(self.ccx, operand.ty) => { - debug_assert!(common::type_is_immediate(self.ccx, cast_ty)); + mir::CastKind::Misc if self.ccx.layout_of(operand.ty).is_llvm_immediate() => { let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - let ll_t_out = type_of::immediate_type_of(self.ccx, cast_ty); + let cast_layout = self.ccx.layout_of(cast_ty); + assert!(cast_layout.is_llvm_immediate()); + let ll_t_out = cast_layout.immediate_llvm_type(self.ccx); let llval = operand.llval; - let signed = if let CastTy::Int(IntTy::CEnum) = r_t_in { - let l = self.ccx.layout_of(operand.ty); - adt::is_discr_signed(&l) - } else { - operand.ty.is_signed() - }; + + let mut signed = false; + let l = self.ccx.layout_of(operand.ty); + if let layout::Abi::Scalar(ref scalar) = l.abi { + if let layout::Int(_, true) = scalar.value { + signed = true; + } + } unsafe { match (r_t_in, r_t_out) { @@ -686,20 +727,18 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { llvm::LLVMConstIntCast(llval, ll_t_out.to_ref(), s) } (CastTy::Int(_), CastTy::Float) => { - if signed { - llvm::LLVMConstSIToFP(llval, ll_t_out.to_ref()) - } else { - llvm::LLVMConstUIToFP(llval, ll_t_out.to_ref()) - } + cast_const_int_to_float(self.ccx, llval, signed, ll_t_out) } (CastTy::Float, CastTy::Float) => { llvm::LLVMConstFPCast(llval, ll_t_out.to_ref()) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - llvm::LLVMConstFPToSI(llval, ll_t_out.to_ref()) + cast_const_float_to_int(self.ccx, &operand, + true, ll_t_out, span) } (CastTy::Float, CastTy::Int(_)) => { - llvm::LLVMConstFPToUI(llval, ll_t_out.to_ref()) + cast_const_float_to_int(self.ccx, &operand, + false, ll_t_out, span) } (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_)) | @@ -718,20 +757,19 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } mir::CastKind::Misc => { // Casts from a fat-ptr. - let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty); - let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty); - if common::type_is_fat_ptr(self.ccx, operand.ty) { - let (data_ptr, meta_ptr) = operand.get_fat_ptr(); - if common::type_is_fat_ptr(self.ccx, cast_ty) { - let ll_cft = ll_cast_ty.field_types(); - let ll_fft = ll_from_ty.field_types(); - let data_cast = consts::ptrcast(data_ptr, ll_cft[0]); - assert_eq!(ll_cft[1].kind(), ll_fft[1].kind()); - C_struct(self.ccx, &[data_cast, meta_ptr], false) + let l = self.ccx.layout_of(operand.ty); + let cast = self.ccx.layout_of(cast_ty); + if l.is_llvm_scalar_pair() { + let (data_ptr, meta) = operand.get_fat_ptr(self.ccx); + if cast.is_llvm_scalar_pair() { + let data_cast = consts::ptrcast(data_ptr, + cast.scalar_pair_element_llvm_type(self.ccx, 0)); + C_fat_ptr(self.ccx, data_cast, meta) } else { // cast to thin-ptr // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and // pointer-cast of that pointer to desired pointer type. - consts::ptrcast(data_ptr, ll_cast_ty) + let llcast_ty = cast.immediate_llvm_type(self.ccx); + consts::ptrcast(data_ptr, llcast_ty) } } else { bug!("Unexpected non-fat-pointer operand") @@ -741,20 +779,20 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { Const::new(val, cast_ty) } - mir::Rvalue::Ref(_, bk, ref lvalue) => { - let tr_lvalue = self.const_lvalue(lvalue, span)?; + mir::Rvalue::Ref(_, bk, ref place) => { + let tr_place = self.const_place(place, span)?; - let ty = tr_lvalue.ty; + let ty = tr_place.ty; let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }); - let base = match tr_lvalue.base { + let base = match tr_place.base { Base::Value(llval) => { // FIXME: may be wrong for &*(&simd_vec as &fmt::Debug) let align = if self.ccx.shared().type_is_sized(ty) { self.ccx.align_of(ty) } else { - self.ccx.tcx().data_layout.pointer_align.abi() as machine::llalign + self.ccx.tcx().data_layout.pointer_align }; if bk == mir::BorrowKind::Mut { consts::addr_of_mut(self.ccx, llval, align, "ref_mut") @@ -769,14 +807,14 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let ptr = if self.ccx.shared().type_is_sized(ty) { base } else { - C_struct(self.ccx, &[base, tr_lvalue.llextra], false) + C_fat_ptr(self.ccx, base, tr_place.llextra) }; Const::new(ptr, ref_ty) } - mir::Rvalue::Len(ref lvalue) => { - let tr_lvalue = self.const_lvalue(lvalue, span)?; - Const::new(tr_lvalue.len(self.ccx), tcx.types.usize) + mir::Rvalue::Len(ref place) => { + let tr_place = self.const_place(place, span)?; + Const::new(tr_place.len(self.ccx), tcx.types.usize) } mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { @@ -799,8 +837,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) { Some((llval, of)) => { - let llof = C_bool(self.ccx, of); - Const::new(C_struct(self.ccx, &[llval, llof], false), binop_ty) + trans_const_adt(self.ccx, binop_ty, &mir::AggregateKind::Tuple, &[ + Const::new(llval, val_ty), + Const::new(C_bool(self.ccx, of), tcx.types.bool) + ]) } None => { span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}", @@ -834,7 +874,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { assert!(self.ccx.shared().type_is_sized(ty)); - let llval = C_usize(self.ccx, self.ccx.size_of(ty)); + let llval = C_usize(self.ccx, self.ccx.size_of(ty).bytes()); Const::new(llval, tcx.types.usize) } @@ -952,6 +992,64 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +unsafe fn cast_const_float_to_int(ccx: &CrateContext, + operand: &Const, + signed: bool, + int_ty: Type, + span: Span) -> ValueRef { + let llval = operand.llval; + let float_bits = match operand.ty.sty { + ty::TyFloat(fty) => fty.bit_width(), + _ => bug!("cast_const_float_to_int: operand not a float"), + }; + // Note: this breaks if llval is a complex constant expression rather than a simple constant. + // One way that might happen would be if addresses could be turned into integers in constant + // expressions, but that doesn't appear to be possible? + // In any case, an ICE is better than producing undef. + let llval_bits = consts::bitcast(llval, Type::ix(ccx, float_bits as u64)); + let bits = const_to_opt_u128(llval_bits, false).unwrap_or_else(|| { + panic!("could not get bits of constant float {:?}", + Value(llval)); + }); + let int_width = int_ty.int_width() as usize; + // Try to convert, but report an error for overflow and NaN. This matches HIR const eval. + let cast_result = match float_bits { + 32 if signed => ieee::Single::from_bits(bits).to_i128(int_width).map(|v| v as u128), + 64 if signed => ieee::Double::from_bits(bits).to_i128(int_width).map(|v| v as u128), + 32 => ieee::Single::from_bits(bits).to_u128(int_width), + 64 => ieee::Double::from_bits(bits).to_u128(int_width), + n => bug!("unsupported float width {}", n), + }; + if cast_result.status.contains(Status::INVALID_OP) { + let err = ConstEvalErr { span: span, kind: ErrKind::CannotCast }; + err.report(ccx.tcx(), span, "expression"); + } + C_uint_big(int_ty, cast_result.value) +} + +unsafe fn cast_const_int_to_float(ccx: &CrateContext, + llval: ValueRef, + signed: bool, + float_ty: Type) -> ValueRef { + // Note: this breaks if llval is a complex constant expression rather than a simple constant. + // One way that might happen would be if addresses could be turned into integers in constant + // expressions, but that doesn't appear to be possible? + // In any case, an ICE is better than producing undef. + let value = const_to_opt_u128(llval, signed).unwrap_or_else(|| { + panic!("could not get z128 value of constant integer {:?}", + Value(llval)); + }); + if signed { + llvm::LLVMConstSIToFP(llval, float_ty.to_ref()) + } else if float_ty.float_width() == 32 && value >= MAX_F32_PLUS_HALF_ULP { + // We're casting to f32 and the value is > f32::MAX + 0.5 ULP -> round up to infinity. + let infinity_bits = C_u32(ccx, ieee::Single::INFINITY.to_bits() as u32); + consts::bitcast(infinity_bits, float_ty) + } else { + llvm::LLVMConstUIToFP(llval, float_ty.to_ref()) + } +} + impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_constant(&mut self, bcx: &Builder<'a, 'tcx>, @@ -977,7 +1075,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let result = result.unwrap_or_else(|_| { // We've errored, so we don't have to produce working code. - let llty = type_of::type_of(bcx.ccx, ty); + let llty = bcx.ccx.layout_of(ty).llvm_type(bcx.ccx); Const::new(C_undef(llty), ty) }); @@ -1015,19 +1113,41 @@ pub fn trans_static_initializer<'a, 'tcx>( /// 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>( +fn trans_const_adt<'a, 'tcx>( ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, kind: &mir::AggregateKind, - vals: &[ValueRef] -) -> ValueRef { + vals: &[Const<'tcx>] +) -> Const<'tcx> { let l = ccx.layout_of(t); let variant_index = match *kind { mir::AggregateKind::Adt(_, index, _, _) => index, _ => 0, }; - match *l { - layout::CEnum { discr: d, min, max, .. } => { + + if let layout::Abi::Uninhabited = l.abi { + return Const::new(C_undef(l.llvm_type(ccx)), t); + } + + match l.variants { + layout::Variants::Single { index } => { + assert_eq!(variant_index, index); + if let layout::Abi::Vector = l.abi { + Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::>()), t) + } else if let layout::FieldPlacement::Union(_) = l.fields { + assert_eq!(variant_index, 0); + assert_eq!(vals.len(), 1); + let contents = [ + vals[0].llval, + padding(ccx, l.size - ccx.size_of(vals[0].ty)) + ]; + + Const::new(C_struct(ccx, &contents, l.is_packed()), t) + } else { + build_const_struct(ccx, l, vals, None) + } + } + layout::Variants::Tagged { .. } => { let discr = match *kind { mir::AggregateKind::Adt(adt_def, _, _, _) => { adt_def.discriminant_for_variant(ccx.tcx(), variant_index) @@ -1035,114 +1155,103 @@ fn trans_const<'a, 'tcx>( }, _ => 0, }; - assert_eq!(vals.len(), 0); - adt::assert_discr_in_range(min, max, discr); - C_int(Type::from_integer(ccx, d), discr as i64) - } - layout::General { discr: d, ref variants, .. } => { - let variant = &variants[variant_index]; - let lldiscr = C_int(Type::from_integer(ccx, d), variant_index as i64); - 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(ccx).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, .. } => { - if variant_index as u64 == nndiscr { - assert_eq!(vals.len(), 1); - vals[0] + let discr_field = l.field(ccx, 0); + let discr = C_int(discr_field.llvm_type(ccx), discr as i64); + if let layout::Abi::Scalar(_) = l.abi { + Const::new(discr, t) } else { - C_null(type_of::type_of(ccx, t)) + let discr = Const::new(discr, discr_field.ty); + build_const_struct(ccx, l.for_variant(ccx, variant_index), vals, Some(discr)) } } - layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { - if variant_index as u64 == nndiscr { - C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false) + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + if variant_index == dataful_variant { + build_const_struct(ccx, l.for_variant(ccx, dataful_variant), vals, None) } else { - // Always use null even if it's not the `discrfield`th - // field; see #8506. - C_null(type_of::type_of(ccx, t)) + let niche = l.field(ccx, 0); + let niche_llty = niche.llvm_type(ccx); + let niche_value = ((variant_index - niche_variants.start) as u128) + .wrapping_add(niche_start); + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_value == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_value) + }; + build_const_struct(ccx, l, &[Const::new(niche_llval, niche.ty)], None) } } - _ => 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, +/// Continuing the example from `trans_const_adt`, 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(); + layout: layout::TyLayout<'tcx>, + vals: &[Const<'tcx>], + discr: Option>) + -> Const<'tcx> { + assert_eq!(vals.len(), layout.fields.count()); + + match layout.abi { + layout::Abi::Scalar(_) | + layout::Abi::ScalarPair(..) if discr.is_none() => { + let mut non_zst_fields = vals.iter().enumerate().map(|(i, f)| { + (f, layout.fields.offset(i)) + }).filter(|&(f, _)| !ccx.layout_of(f.ty).is_zst()); + match (non_zst_fields.next(), non_zst_fields.next()) { + (Some((x, offset)), None) if offset.bytes() == 0 => { + return Const::new(x.llval, layout.ty); + } + (Some((a, a_offset)), Some((b, _))) if a_offset.bytes() == 0 => { + return Const::new(C_struct(ccx, &[a.llval, b.llval], false), layout.ty); + } + (Some((a, _)), Some((b, b_offset))) if b_offset.bytes() == 0 => { + return Const::new(C_struct(ccx, &[b.llval, a.llval], false), layout.ty); + } + _ => {} + } + } + _ => {} } // offset of current value - let mut offset = 0; + let mut offset = Size::from_bytes(0); let mut cfields = Vec::new(); - cfields.reserve(st.offsets.len()*2); + cfields.reserve(discr.is_some() as usize + 1 + layout.fields.count() * 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 let Some(discr) = discr { + cfields.push(discr.llval); + offset = ccx.size_of(discr.ty); } - if offset < st.stride().bytes() { - cfields.push(padding(ccx, st.stride().bytes() - offset)); + let parts = layout.fields.index_by_increasing_offset().map(|i| { + (vals[i], layout.fields.offset(i)) + }); + for (val, target_offset) in parts { + cfields.push(padding(ccx, target_offset - offset)); + cfields.push(val.llval); + offset = target_offset + ccx.size_of(val.ty); } - 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)); - } + // Pad to the size of the whole type, not e.g. the variant. + cfields.push(padding(ccx, ccx.size_of(layout.ty) - offset)); - cfields + Const::new(C_struct(ccx, &cfields, layout.is_packed()), layout.ty) } -fn padding(ccx: &CrateContext, size: u64) -> ValueRef { - C_undef(Type::array(&Type::i8(ccx), size)) +fn padding(ccx: &CrateContext, size: Size) -> ValueRef { + C_undef(Type::array(&Type::i8(ccx), size.bytes())) } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs deleted file mode 100644 index 6799e52904d34..0000000000000 --- a/src/librustc_trans/mir/lvalue.rs +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::ValueRef; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, LayoutTyper}; -use rustc::mir; -use rustc::mir::tcx::LvalueTy; -use rustc_data_structures::indexed_vec::Idx; -use adt; -use builder::Builder; -use common::{self, CrateContext, C_usize}; -use consts; -use machine; -use type_of; -use type_::Type; -use value::Value; -use glue; - -use std::ptr; -use std::ops; - -use super::{MirContext, LocalRef}; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Alignment { - Packed, - AbiAligned, -} - -impl ops::BitOr for Alignment { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self { - match (self, rhs) { - (Alignment::Packed, _) => Alignment::Packed, - (Alignment::AbiAligned, a) => a, - } - } -} - -impl Alignment { - pub fn from_packed(packed: bool) -> Self { - if packed { - Alignment::Packed - } else { - Alignment::AbiAligned - } - } - - pub fn to_align(self) -> Option { - match self { - Alignment::Packed => Some(1), - Alignment::AbiAligned => None, - } - } - - pub fn min_with(self, align: u32) -> Option { - match self { - Alignment::Packed => Some(1), - Alignment::AbiAligned => Some(align), - } - } -} - -#[derive(Copy, Clone, Debug)] -pub struct LvalueRef<'tcx> { - /// Pointer to the contents of the lvalue - pub llval: ValueRef, - - /// This lvalue's extra data if it is unsized, or null - pub llextra: ValueRef, - - /// Monomorphized type of this lvalue, including variant information - pub ty: LvalueTy<'tcx>, - - /// Whether this lvalue is known to be aligned according to its layout - pub alignment: Alignment, -} - -impl<'a, 'tcx> LvalueRef<'tcx> { - pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>, - alignment: Alignment) -> LvalueRef<'tcx> { - LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty, alignment: alignment } - } - - pub fn new_sized_ty(llval: ValueRef, ty: Ty<'tcx>, alignment: Alignment) -> LvalueRef<'tcx> { - LvalueRef::new_sized(llval, LvalueTy::from_ty(ty), alignment) - } - - pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { - debug!("alloca({:?}: {:?})", name, ty); - let tmp = bcx.alloca( - type_of::type_of(bcx.ccx, ty), name, bcx.ccx.over_align_of(ty)); - assert!(!ty.has_param_types()); - Self::new_sized_ty(tmp, ty, Alignment::AbiAligned) - } - - 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_usize(ccx, n.val.to_const_int().unwrap().to_u64().unwrap()) - } - ty::TySlice(_) | ty::TyStr => { - assert!(self.llextra != ptr::null_mut()); - self.llextra - } - _ => 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, Alignment) { - let fty = fields[ix]; - let ccx = bcx.ccx; - - let alignment = self.alignment | Alignment::from_packed(st.packed); - - let llfields = adt::struct_llfields(ccx, fields, st); - let ptr_val = if needs_cast { - let real_ty = Type::struct_(ccx, &llfields[..], 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, adt::struct_llfields_index(st, ix)), alignment); - } - - // 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, adt::struct_llfields_index(st, ix)), alignment); - } - _ => () - } - - // 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, adt::struct_llfields_index(st, ix)), alignment); - } - - // 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_usize(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_usize(bcx.ccx, 1)); - 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()), alignment) - } - - /// 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, Alignment) { - 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), self.alignment) - } - 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 { ref variants } => { - 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()), - self.alignment | Alignment::from_packed(variants.packed)) - } - 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()), Alignment::Packed) - } - 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()), self.alignment) - } - 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) - } - } - - pub fn project_index(&self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) -> ValueRef { - if let ty::TySlice(_) = self.ty.to_ty(bcx.tcx()).sty { - // Slices already point to the array element type. - bcx.inbounds_gep(self.llval, &[llindex]) - } else { - let zero = common::C_usize(bcx.ccx, 0); - bcx.inbounds_gep(self.llval, &[zero, llindex]) - } - } -} - -impl<'a, 'tcx> MirContext<'a, 'tcx> { - pub fn trans_lvalue(&mut self, - bcx: &Builder<'a, 'tcx>, - lvalue: &mir::Lvalue<'tcx>) - -> LvalueRef<'tcx> { - debug!("trans_lvalue(lvalue={:?})", lvalue); - - let ccx = bcx.ccx; - let tcx = ccx.tcx(); - - if let mir::Lvalue::Local(index) = *lvalue { - match self.locals[index] { - LocalRef::Lvalue(lvalue) => { - return lvalue; - } - LocalRef::Operand(..) => { - bug!("using operand local {:?} as lvalue", lvalue); - } - } - } - - let result = match *lvalue { - mir::Lvalue::Local(_) => bug!(), // handled above - mir::Lvalue::Static(box mir::Static { def_id, ty }) => { - LvalueRef::new_sized(consts::get_static(ccx, def_id), - LvalueTy::from_ty(self.monomorphize(&ty)), - Alignment::AbiAligned) - }, - mir::Lvalue::Projection(box mir::Projection { - ref base, - elem: mir::ProjectionElem::Deref - }) => { - // Load the pointer from its location. - self.trans_consume(bcx, base).deref() - } - mir::Lvalue::Projection(ref projection) => { - let tr_base = self.trans_lvalue(bcx, &projection.base); - let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); - let projected_ty = self.monomorphize(&projected_ty); - let align = tr_base.alignment; - - let ((llprojected, align), llextra) = match projection.elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - let llextra = if self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)) { - ptr::null_mut() - } else { - tr_base.llextra - }; - (tr_base.trans_field_ptr(bcx, field.index()), llextra) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Consume(mir::Lvalue::Local(index)); - let index = self.trans_operand(bcx, index); - let llindex = self.prepare_index(bcx, index.immediate()); - ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: false, - min_length: _ } => { - let lloffset = C_usize(bcx.ccx, offset as u64); - ((tr_base.project_index(bcx, lloffset), align), ptr::null_mut()) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: true, - min_length: _ } => { - let lloffset = C_usize(bcx.ccx, offset as u64); - let lllen = tr_base.len(bcx.ccx); - let llindex = bcx.sub(lllen, lloffset); - ((tr_base.project_index(bcx, llindex), align), ptr::null_mut()) - } - mir::ProjectionElem::Subslice { from, to } => { - let llbase = tr_base.project_index(bcx, C_usize(bcx.ccx, from as u64)); - - let base_ty = tr_base.ty.to_ty(bcx.tcx()); - match base_ty.sty { - ty::TyArray(..) => { - // must cast the lvalue pointer type to the new - // array type (*[%_; new_len]). - let base_ty = self.monomorphized_lvalue_ty(lvalue); - let llbasety = type_of::type_of(bcx.ccx, base_ty).ptr_to(); - let llbase = bcx.pointercast(llbase, llbasety); - ((llbase, align), ptr::null_mut()) - } - ty::TySlice(..) => { - assert!(tr_base.llextra != ptr::null_mut()); - let lllen = bcx.sub(tr_base.llextra, - C_usize(bcx.ccx, (from as u64)+(to as u64))); - ((llbase, align), lllen) - } - _ => bug!("unexpected type {:?} in Subslice", base_ty) - } - } - mir::ProjectionElem::Downcast(..) => { - ((tr_base.llval, align), tr_base.llextra) - } - }; - LvalueRef { - llval: llprojected, - llextra, - ty: projected_ty, - alignment: align, - } - } - }; - debug!("trans_lvalue(lvalue={:?}) => {:?}", lvalue, result); - result - } - - /// Adjust the bitwidth of an index since LLVM is less forgiving - /// than we are. - /// - /// nmatsakis: is this still necessary? Not sure. - 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, bcx.ccx.isize_ty()); - if index_size < int_size { - bcx.zext(llindex, bcx.ccx.isize_ty()) - } else if index_size > int_size { - bcx.trunc(llindex, bcx.ccx.isize_ty()) - } else { - llindex - } - } - - pub fn monomorphized_lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - let tcx = self.ccx.tcx(); - let lvalue_ty = lvalue.ty(self.mir, tcx); - self.monomorphize(&lvalue_ty.to_ty(tcx)) - } -} diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 5206ad74e2054..39e2503081af8 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -11,20 +11,18 @@ use libc::c_uint; use llvm::{self, ValueRef, BasicBlockRef}; use llvm::debuginfo::DIScope; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, LayoutTyper}; +use rustc::ty::{self, TypeFoldable}; +use rustc::ty::layout::{LayoutOf, TyLayout}; use rustc::mir::{self, Mir}; -use rustc::mir::tcx::LvalueTy; use rustc::ty::subst::Substs; use rustc::infer::TransNormalize; use rustc::session::config::FullDebugInfo; use base; use builder::Builder; -use common::{self, CrateContext, Funclet}; +use common::{CrateContext, Funclet}; use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; use monomorphize::Instance; -use abi::FnType; -use type_of; +use abi::{ArgAttribute, FnType, PassMode}; use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; use syntax::symbol::keywords; @@ -37,7 +35,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx}; pub use self::constant::trans_static_initializer; use self::analyze::CleanupKind; -use self::lvalue::{Alignment, LvalueRef}; +use self::place::{Alignment, PlaceRef}; use rustc::mir::traversal; use self::operand::{OperandRef, OperandValue}; @@ -61,7 +59,7 @@ pub struct MirContext<'a, 'tcx:'a> { /// don't really care about it very much. Anyway, this value /// contains an alloca into which the personality is stored and /// then later loaded when generating the DIVERGE_BLOCK. - llpersonalityslot: Option, + personality_slot: Option>, /// A `Block` for each MIR `BasicBlock` blocks: IndexVec, @@ -81,15 +79,15 @@ pub struct MirContext<'a, 'tcx:'a> { unreachable_block: Option, /// The location where each MIR arg/var/tmp/ret is stored. This is - /// usually an `LvalueRef` representing an alloca, but not always: + /// usually an `PlaceRef` representing an alloca, but not always: /// sometimes we can skip the alloca and just store the value /// directly using an `OperandRef`, which makes for tighter LLVM /// IR. The conditions for using an `OperandRef` are as follows: /// - /// - the type of the local must be judged "immediate" by `type_is_immediate` + /// - the type of the local must be judged "immediate" by `is_llvm_immediate` /// - the operand must never be referenced indirectly /// - we should not take its address using the `&` operator - /// - nor should it appear in an lvalue path like `tmp.a` + /// - nor should it appear in a place path like `tmp.a` /// - the operand must be defined by an rvalue that can generate immediate /// values /// @@ -173,18 +171,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } enum LocalRef<'tcx> { - Lvalue(LvalueRef<'tcx>), + Place(PlaceRef<'tcx>), Operand(Option>), } -impl<'tcx> LocalRef<'tcx> { - fn new_operand<'a>(ccx: &CrateContext<'a, 'tcx>, - ty: Ty<'tcx>) -> LocalRef<'tcx> { - if common::type_is_zero_size(ccx, ty) { +impl<'a, 'tcx> LocalRef<'tcx> { + fn new_operand(ccx: &CrateContext<'a, 'tcx>, layout: TyLayout<'tcx>) -> LocalRef<'tcx> { + if layout.is_zst() { // Zero-size temporaries aren't always initialized, which // doesn't matter because they don't contain data, but // we need something in the operand. - LocalRef::Operand(Some(OperandRef::new_zst(ccx, ty))) + LocalRef::Operand(Some(OperandRef::new_zst(ccx, layout))) } else { LocalRef::Operand(None) } @@ -232,7 +229,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( llfn, fn_ty, ccx, - llpersonalityslot: None, + personality_slot: None, blocks: block_bcxs, unreachable_block: None, cleanup_kinds, @@ -247,58 +244,58 @@ pub fn trans_mir<'a, 'tcx: 'a>( }, }; - let lvalue_locals = analyze::lvalue_locals(&mircx); + let memory_locals = analyze::memory_locals(&mircx); // Allocate variable and temp allocas mircx.locals = { - let args = arg_local_refs(&bcx, &mircx, &mircx.scopes, &lvalue_locals); + let args = arg_local_refs(&bcx, &mircx, &mircx.scopes, &memory_locals); let mut allocate_local = |local| { let decl = &mir.local_decls[local]; - let ty = mircx.monomorphize(&decl.ty); + let layout = bcx.ccx.layout_of(mircx.monomorphize(&decl.ty)); + assert!(!layout.ty.has_erasable_regions()); if let Some(name) = decl.name { // User variable let debug_scope = mircx.scopes[decl.source_info.scope]; let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo; - if !lvalue_locals.contains(local.index()) && !dbg { + if !memory_locals.contains(local.index()) && !dbg { debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(bcx.ccx, ty); + return LocalRef::new_operand(bcx.ccx, layout); } - debug!("alloc: {:?} ({}) -> lvalue", local, name); - assert!(!ty.has_erasable_regions()); - let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str()); + debug!("alloc: {:?} ({}) -> place", local, name); + let place = PlaceRef::alloca(&bcx, layout, &name.as_str()); if dbg { let (scope, span) = mircx.debug_loc(decl.source_info); - declare_local(&bcx, &mircx.debug_context, name, ty, scope, - VariableAccess::DirectVariable { alloca: lvalue.llval }, + declare_local(&bcx, &mircx.debug_context, name, layout.ty, scope, + VariableAccess::DirectVariable { alloca: place.llval }, VariableKind::LocalVariable, span); } - LocalRef::Lvalue(lvalue) + LocalRef::Place(place) } else { - // Temporary or return pointer - if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() { - debug!("alloc: {:?} (return pointer) -> lvalue", local); + // Temporary or return place + if local == mir::RETURN_PLACE && mircx.fn_ty.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); let llretptr = llvm::get_param(llfn, 0); - LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty), + LocalRef::Place(PlaceRef::new_sized(llretptr, + layout, Alignment::AbiAligned)) - } else if lvalue_locals.contains(local.index()) { - debug!("alloc: {:?} -> lvalue", local); - assert!(!ty.has_erasable_regions()); - LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", local))) + } else if memory_locals.contains(local.index()) { + debug!("alloc: {:?} -> place", local); + LocalRef::Place(PlaceRef::alloca(&bcx, layout, &format!("{:?}", local))) } else { // If this is an immediate local, we do not create an // alloca in advance. Instead we wait until we see the // definition and update the operand there. debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(bcx.ccx, ty) + LocalRef::new_operand(bcx.ccx, layout) } } }; - let retptr = allocate_local(mir::RETURN_POINTER); + let retptr = allocate_local(mir::RETURN_PLACE); iter::once(retptr) .chain(args.into_iter()) .chain(mir.vars_and_temps_iter().map(allocate_local)) @@ -358,12 +355,12 @@ fn create_funclets<'a, 'tcx>( } /// Produce, for each argument, a `ValueRef` pointing at the -/// argument's value. As arguments are lvalues, these are always +/// argument's value. As arguments are places, these are always /// indirect. fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, mircx: &MirContext<'a, 'tcx>, scopes: &IndexVec, - lvalue_locals: &BitVector) + memory_locals: &BitVector) -> Vec> { let mir = mircx.mir; let tcx = bcx.tcx(); @@ -378,9 +375,18 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, None }; + let deref_op = unsafe { + [llvm::LLVMRustDIBuilderCreateOpDeref()] + }; + mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; - let arg_ty = mircx.monomorphize(&arg_decl.ty); + + let name = if let Some(name) = arg_decl.name { + name.as_str().to_string() + } else { + format!("arg{}", arg_index) + }; if Some(local) == mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) @@ -388,33 +394,24 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // to reconstruct it into a tuple local variable, from multiple // individual LLVM function arguments. + let arg_ty = mircx.monomorphize(&arg_decl.ty); let tupled_arg_tys = match arg_ty.sty { ty::TyTuple(ref tys, _) => tys, _ => bug!("spread argument isn't a tuple?!") }; - let lvalue = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index)); - for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { - let (dst, _) = lvalue.trans_field_ptr(bcx, i); + let place = PlaceRef::alloca(bcx, bcx.ccx.layout_of(arg_ty), &name); + for i in 0..tupled_arg_tys.len() { let arg = &mircx.fn_ty.args[idx]; idx += 1; - if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) { - // We pass fat pointers as two words, but inside the tuple - // they are the two sub-fields of a single aggregate field. - let meta = &mircx.fn_ty.args[idx]; - idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, dst)); - meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, dst)); - } else { - arg.store_fn_arg(bcx, &mut llarg_idx, dst); - } + arg.store_fn_arg(bcx, &mut llarg_idx, place.project_field(bcx, i)); } // Now that we have one alloca that contains the aggregate value, // we can create one debuginfo entry for the argument. arg_scope.map(|scope| { let variable_access = VariableAccess::DirectVariable { - alloca: lvalue.llval + alloca: place.llval }; declare_local( bcx, @@ -427,96 +424,88 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ); }); - return LocalRef::Lvalue(lvalue); + return LocalRef::Place(place); } let arg = &mircx.fn_ty.args[idx]; idx += 1; - let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up, unless - // we emit extra-debug-info, which requires local allocas :(. - // FIXME: lifetimes - if arg.pad.is_some() { - llarg_idx += 1; - } - let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + if arg.pad.is_some() { llarg_idx += 1; - llarg - } else if !lvalue_locals.contains(local.index()) && - !arg.is_indirect() && arg.cast.is_none() && - arg_scope.is_none() { - if arg.is_ignore() { - return LocalRef::new_operand(bcx.ccx, arg_ty); - } + } + if arg_scope.is_none() && !memory_locals.contains(local.index()) { // We don't have to cast or keep the argument in the alloca. // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead // of putting everything in allocas just so we can use llvm.dbg.declare. - if arg.pad.is_some() { - llarg_idx += 1; + let local = |op| LocalRef::Operand(Some(op)); + match arg.mode { + PassMode::Ignore => { + return local(OperandRef::new_zst(bcx.ccx, arg.layout)); + } + PassMode::Direct(_) => { + let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(llarg, &name); + llarg_idx += 1; + return local( + OperandRef::from_immediate_or_packed_pair(bcx, llarg, arg.layout)); + } + PassMode::Pair(..) => { + let a = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(a, &(name.clone() + ".0")); + llarg_idx += 1; + + let b = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(b, &(name + ".1")); + llarg_idx += 1; + + return local(OperandRef { + val: OperandValue::Pair(a, b), + layout: arg.layout + }); + } + _ => {} } + } + + let place = if arg.is_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up. + // FIXME: lifetimes let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); + bcx.set_value_name(llarg, &name); 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(bcx.llfn(), llarg_idx as c_uint); - llarg_idx += 1; - - // FIXME(eddyb) As we can't perfectly represent the data and/or - // vtable pointer in a fat pointers in Rust's typesystem, and - // because we split fat pointers into two ArgType's, they're - // not the right type so we have to cast them for now. - let pointee = match arg_ty.sty { - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => ty, - ty::TyAdt(def, _) if def.is_box() => arg_ty.boxed_ty(), - _ => bug!() - }; - let data_llty = type_of::in_memory_type_of(bcx.ccx, pointee); - let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee); - - let llarg = bcx.pointercast(llarg, data_llty.ptr_to()); - let llmeta = bcx.pointercast(llmeta, meta_llty); - - OperandValue::Pair(llarg, llmeta) - } else { - OperandValue::Immediate(llarg) - }; - let operand = OperandRef { - val, - ty: arg_ty - }; - return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); + PlaceRef::new_sized(llarg, arg.layout, Alignment::AbiAligned) } else { - let lltemp = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index)); - if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - // we pass fat pointers as two words, but we want to - // represent them internally as a pointer to two words, - // so make an alloca to store them in. - let meta = &mircx.fn_ty.args[idx]; - idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, lltemp.llval)); - meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, lltemp.llval)); - } else { - // otherwise, arg is passed by value, so make a - // temporary and store it there - arg.store_fn_arg(bcx, &mut llarg_idx, lltemp.llval); - } - lltemp.llval + let tmp = PlaceRef::alloca(bcx, arg.layout, &name); + arg.store_fn_arg(bcx, &mut llarg_idx, tmp); + tmp }; arg_scope.map(|scope| { // Is this a regular argument? if arg_index > 0 || mir.upvar_decls.is_empty() { + // The Rust ABI passes indirect variables using a pointer and a manual copy, so we + // need to insert a deref here, but the C ABI uses a pointer and a copy using the + // byval attribute, for which LLVM does the deref itself, so we must not add it. + let mut variable_access = VariableAccess::DirectVariable { + alloca: place.llval + }; + + if let PassMode::Indirect(ref attrs) = arg.mode { + if !attrs.contains(ArgAttribute::ByVal) { + variable_access = VariableAccess::IndirectVariable { + alloca: place.llval, + address_operations: &deref_op, + }; + } + } + declare_local( bcx, &mircx.debug_context, arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg_ty, + arg.layout.ty, scope, - VariableAccess::DirectVariable { alloca: llval }, + variable_access, VariableKind::ArgumentVariable(arg_index + 1), DUMMY_SP ); @@ -524,15 +513,15 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, } // Or is it the closure environment? - let (closure_ty, env_ref) = match arg_ty.sty { - ty::TyRef(_, mt) | ty::TyRawPtr(mt) => (mt.ty, true), - _ => (arg_ty, false) + let (closure_layout, env_ref) = match arg.layout.ty.sty { + ty::TyRef(_, mt) | ty::TyRawPtr(mt) => (bcx.ccx.layout_of(mt.ty), true), + _ => (arg.layout, false) }; - let upvar_tys = match closure_ty.sty { + let upvar_tys = match closure_layout.ty.sty { ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => substs.upvar_tys(def_id, tcx), - _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_ty) + _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_layout.ty) }; // Store the pointer to closure data in an alloca for debuginfo @@ -543,21 +532,17 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'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.alloca(common::val_ty(llval), "__debuginfo_env_ptr", None); - bcx.store(llval, alloc, None); - alloc + let alloc = PlaceRef::alloca(bcx, + bcx.ccx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), + "__debuginfo_env_ptr"); + bcx.store(place.llval, alloc.llval, None); + alloc.llval } else { - llval - }; - - let layout = bcx.ccx.layout_of(closure_ty); - let offsets = match *layout { - layout::Univariant { ref variant, .. } => &variant.offsets[..], - _ => bug!("Closures are only supposed to be Univariant") + place.llval }; for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { - let byte_offset_of_var_in_env = offsets[i].bytes(); + let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); let ops = unsafe { [llvm::LLVMRustDIBuilderCreateOpDeref(), @@ -595,15 +580,14 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ); } }); - LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty), - Alignment::AbiAligned)) + LocalRef::Place(place) }).collect() } mod analyze; mod block; mod constant; -pub mod lvalue; -mod operand; +pub mod place; +pub mod operand; mod rvalue; mod statement; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 9ce1749190ba1..9f97102577231 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -9,25 +9,23 @@ // except according to those terms. use llvm::ValueRef; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{Layout, LayoutTyper}; +use rustc::ty; +use rustc::ty::layout::{self, LayoutOf, TyLayout}; use rustc::mir; -use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; -use adt; use base; -use common::{self, CrateContext, C_null}; +use common::{self, CrateContext, C_undef, C_usize}; use builder::Builder; use value::Value; -use type_of; +use type_of::LayoutLlvmExt; use type_::Type; use std::fmt; use std::ptr; use super::{MirContext, LocalRef}; -use super::lvalue::{Alignment, LvalueRef}; +use super::place::{Alignment, PlaceRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -43,63 +41,52 @@ pub enum OperandValue { Pair(ValueRef, ValueRef) } +impl fmt::Debug for OperandValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + OperandValue::Ref(r, align) => { + write!(f, "Ref({:?}, {:?})", Value(r), align) + } + OperandValue::Immediate(i) => { + write!(f, "Immediate({:?})", Value(i)) + } + OperandValue::Pair(a, b) => { + write!(f, "Pair({:?}, {:?})", Value(a), Value(b)) + } + } + } +} + /// An `OperandRef` is an "SSA" reference to a Rust value, along with /// its type. /// /// NOTE: unless you know a value's type exactly, you should not /// generate LLVM opcodes acting on it and instead act via methods, -/// to avoid nasty edge cases. In particular, using `Builder.store` -/// directly is sure to cause problems -- use `MirContext.store_operand` +/// to avoid nasty edge cases. In particular, using `Builder::store` +/// directly is sure to cause problems -- use `OperandRef::store` /// instead. #[derive(Copy, Clone)] pub struct OperandRef<'tcx> { // The value. pub val: OperandValue, - // The type of value being returned. - pub ty: Ty<'tcx> + // The layout of value, based on its Rust type. + pub layout: TyLayout<'tcx>, } impl<'tcx> fmt::Debug for OperandRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.val { - OperandValue::Ref(r, align) => { - write!(f, "OperandRef(Ref({:?}, {:?}) @ {:?})", - Value(r), align, self.ty) - } - OperandValue::Immediate(i) => { - write!(f, "OperandRef(Immediate({:?}) @ {:?})", - Value(i), self.ty) - } - OperandValue::Pair(a, b) => { - write!(f, "OperandRef(Pair({:?}, {:?}) @ {:?})", - Value(a), Value(b), self.ty) - } - } + write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) } } impl<'a, 'tcx> OperandRef<'tcx> { pub fn new_zst(ccx: &CrateContext<'a, 'tcx>, - ty: Ty<'tcx>) -> OperandRef<'tcx> { - assert!(common::type_is_zero_size(ccx, ty)); - let llty = type_of::type_of(ccx, ty); - let val = if common::type_is_imm_pair(ccx, ty) { - let layout = ccx.layout_of(ty); - let (ix0, ix1) = if let Layout::Univariant { ref variant, .. } = *layout { - (adt::struct_llfields_index(variant, 0), - adt::struct_llfields_index(variant, 1)) - } else { - (0, 1) - }; - let fields = llty.field_types(); - OperandValue::Pair(C_null(fields[ix0]), C_null(fields[ix1])) - } else { - OperandValue::Immediate(C_null(llty)) - }; + layout: TyLayout<'tcx>) -> OperandRef<'tcx> { + assert!(layout.is_zst()); OperandRef { - val, - ty, + val: OperandValue::Immediate(C_undef(layout.immediate_llvm_type(ccx))), + layout } } @@ -112,174 +99,205 @@ impl<'a, 'tcx> OperandRef<'tcx> { } } - pub fn deref(self) -> LvalueRef<'tcx> { - let projected_ty = self.ty.builtin_deref(true, ty::NoPreference) + pub fn deref(self, ccx: &CrateContext<'a, 'tcx>) -> PlaceRef<'tcx> { + let projected_ty = self.layout.ty.builtin_deref(true, ty::NoPreference) .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; let (llptr, llextra) = match self.val { OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), OperandValue::Pair(llptr, llextra) => (llptr, llextra), OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) }; - LvalueRef { + PlaceRef { llval: llptr, llextra, - ty: LvalueTy::from_ty(projected_ty), + layout: ccx.layout_of(projected_ty), alignment: Alignment::AbiAligned, } } - /// If this operand is a Pair, we return an - /// Immediate aggregate with the two values. - pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { + /// If this operand is a `Pair`, we return an aggregate with the two values. + /// For other cases, see `immediate`. + pub fn immediate_or_packed_pair(self, bcx: &Builder<'a, 'tcx>) -> ValueRef { if let OperandValue::Pair(a, b) = self.val { + let llty = self.layout.llvm_type(bcx.ccx); + debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", + self, llty); // Reconstruct the immediate aggregate. - let llty = type_of::type_of(bcx.ccx, self.ty); - let mut llpair = common::C_undef(llty); - let elems = [a, b]; - for i in 0..2 { - let mut elem = elems[i]; - // Extend boolean i1's to i8. - if common::val_ty(elem) == Type::i1(bcx.ccx) { - elem = bcx.zext(elem, Type::i8(bcx.ccx)); - } - let layout = bcx.ccx.layout_of(self.ty); - let i = if let Layout::Univariant { ref variant, .. } = *layout { - adt::struct_llfields_index(variant, i) - } else { - i - }; - llpair = bcx.insert_value(llpair, elem, i); - } - self.val = OperandValue::Immediate(llpair); + let mut llpair = C_undef(llty); + llpair = bcx.insert_value(llpair, a, 0); + llpair = bcx.insert_value(llpair, b, 1); + llpair + } else { + self.immediate() } - self } - /// 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: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { - if let OperandValue::Immediate(llval) = self.val { + /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. + pub fn from_immediate_or_packed_pair(bcx: &Builder<'a, 'tcx>, + llval: ValueRef, + layout: TyLayout<'tcx>) + -> OperandRef<'tcx> { + let val = if layout.is_llvm_scalar_pair() { + debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", + llval, layout); + // Deconstruct the immediate aggregate. - if common::type_is_imm_pair(bcx.ccx, self.ty) { - debug!("Operand::unpack_if_pair: unpacking {:?}", self); + OperandValue::Pair(bcx.extract_value(llval, 0), + bcx.extract_value(llval, 1)) + } else { + OperandValue::Immediate(llval) + }; + OperandRef { val, layout } + } - let layout = bcx.ccx.layout_of(self.ty); - let (ix0, ix1) = if let Layout::Univariant { ref variant, .. } = *layout { - (adt::struct_llfields_index(variant, 0), - adt::struct_llfields_index(variant, 1)) - } else { - (0, 1) + pub fn extract_field(&self, bcx: &Builder<'a, 'tcx>, i: usize) -> OperandRef<'tcx> { + let field = self.layout.field(bcx.ccx, i); + let offset = self.layout.fields.offset(i); + + let mut val = match (self.val, &self.layout.abi) { + // If we're uninhabited, or the field is ZST, it has no data. + _ if self.layout.abi == layout::Abi::Uninhabited || field.is_zst() => { + return OperandRef { + val: OperandValue::Immediate(C_undef(field.immediate_llvm_type(bcx.ccx))), + layout: field }; + } - let mut a = bcx.extract_value(llval, ix0); - let mut b = bcx.extract_value(llval, ix1); + // Newtype of a scalar or scalar pair. + (OperandValue::Immediate(_), _) | + (OperandValue::Pair(..), _) if field.size == self.layout.size => { + assert_eq!(offset.bytes(), 0); + self.val + } - let pair_fields = common::type_pair_fields(bcx.ccx, self.ty); - if let Some([a_ty, b_ty]) = pair_fields { - if a_ty.is_bool() { - a = bcx.trunc(a, Type::i1(bcx.ccx)); - } - if b_ty.is_bool() { - b = bcx.trunc(b, Type::i1(bcx.ccx)); - } + // Extract a scalar component from a pair. + (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { + if offset.bytes() == 0 { + assert_eq!(field.size, a.value.size(bcx.ccx)); + OperandValue::Immediate(a_llval) + } else { + assert_eq!(offset, a.value.size(bcx.ccx) + .abi_align(b.value.align(bcx.ccx))); + assert_eq!(field.size, b.value.size(bcx.ccx)); + OperandValue::Immediate(b_llval) } + } - self.val = OperandValue::Pair(a, b); + // `#[repr(simd)]` types are also immediate. + (OperandValue::Immediate(llval), &layout::Abi::Vector) => { + OperandValue::Immediate( + bcx.extract_element(llval, C_usize(bcx.ccx, i as u64))) } + + _ => bug!("OperandRef::extract_field({:?}): not applicable", self) + }; + + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + match val { + OperandValue::Immediate(ref mut llval) => { + *llval = bcx.bitcast(*llval, field.immediate_llvm_type(bcx.ccx)); + } + OperandValue::Pair(ref mut a, ref mut b) => { + *a = bcx.bitcast(*a, field.scalar_pair_element_llvm_type(bcx.ccx, 0)); + *b = bcx.bitcast(*b, field.scalar_pair_element_llvm_type(bcx.ccx, 1)); + } + OperandValue::Ref(..) => bug!() + } + + OperandRef { + val, + layout: field } - self } } -impl<'a, 'tcx> MirContext<'a, 'tcx> { - pub fn trans_load(&mut self, - bcx: &Builder<'a, 'tcx>, - llval: ValueRef, - align: Alignment, - ty: Ty<'tcx>) - -> OperandRef<'tcx> - { - debug!("trans_load: {:?} @ {:?}", Value(llval), ty); - - let val = if common::type_is_fat_ptr(bcx.ccx, ty) { - let (lldata, llextra) = base::load_fat_ptr(bcx, llval, align, ty); - OperandValue::Pair(lldata, llextra) - } else if common::type_is_imm_pair(bcx.ccx, ty) { - let (ix0, ix1, f_align) = match *bcx.ccx.layout_of(ty) { - Layout::Univariant { ref variant, .. } => { - (adt::struct_llfields_index(variant, 0), - adt::struct_llfields_index(variant, 1), - Alignment::from_packed(variant.packed) | align) - }, - _ => (0, 1, align) - }; - let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap(); - let a_ptr = bcx.struct_gep(llval, ix0); - let b_ptr = bcx.struct_gep(llval, ix1); - - OperandValue::Pair( - base::load_ty(bcx, a_ptr, f_align, a_ty), - base::load_ty(bcx, b_ptr, f_align, b_ty) - ) - } else if common::type_is_immediate(bcx.ccx, ty) { - OperandValue::Immediate(base::load_ty(bcx, llval, align, ty)) - } else { - OperandValue::Ref(llval, align) - }; - - OperandRef { val: val, ty: ty } +impl<'a, 'tcx> OperandValue { + pub fn store(self, bcx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); + // 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 dest.layout.is_zst() { + return; + } + match self { + OperandValue::Ref(r, source_align) => + base::memcpy_ty(bcx, dest.llval, r, dest.layout, + (source_align | dest.alignment).non_abi()), + OperandValue::Immediate(s) => { + bcx.store(base::from_immediate(bcx, s), dest.llval, dest.alignment.non_abi()); + } + OperandValue::Pair(a, b) => { + for (i, &x) in [a, b].iter().enumerate() { + let mut llptr = bcx.struct_gep(dest.llval, i as u64); + // Make sure to always store i1 as i8. + if common::val_ty(x) == Type::i1(bcx.ccx) { + llptr = bcx.pointercast(llptr, Type::i8p(bcx.ccx)); + } + bcx.store(base::from_immediate(bcx, x), llptr, dest.alignment.non_abi()); + } + } + } } +} - pub fn trans_consume(&mut self, - bcx: &Builder<'a, 'tcx>, - lvalue: &mir::Lvalue<'tcx>) - -> OperandRef<'tcx> +impl<'a, 'tcx> MirContext<'a, 'tcx> { + fn maybe_trans_consume_direct(&mut self, + bcx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> Option> { - debug!("trans_consume(lvalue={:?})", lvalue); + debug!("maybe_trans_consume_direct(place={:?})", place); // watch out for locals that do not have an // alloca; they are handled somewhat differently - if let mir::Lvalue::Local(index) = *lvalue { + if let mir::Place::Local(index) = *place { match self.locals[index] { LocalRef::Operand(Some(o)) => { - return o; + return Some(o); } LocalRef::Operand(None) => { - bug!("use of {:?} before def", lvalue); + bug!("use of {:?} before def", place); } - LocalRef::Lvalue(..) => { + LocalRef::Place(..) => { // use path below } } } - // Moves out of pair fields are trivial. - if let &mir::Lvalue::Projection(ref proj) = lvalue { - if let mir::Lvalue::Local(index) = proj.base { - if let LocalRef::Operand(Some(o)) = self.locals[index] { - match (o.val, &proj.elem) { - (OperandValue::Pair(a, b), - &mir::ProjectionElem::Field(ref f, ty)) => { - let llval = [a, b][f.index()]; - let op = OperandRef { - val: OperandValue::Immediate(llval), - ty: self.monomorphize(&ty) - }; - - // Handle nested pairs. - return op.unpack_if_pair(bcx); - } - _ => {} - } + // Moves out of scalar and scalar pair fields are trivial. + if let &mir::Place::Projection(ref proj) = place { + if let mir::ProjectionElem::Field(ref f, _) = proj.elem { + if let Some(o) = self.maybe_trans_consume_direct(bcx, &proj.base) { + return Some(o.extract_field(bcx, f.index())); } } } - // for most lvalues, to consume them we just load them + None + } + + pub fn trans_consume(&mut self, + bcx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_consume(place={:?})", place); + + let ty = self.monomorphized_place_ty(place); + let layout = bcx.ccx.layout_of(ty); + + // ZSTs don't require any actual memory access. + if layout.is_zst() { + return OperandRef::new_zst(bcx.ccx, layout); + } + + if let Some(o) = self.maybe_trans_consume_direct(bcx, place) { + return o; + } + + // for most places, to consume them we just load them // out from their home - let tr_lvalue = self.trans_lvalue(bcx, lvalue); - let ty = tr_lvalue.ty.to_ty(bcx.tcx()); - self.trans_load(bcx, tr_lvalue.llval, tr_lvalue.alignment, ty) + self.trans_place(bcx, place).load(bcx) } pub fn trans_operand(&mut self, @@ -290,8 +308,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { debug!("trans_operand(operand={:?})", operand); match *operand { - mir::Operand::Consume(ref lvalue) => { - self.trans_consume(bcx, lvalue) + mir::Operand::Copy(ref place) | + mir::Operand::Move(ref place) => { + self.trans_consume(bcx, place) } mir::Operand::Constant(ref constant) => { @@ -299,60 +318,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let operand = val.to_operand(bcx.ccx); if let OperandValue::Ref(ptr, align) = operand.val { // If this is a OperandValue::Ref to an immediate constant, load it. - self.trans_load(bcx, ptr, align, operand.ty) + PlaceRef::new_sized(ptr, operand.layout, align).load(bcx) } else { operand } } } } - - pub fn store_operand(&mut self, - bcx: &Builder<'a, 'tcx>, - lldest: ValueRef, - align: Option, - operand: OperandRef<'tcx>) { - 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, Alignment::Packed) => - base::memcpy_ty(bcx, lldest, r, operand.ty, Some(1)), - OperandValue::Ref(r, Alignment::AbiAligned) => - 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 (ix0, ix1, f_align) = match *bcx.ccx.layout_of(operand.ty) { - Layout::Univariant { ref variant, .. } => { - (adt::struct_llfields_index(variant, 0), - adt::struct_llfields_index(variant, 1), - if variant.packed { Some(1) } else { None }) - } - _ => (0, 1, align) - }; - - let a = base::from_immediate(bcx, a); - let b = base::from_immediate(bcx, b); - - // See comment above about zero-sized values. - let (a_zst, b_zst) = common::type_pair_fields(bcx.ccx, operand.ty) - .map_or((false, false), |[a_ty, b_ty]| { - (common::type_is_zero_size(bcx.ccx, a_ty), - common::type_is_zero_size(bcx.ccx, b_ty)) - }); - - if !a_zst { - bcx.store(a, bcx.struct_gep(lldest, ix0), f_align); - } - if !b_zst { - bcx.store(b, bcx.struct_gep(lldest, ix1), f_align); - } - } - } - } } diff --git a/src/librustc_trans/mir/place.rs b/src/librustc_trans/mir/place.rs new file mode 100644 index 0000000000000..3bcbb7f3b46eb --- /dev/null +++ b/src/librustc_trans/mir/place.rs @@ -0,0 +1,545 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::mir; +use rustc::mir::tcx::PlaceTy; +use rustc_data_structures::indexed_vec::Idx; +use base; +use builder::Builder; +use common::{CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big}; +use consts; +use type_of::LayoutLlvmExt; +use type_::Type; +use value::Value; +use glue; + +use std::ptr; +use std::ops; + +use super::{MirContext, LocalRef}; +use super::operand::{OperandRef, OperandValue}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Alignment { + Packed(Align), + AbiAligned, +} + +impl ops::BitOr for Alignment { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + match (self, rhs) { + (Alignment::Packed(a), Alignment::Packed(b)) => { + Alignment::Packed(a.min(b)) + } + (Alignment::Packed(x), _) | (_, Alignment::Packed(x)) => { + Alignment::Packed(x) + } + (Alignment::AbiAligned, Alignment::AbiAligned) => { + Alignment::AbiAligned + } + } + } +} + +impl<'a> From> for Alignment { + fn from(layout: TyLayout) -> Self { + if layout.is_packed() { + Alignment::Packed(layout.align) + } else { + Alignment::AbiAligned + } + } +} + +impl Alignment { + pub fn non_abi(self) -> Option { + match self { + Alignment::Packed(x) => Some(x), + Alignment::AbiAligned => None, + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PlaceRef<'tcx> { + /// Pointer to the contents of the place + pub llval: ValueRef, + + /// This place's extra data if it is unsized, or null + pub llextra: ValueRef, + + /// Monomorphized type of this place, including variant information + pub layout: TyLayout<'tcx>, + + /// Whether this place is known to be aligned according to its layout + pub alignment: Alignment, +} + +impl<'a, 'tcx> PlaceRef<'tcx> { + pub fn new_sized(llval: ValueRef, + layout: TyLayout<'tcx>, + alignment: Alignment) + -> PlaceRef<'tcx> { + PlaceRef { + llval, + llextra: ptr::null_mut(), + layout, + alignment + } + } + + pub fn alloca(bcx: &Builder<'a, 'tcx>, layout: TyLayout<'tcx>, name: &str) + -> PlaceRef<'tcx> { + debug!("alloca({:?}: {:?})", name, layout); + let tmp = bcx.alloca(layout.llvm_type(bcx.ccx), name, layout.align); + Self::new_sized(tmp, layout, Alignment::AbiAligned) + } + + pub fn len(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { + if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { + if self.layout.is_unsized() { + assert!(self.has_extra()); + assert_eq!(count, 0); + self.llextra + } else { + C_usize(ccx, count) + } + } else { + bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) + } + } + + pub fn has_extra(&self) -> bool { + !self.llextra.is_null() + } + + pub fn load(&self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { + debug!("PlaceRef::load: {:?}", self); + + assert!(!self.has_extra()); + + if self.layout.is_zst() { + return OperandRef::new_zst(bcx.ccx, self.layout); + } + + let scalar_load_metadata = |load, scalar: &layout::Scalar| { + let (min, max) = (scalar.valid_range.start, scalar.valid_range.end); + let max_next = max.wrapping_add(1); + let bits = scalar.value.size(bcx.ccx).bits(); + assert!(bits <= 128); + let mask = !0u128 >> (128 - bits); + // For a (max) value of -1, max will be `-1 as usize`, which overflows. + // However, that is fine here (it would still represent the full range), + // i.e., if the range is everything. The lo==hi case would be + // rejected by the LLVM verifier (it would mean either an + // empty set, which is impossible, or the entire range of the + // type, which is pointless). + match scalar.value { + layout::Int(..) if max_next & mask != min & mask => { + // llvm::ConstantRange can deal with ranges that wrap around, + // so an overflow on (max + 1) is fine. + bcx.range_metadata(load, min..max_next); + } + layout::Pointer if 0 < min && min < max => { + bcx.nonnull_metadata(load); + } + _ => {} + } + }; + + let val = if self.layout.is_llvm_immediate() { + let mut const_llval = ptr::null_mut(); + unsafe { + let global = llvm::LLVMIsAGlobalVariable(self.llval); + if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { + const_llval = llvm::LLVMGetInitializer(global); + } + } + + let llval = if !const_llval.is_null() { + const_llval + } else { + let load = bcx.load(self.llval, self.alignment.non_abi()); + if let layout::Abi::Scalar(ref scalar) = self.layout.abi { + scalar_load_metadata(load, scalar); + } + load + }; + OperandValue::Immediate(base::to_immediate(bcx, llval, self.layout)) + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + let load = |i, scalar: &layout::Scalar| { + let mut llptr = bcx.struct_gep(self.llval, i as u64); + // Make sure to always load i1 as i8. + if scalar.is_bool() { + llptr = bcx.pointercast(llptr, Type::i8p(bcx.ccx)); + } + let load = bcx.load(llptr, self.alignment.non_abi()); + scalar_load_metadata(load, scalar); + if scalar.is_bool() { + bcx.trunc(load, Type::i1(bcx.ccx)) + } else { + load + } + }; + OperandValue::Pair(load(0, a), load(1, b)) + } else { + OperandValue::Ref(self.llval, self.alignment) + }; + + OperandRef { val, layout: self.layout } + } + + /// Access a field, at a point when the value's case is known. + pub fn project_field(self, bcx: &Builder<'a, 'tcx>, ix: usize) -> PlaceRef<'tcx> { + let ccx = bcx.ccx; + let field = self.layout.field(ccx, ix); + let offset = self.layout.fields.offset(ix); + let alignment = self.alignment | Alignment::from(self.layout); + + let simple = || { + // Unions and newtypes only use an offset of 0. + let llval = if offset.bytes() == 0 { + self.llval + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + // Offsets have to match either first or second field. + assert_eq!(offset, a.value.size(ccx).abi_align(b.value.align(ccx))); + bcx.struct_gep(self.llval, 1) + } else { + bcx.struct_gep(self.llval, self.layout.llvm_field_index(ix)) + }; + PlaceRef { + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + llval: bcx.pointercast(llval, field.llvm_type(ccx).ptr_to()), + llextra: if ccx.shared().type_has_metadata(field.ty) { + self.llextra + } else { + ptr::null_mut() + }, + layout: field, + alignment, + } + }; + + // Simple case - we can just GEP the field + // * Packed struct - There is no alignment padding + // * Field is sized - pointer is properly aligned already + if self.layout.is_packed() || !field.is_unsized() { + return simple(); + } + + // If the type of the last field is [T], str or a foreign type, then we don't need to do + // any adjusments + match field.ty.sty { + ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(), + _ => () + } + + // 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(self.llval)); + return simple(); + } + + // 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 unaligned_offset = C_usize(ccx, offset.bytes()); + + // Get the alignment of the field + let (_, align) = glue::size_and_align_of_dst(bcx, field.ty, 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_usize(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(self.llval, Type::i8p(ccx)); + let byte_ptr = bcx.gep(byte_ptr, &[offset]); + + // Finally, cast back to the type expected + let ll_fty = field.llvm_type(ccx); + debug!("struct_field_ptr: Field type is {:?}", ll_fty); + + PlaceRef { + llval: bcx.pointercast(byte_ptr, ll_fty.ptr_to()), + llextra: self.llextra, + layout: field, + alignment, + } + } + + /// Obtain the actual discriminant of a value. + pub fn trans_get_discr(self, bcx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> ValueRef { + let cast_to = bcx.ccx.layout_of(cast_to).immediate_llvm_type(bcx.ccx); + match self.layout.variants { + layout::Variants::Single { index } => { + return C_uint(cast_to, index as u64); + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + + let discr = self.project_field(bcx, 0); + let lldiscr = discr.load(bcx).immediate(); + match self.layout.variants { + layout::Variants::Single { .. } => bug!(), + layout::Variants::Tagged { ref discr, .. } => { + let signed = match discr.value { + layout::Int(_, signed) => signed, + _ => false + }; + bcx.intcast(lldiscr, cast_to, signed) + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + let niche_llty = discr.layout.immediate_llvm_type(bcx.ccx); + if niche_variants.start == niche_variants.end { + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_start == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_start) + }; + bcx.select(bcx.icmp(llvm::IntEQ, lldiscr, niche_llval), + C_uint(cast_to, niche_variants.start as u64), + C_uint(cast_to, dataful_variant as u64)) + } else { + // Rebase from niche values to discriminant values. + let delta = niche_start.wrapping_sub(niche_variants.start as u128); + let lldiscr = bcx.sub(lldiscr, C_uint_big(niche_llty, delta)); + let lldiscr_max = C_uint(niche_llty, niche_variants.end as u64); + bcx.select(bcx.icmp(llvm::IntULE, lldiscr, lldiscr_max), + bcx.intcast(lldiscr, cast_to, false), + C_uint(cast_to, dataful_variant as u64)) + } + } + } + } + + /// Set the discriminant for a new value of the given case of the given + /// representation. + pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) { + match self.layout.variants { + layout::Variants::Single { index } => { + if index != variant_index { + // If the layout of an enum is `Single`, all + // other variants are necessarily uninhabited. + assert_eq!(self.layout.for_variant(bcx.ccx, variant_index).abi, + layout::Abi::Uninhabited); + } + } + layout::Variants::Tagged { .. } => { + let ptr = self.project_field(bcx, 0); + let to = self.layout.ty.ty_adt_def().unwrap() + .discriminant_for_variant(bcx.tcx(), variant_index) + .to_u128_unchecked() as u64; + bcx.store(C_int(ptr.layout.llvm_type(bcx.ccx), to as i64), + ptr.llval, ptr.alignment.non_abi()); + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + if variant_index != dataful_variant { + if bcx.sess().target.target.arch == "arm" || + bcx.sess().target.target.arch == "aarch64" { + // Issue #34427: As workaround for LLVM bug on ARM, + // use memset of 0 before assigning niche value. + let llptr = bcx.pointercast(self.llval, Type::i8(bcx.ccx).ptr_to()); + let fill_byte = C_u8(bcx.ccx, 0); + let (size, align) = self.layout.size_and_align(); + let size = C_usize(bcx.ccx, size.bytes()); + let align = C_u32(bcx.ccx, align.abi() as u32); + base::call_memset(bcx, llptr, fill_byte, size, align, false); + } + + let niche = self.project_field(bcx, 0); + let niche_llty = niche.layout.immediate_llvm_type(bcx.ccx); + let niche_value = ((variant_index - niche_variants.start) as u128) + .wrapping_add(niche_start); + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_value == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_value) + }; + OperandValue::Immediate(niche_llval).store(bcx, niche); + } + } + } + } + + pub fn project_index(&self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) + -> PlaceRef<'tcx> { + PlaceRef { + llval: bcx.inbounds_gep(self.llval, &[C_usize(bcx.ccx, 0), llindex]), + llextra: ptr::null_mut(), + layout: self.layout.field(bcx.ccx, 0), + alignment: self.alignment + } + } + + pub fn project_downcast(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) + -> PlaceRef<'tcx> { + let mut downcast = *self; + downcast.layout = self.layout.for_variant(bcx.ccx, variant_index); + + // Cast to the appropriate variant struct type. + let variant_ty = downcast.layout.llvm_type(bcx.ccx); + downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to()); + + downcast + } + + pub fn storage_live(&self, bcx: &Builder<'a, 'tcx>) { + bcx.lifetime_start(self.llval, self.layout.size); + } + + pub fn storage_dead(&self, bcx: &Builder<'a, 'tcx>) { + bcx.lifetime_end(self.llval, self.layout.size); + } +} + +impl<'a, 'tcx> MirContext<'a, 'tcx> { + pub fn trans_place(&mut self, + bcx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> PlaceRef<'tcx> { + debug!("trans_place(place={:?})", place); + + let ccx = bcx.ccx; + let tcx = ccx.tcx(); + + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Place(place) => { + return place; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as place", place); + } + } + } + + let result = match *place { + mir::Place::Local(_) => bug!(), // handled above + mir::Place::Static(box mir::Static { def_id, ty }) => { + PlaceRef::new_sized(consts::get_static(ccx, def_id), + ccx.layout_of(self.monomorphize(&ty)), + Alignment::AbiAligned) + }, + mir::Place::Projection(box mir::Projection { + ref base, + elem: mir::ProjectionElem::Deref + }) => { + // Load the pointer from its location. + self.trans_consume(bcx, base).deref(bcx.ccx) + } + mir::Place::Projection(ref projection) => { + let tr_base = self.trans_place(bcx, &projection.base); + + match projection.elem { + mir::ProjectionElem::Deref => bug!(), + mir::ProjectionElem::Field(ref field, _) => { + tr_base.project_field(bcx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::Local(index)); + let index = self.trans_operand(bcx, index); + let llindex = index.immediate(); + tr_base.project_index(bcx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let lloffset = C_usize(bcx.ccx, offset as u64); + tr_base.project_index(bcx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = C_usize(bcx.ccx, offset as u64); + let lllen = tr_base.len(bcx.ccx); + let llindex = bcx.sub(lllen, lloffset); + tr_base.project_index(bcx, llindex) + } + mir::ProjectionElem::Subslice { from, to } => { + let mut subslice = tr_base.project_index(bcx, + C_usize(bcx.ccx, from as u64)); + let projected_ty = PlaceTy::Ty { ty: tr_base.layout.ty } + .projection_ty(tcx, &projection.elem).to_ty(bcx.tcx()); + subslice.layout = bcx.ccx.layout_of(self.monomorphize(&projected_ty)); + + if subslice.layout.is_unsized() { + assert!(tr_base.has_extra()); + subslice.llextra = bcx.sub(tr_base.llextra, + C_usize(bcx.ccx, (from as u64) + (to as u64))); + } + + // Cast the place pointer type to the new + // array or slice type (*[%_; new_len]). + subslice.llval = bcx.pointercast(subslice.llval, + subslice.layout.llvm_type(bcx.ccx).ptr_to()); + + subslice + } + mir::ProjectionElem::Downcast(_, v) => { + tr_base.project_downcast(bcx, v) + } + } + } + }; + debug!("trans_place(place={:?}) => {:?}", place, result); + result + } + + pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { + let tcx = self.ccx.tcx(); + let place_ty = place.ty(self.mir, tcx); + self.monomorphize(&place_ty.to_ty(tcx)) + } +} + diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 822431eba42f1..a93c0cea11869 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -11,32 +11,33 @@ use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::layout::{Layout, LayoutTyper}; -use rustc::mir::tcx::LvalueTy; +use rustc::ty::layout::{self, LayoutOf}; use rustc::mir; use rustc::middle::lang_items::ExchangeMallocFnLangItem; +use rustc_apfloat::{ieee, Float, Status, Round}; +use rustc_const_math::MAX_F32_PLUS_HALF_ULP; +use std::{u128, i128}; use base; use builder::Builder; use callee; -use common::{self, val_ty, C_bool, C_i32, C_null, C_usize, C_uint}; -use adt; -use machine; +use common::{self, val_ty}; +use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_null, C_usize, C_uint, C_uint_big}; +use consts; use monomorphize; use type_::Type; -use type_of; -use tvec; +use type_of::LayoutLlvmExt; use value::Value; use super::{MirContext, LocalRef}; use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; -use super::lvalue::LvalueRef; +use super::place::PlaceRef; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_rvalue(&mut self, bcx: Builder<'a, 'tcx>, - dest: LvalueRef<'tcx>, + dest: PlaceRef<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> Builder<'a, 'tcx> { @@ -48,18 +49,18 @@ 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, dest.alignment.to_align(), tr_operand); + tr_operand.val.store(&bcx, dest); bcx } - mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, cast_ty) => { - let cast_ty = self.monomorphize(&cast_ty); - - if common::type_is_fat_ptr(bcx.ccx, cast_ty) { + mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, _) => { + // The destination necessarily contains a fat pointer, so if + // it's a scalar pair, it's a fat pointer or newtype thereof. + if dest.layout.is_llvm_scalar_pair() { // 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, dest.alignment.to_align(), temp); + temp.val.store(&bcx, dest); return bcx; } @@ -68,10 +69,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // `CoerceUnsized` can be passed by a where-clause, // so the (generic) MIR may not be able to expand it. let operand = self.trans_operand(&bcx, source); - let operand = operand.pack_if_pair(&bcx); - let llref = match operand.val { - OperandValue::Pair(..) => bug!(), - OperandValue::Immediate(llval) => { + match operand.val { + OperandValue::Pair(..) | + OperandValue::Immediate(_) => { // unsize from an immediate structure. We don't // really need a temporary alloca here, but // avoiding it would require us to have @@ -79,107 +79,94 @@ 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 scratch = LvalueRef::alloca(&bcx, operand.ty, "__unsize_temp"); - base::store_ty(&bcx, llval, scratch.llval, scratch.alignment, operand.ty); - scratch + let scratch = PlaceRef::alloca(&bcx, operand.layout, "__unsize_temp"); + scratch.storage_live(&bcx); + operand.val.store(&bcx, scratch); + base::coerce_unsized_into(&bcx, scratch, dest); + scratch.storage_dead(&bcx); } OperandValue::Ref(llref, align) => { - LvalueRef::new_sized_ty(llref, operand.ty, align) + let source = PlaceRef::new_sized(llref, operand.layout, align); + base::coerce_unsized_into(&bcx, source, dest); } - }; - base::coerce_unsized_into(&bcx, &llref, &dest); + } bcx } mir::Rvalue::Repeat(ref elem, count) => { - let dest_ty = dest.ty.to_ty(bcx.tcx()); + let tr_elem = self.trans_operand(&bcx, elem); - // No need to inizialize memory of a zero-sized slice - if common::type_is_zero_size(bcx.ccx, dest_ty) { + // Do not generate the loop for zero-sized elements or empty arrays. + if dest.layout.is_zst() { return bcx; } - let tr_elem = self.trans_operand(&bcx, elem); - let size = count.as_u64(); - let size = C_usize(bcx.ccx, size); - let base = base::get_dataptr(&bcx, dest.llval); - let align = dest.alignment.to_align(); + let start = dest.project_index(&bcx, C_usize(bcx.ccx, 0)).llval; if let OperandValue::Immediate(v) = tr_elem.val { + let align = dest.alignment.non_abi() + .unwrap_or(tr_elem.layout.align); + let align = C_i32(bcx.ccx, align.abi() as i32); + let size = C_usize(bcx.ccx, dest.layout.size.bytes()); + // Use llvm.memset.p0i8.* to initialize all zero arrays if common::is_const_integral(v) && common::const_to_uint(v) == 0 { - let align = align.unwrap_or_else(|| bcx.ccx.align_of(tr_elem.ty)); - let align = C_i32(bcx.ccx, align as i32); - let ty = type_of::type_of(bcx.ccx, dest_ty); - let size = machine::llsize_of(bcx.ccx, ty); - let fill = C_uint(Type::i8(bcx.ccx), 0); - base::call_memset(&bcx, base, fill, size, align, false); + let fill = C_u8(bcx.ccx, 0); + base::call_memset(&bcx, start, fill, size, align, false); return bcx; } // Use llvm.memset.p0i8.* to initialize byte arrays + let v = base::from_immediate(&bcx, v); if common::val_ty(v) == Type::i8(bcx.ccx) { - let align = align.unwrap_or_else(|| bcx.ccx.align_of(tr_elem.ty)); - let align = C_i32(bcx.ccx, align as i32); - base::call_memset(&bcx, base, v, size, align, false); + base::call_memset(&bcx, start, v, size, align, false); return bcx; } } - tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot, loop_bb| { - self.store_operand(bcx, llslot, align, tr_elem); - bcx.br(loop_bb); - }) + let count = count.as_u64(); + let count = C_usize(bcx.ccx, count); + let end = dest.project_index(&bcx, count).llval; + + let header_bcx = bcx.build_sibling_block("repeat_loop_header"); + let body_bcx = bcx.build_sibling_block("repeat_loop_body"); + let next_bcx = bcx.build_sibling_block("repeat_loop_next"); + + bcx.br(header_bcx.llbb()); + let current = header_bcx.phi(common::val_ty(start), &[start], &[bcx.llbb()]); + + let keep_going = header_bcx.icmp(llvm::IntNE, current, end); + header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb()); + + tr_elem.val.store(&body_bcx, + PlaceRef::new_sized(current, tr_elem.layout, dest.alignment)); + + let next = body_bcx.inbounds_gep(current, &[C_usize(bcx.ccx, 1)]); + body_bcx.br(header_bcx.llbb()); + header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb()); + + next_bcx } mir::Rvalue::Aggregate(ref kind, ref operands) => { - match **kind { - mir::AggregateKind::Adt(adt_def, variant_index, substs, active_field_index) => { - let discr = adt_def.discriminant_for_variant(bcx.tcx(), variant_index) - .to_u128_unchecked() as u64; - let dest_ty = dest.ty.to_ty(bcx.tcx()); - adt::trans_set_discr(&bcx, dest_ty, dest.llval, discr); - for (i, operand) in operands.iter().enumerate() { - let op = self.trans_operand(&bcx, operand); - // Do not generate stores and GEPis for zero-sized fields. - if !common::type_is_zero_size(bcx.ccx, op.ty) { - let mut val = LvalueRef::new_sized( - dest.llval, dest.ty, dest.alignment); - let field_index = active_field_index.unwrap_or(i); - val.ty = LvalueTy::Downcast { - adt_def, - substs: self.monomorphize(&substs), - variant_index, - }; - let (lldest_i, align) = val.trans_field_ptr(&bcx, field_index); - self.store_operand(&bcx, lldest_i, align.to_align(), op); - } - } - }, - _ => { - // If this is a tuple or closure, we need to translate GEP indices. - let layout = bcx.ccx.layout_of(dest.ty.to_ty(bcx.tcx())); - let get_memory_index = |i| { - if let Layout::Univariant { ref variant, .. } = *layout { - adt::struct_llfields_index(variant, i) - } else { - i - } - }; - let alignment = dest.alignment; - for (i, operand) in operands.iter().enumerate() { - let op = self.trans_operand(&bcx, operand); - // Do not generate stores and GEPis for zero-sized fields. - if !common::type_is_zero_size(bcx.ccx, op.ty) { - // Note: perhaps this should be StructGep, but - // note that in some cases the values here will - // not be structs but arrays. - let i = get_memory_index(i); - let dest = bcx.gepi(dest.llval, &[0, i]); - self.store_operand(&bcx, dest, alignment.to_align(), op); - } + let (dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + dest.trans_set_discr(&bcx, variant_index); + if adt_def.is_enum() { + (dest.project_downcast(&bcx, variant_index), active_field_index) + } else { + (dest, active_field_index) } } + _ => (dest, None) + }; + for (i, operand) in operands.iter().enumerate() { + let op = self.trans_operand(&bcx, operand); + // Do not generate stores and GEPis for zero-sized fields. + if !op.layout.is_zst() { + let field_index = active_field_index.unwrap_or(i); + op.val.store(&bcx, dest.project_field(&bcx, field_index)); + } } bcx } @@ -187,7 +174,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { _ => { assert!(self.rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(&bcx, dest.llval, dest.alignment.to_align(), temp); + temp.val.store(&bcx, dest); bcx } } @@ -201,32 +188,32 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); match *rvalue { - mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { + mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { let operand = self.trans_operand(&bcx, source); debug!("cast operand is {:?}", operand); - let cast_ty = self.monomorphize(&cast_ty); + let cast = bcx.ccx.layout_of(self.monomorphize(&mir_cast_ty)); let val = match *kind { mir::CastKind::ReifyFnPointer => { - match operand.ty.sty { + match operand.layout.ty.sty { ty::TyFnDef(def_id, substs) => { OperandValue::Immediate( callee::resolve_and_get_fn(bcx.ccx, def_id, substs)) } _ => { - bug!("{} cannot be reified to a fn ptr", operand.ty) + bug!("{} cannot be reified to a fn ptr", operand.layout.ty) } } } mir::CastKind::ClosureFnPointer => { - match operand.ty.sty { + match operand.layout.ty.sty { ty::TyClosure(def_id, substs) => { let instance = monomorphize::resolve_closure( bcx.ccx.tcx(), def_id, substs, ty::ClosureKind::FnOnce); OperandValue::Immediate(callee::get_fn(bcx.ccx, instance)) } _ => { - bug!("{} cannot be cast to a fn ptr", operand.ty) + bug!("{} cannot be cast to a fn ptr", operand.layout.ty) } } } @@ -235,26 +222,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { operand.val } mir::CastKind::Unsize => { - // unsize targets other than to a fat pointer currently - // can't be operands. - assert!(common::type_is_fat_ptr(bcx.ccx, cast_ty)); - + assert!(cast.is_llvm_scalar_pair()); match operand.val { OperandValue::Pair(lldata, llextra) => { // unsize from a fat pointer - this is a // "trait-object-to-supertrait" coercion, for // example, // &'a fmt::Debug+Send => &'a fmt::Debug, - // So we need to pointercast the base to ensure - // the types match up. - let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, cast_ty); - let lldata = bcx.pointercast(lldata, llcast_ty); + + // HACK(eddyb) have to bitcast pointers + // until LLVM removes pointee types. + let lldata = bcx.pointercast(lldata, + cast.scalar_pair_element_llvm_type(bcx.ccx, 0)); OperandValue::Pair(lldata, llextra) } OperandValue::Immediate(lldata) => { // "standard" unsize let (lldata, llextra) = base::unsize_thin_ptr(&bcx, lldata, - operand.ty, cast_ty); + operand.layout.ty, cast.ty); OperandValue::Pair(lldata, llextra) } OperandValue::Ref(..) => { @@ -263,20 +248,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } } - mir::CastKind::Misc if common::type_is_fat_ptr(bcx.ccx, operand.ty) => { - let ll_cast_ty = type_of::immediate_type_of(bcx.ccx, cast_ty); - let ll_from_ty = type_of::immediate_type_of(bcx.ccx, operand.ty); - if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val { - if common::type_is_fat_ptr(bcx.ccx, cast_ty) { - let ll_cft = ll_cast_ty.field_types(); - let ll_fft = ll_from_ty.field_types(); - let data_cast = bcx.pointercast(data_ptr, ll_cft[0]); - assert_eq!(ll_cft[1].kind(), ll_fft[1].kind()); - OperandValue::Pair(data_cast, meta_ptr) + mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => { + if let OperandValue::Pair(data_ptr, meta) = operand.val { + if cast.is_llvm_scalar_pair() { + let data_cast = bcx.pointercast(data_ptr, + cast.scalar_pair_element_llvm_type(bcx.ccx, 0)); + OperandValue::Pair(data_cast, meta) } else { // cast to thin-ptr // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and // pointer-cast of that pointer to desired pointer type. - let llval = bcx.pointercast(data_ptr, ll_cast_ty); + let llcast_ty = cast.immediate_llvm_type(bcx.ccx); + let llval = bcx.pointercast(data_ptr, llcast_ty); OperandValue::Immediate(llval) } } else { @@ -284,30 +266,32 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } mir::CastKind::Misc => { - debug_assert!(common::type_is_immediate(bcx.ccx, cast_ty)); - let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); - let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - let ll_t_in = type_of::immediate_type_of(bcx.ccx, operand.ty); - let ll_t_out = type_of::immediate_type_of(bcx.ccx, cast_ty); + assert!(cast.is_llvm_immediate()); + let r_t_in = CastTy::from_ty(operand.layout.ty) + .expect("bad input type for cast"); + let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); + let ll_t_in = operand.layout.immediate_llvm_type(bcx.ccx); + let ll_t_out = cast.immediate_llvm_type(bcx.ccx); 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 - // convenient place to put the `assume`. - - base::call_assume(&bcx, bcx.icmp( - llvm::IntULE, - llval, - C_uint(common::val_ty(llval), max) - )); - } - signed - } else { - operand.ty.is_signed() - }; + let mut signed = false; + if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { + if let layout::Int(_, s) = scalar.value { + signed = s; + + if scalar.valid_range.end > scalar.valid_range.start { + // We want `table[e as usize]` to not + // have bound checks, and this is the most + // convenient place to put the `assume`. + + base::call_assume(&bcx, bcx.icmp( + llvm::IntULE, + llval, + C_uint_big(ll_t_in, scalar.valid_range.end) + )); + } + } + } let newval = match (r_t_in, r_t_out) { (CastTy::Int(_), CastTy::Int(_)) => { @@ -333,57 +317,49 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.ptrtoint(llval, ll_t_out), (CastTy::Int(_), CastTy::Ptr(_)) => bcx.inttoptr(llval, ll_t_out), - (CastTy::Int(_), CastTy::Float) if signed => - bcx.sitofp(llval, ll_t_out), (CastTy::Int(_), CastTy::Float) => - bcx.uitofp(llval, ll_t_out), + cast_int_to_float(&bcx, signed, llval, ll_t_in, ll_t_out), (CastTy::Float, CastTy::Int(IntTy::I)) => - bcx.fptosi(llval, ll_t_out), + cast_float_to_int(&bcx, true, llval, ll_t_in, ll_t_out), (CastTy::Float, CastTy::Int(_)) => - bcx.fptoui(llval, ll_t_out), - _ => bug!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty) + cast_float_to_int(&bcx, false, llval, ll_t_in, ll_t_out), + _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty) }; OperandValue::Immediate(newval) } }; - let operand = OperandRef { + (bcx, OperandRef { val, - ty: cast_ty - }; - (bcx, operand) + layout: cast + }) } - mir::Rvalue::Ref(_, bk, ref lvalue) => { - let tr_lvalue = self.trans_lvalue(&bcx, lvalue); + mir::Rvalue::Ref(_, bk, ref place) => { + let tr_place = self.trans_place(&bcx, place); - let ty = tr_lvalue.ty.to_ty(bcx.tcx()); - let ref_ty = bcx.tcx().mk_ref( - bcx.tcx().types.re_erased, - ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() } - ); + let ty = tr_place.layout.ty; - // Note: lvalues are indirect, so storing the `llval` into the + // Note: places are indirect, so storing the `llval` into the // destination effectively creates a reference. - let operand = if bcx.ccx.shared().type_is_sized(ty) { - OperandRef { - val: OperandValue::Immediate(tr_lvalue.llval), - ty: ref_ty, - } + let val = if !bcx.ccx.shared().type_has_metadata(ty) { + OperandValue::Immediate(tr_place.llval) } else { - OperandRef { - val: OperandValue::Pair(tr_lvalue.llval, - tr_lvalue.llextra), - ty: ref_ty, - } + OperandValue::Pair(tr_place.llval, tr_place.llextra) }; - (bcx, operand) + (bcx, OperandRef { + val, + layout: self.ccx.layout_of(self.ccx.tcx().mk_ref( + self.ccx.tcx().types.re_erased, + ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() } + )), + }) } - mir::Rvalue::Len(ref lvalue) => { - let size = self.evaluate_array_len(&bcx, lvalue); + mir::Rvalue::Len(ref place) => { + let size = self.evaluate_array_len(&bcx, place); let operand = OperandRef { val: OperandValue::Immediate(size), - ty: bcx.tcx().types.usize, + layout: bcx.ccx.layout_of(bcx.tcx().types.usize), }; (bcx, operand) } @@ -391,26 +367,26 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { let lhs = self.trans_operand(&bcx, lhs); let rhs = self.trans_operand(&bcx, rhs); - let llresult = if common::type_is_fat_ptr(bcx.ccx, lhs.ty) { - match (lhs.val, rhs.val) { - (OperandValue::Pair(lhs_addr, lhs_extra), - OperandValue::Pair(rhs_addr, rhs_extra)) => { - self.trans_fat_ptr_binop(&bcx, op, - lhs_addr, lhs_extra, - rhs_addr, rhs_extra, - lhs.ty) - } - _ => bug!() + let llresult = match (lhs.val, rhs.val) { + (OperandValue::Pair(lhs_addr, lhs_extra), + OperandValue::Pair(rhs_addr, rhs_extra)) => { + self.trans_fat_ptr_binop(&bcx, op, + lhs_addr, lhs_extra, + rhs_addr, rhs_extra, + lhs.layout.ty) } - } else { - self.trans_scalar_binop(&bcx, op, - lhs.immediate(), rhs.immediate(), - lhs.ty) + (OperandValue::Immediate(lhs_val), + OperandValue::Immediate(rhs_val)) => { + self.trans_scalar_binop(&bcx, op, lhs_val, rhs_val, lhs.layout.ty) + } + + _ => bug!() }; let operand = OperandRef { val: OperandValue::Immediate(llresult), - ty: op.ty(bcx.tcx(), lhs.ty, rhs.ty), + layout: bcx.ccx.layout_of( + op.ty(bcx.tcx(), lhs.layout.ty, rhs.layout.ty)), }; (bcx, operand) } @@ -419,12 +395,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let rhs = self.trans_operand(&bcx, rhs); let result = self.trans_scalar_checked_binop(&bcx, op, lhs.immediate(), rhs.immediate(), - lhs.ty); - let val_ty = op.ty(bcx.tcx(), lhs.ty, rhs.ty); + lhs.layout.ty); + let val_ty = op.ty(bcx.tcx(), lhs.layout.ty, rhs.layout.ty); let operand_ty = bcx.tcx().intern_tup(&[val_ty, bcx.tcx().types.bool], false); let operand = OperandRef { val: result, - ty: operand_ty + layout: bcx.ccx.layout_of(operand_ty) }; (bcx, operand) @@ -433,7 +409,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.trans_operand(&bcx, operand); let lloperand = operand.immediate(); - let is_float = operand.ty.is_fp(); + let is_float = operand.layout.ty.is_fp(); let llval = match op { mir::UnOp::Not => bcx.not(lloperand), mir::UnOp::Neg => if is_float { @@ -444,47 +420,43 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; (bcx, OperandRef { val: OperandValue::Immediate(llval), - ty: operand.ty, + layout: operand.layout, }) } - mir::Rvalue::Discriminant(ref lvalue) => { - let discr_lvalue = self.trans_lvalue(&bcx, lvalue); - let enum_ty = discr_lvalue.ty.to_ty(bcx.tcx()); + mir::Rvalue::Discriminant(ref place) => { let discr_ty = rvalue.ty(&*self.mir, bcx.tcx()); - let discr_type = type_of::immediate_type_of(bcx.ccx, discr_ty); - let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval, - discr_lvalue.alignment, Some(discr_type), true); + let discr = self.trans_place(&bcx, place) + .trans_get_discr(&bcx, discr_ty); (bcx, OperandRef { val: OperandValue::Immediate(discr), - ty: discr_ty + layout: self.ccx.layout_of(discr_ty) }) } mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { assert!(bcx.ccx.shared().type_is_sized(ty)); - let val = C_usize(bcx.ccx, bcx.ccx.size_of(ty)); + let val = C_usize(bcx.ccx, bcx.ccx.size_of(ty).bytes()); let tcx = bcx.tcx(); (bcx, OperandRef { val: OperandValue::Immediate(val), - ty: tcx.types.usize, + layout: self.ccx.layout_of(tcx.types.usize), }) } mir::Rvalue::NullaryOp(mir::NullOp::Box, content_ty) => { let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); - let llty = type_of::type_of(bcx.ccx, content_ty); - let llsize = machine::llsize_of(bcx.ccx, llty); - let align = bcx.ccx.align_of(content_ty); - let llalign = C_usize(bcx.ccx, align as u64); - let llty_ptr = llty.ptr_to(); - let box_ty = bcx.tcx().mk_box(content_ty); + let (size, align) = bcx.ccx.size_and_align_of(content_ty); + let llsize = C_usize(bcx.ccx, size.bytes()); + let llalign = C_usize(bcx.ccx, align.abi()); + let box_layout = bcx.ccx.layout_of(bcx.tcx().mk_box(content_ty)); + let llty_ptr = box_layout.llvm_type(bcx.ccx); // Allocate space: let def_id = match bcx.tcx().lang_items().require(ExchangeMallocFnLangItem) { Ok(id) => id, Err(s) => { - bcx.sess().fatal(&format!("allocation of `{}` {}", box_ty, s)); + bcx.sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); } }; let instance = ty::Instance::mono(bcx.tcx(), def_id); @@ -493,7 +465,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let operand = OperandRef { val: OperandValue::Immediate(val), - ty: box_ty, + layout: box_layout, }; (bcx, operand) } @@ -506,29 +478,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. let ty = rvalue.ty(self.mir, self.ccx.tcx()); - (bcx, OperandRef::new_zst(self.ccx, self.monomorphize(&ty))) + (bcx, OperandRef::new_zst(self.ccx, + self.ccx.layout_of(self.monomorphize(&ty)))) } } } fn evaluate_array_len(&mut self, bcx: &Builder<'a, 'tcx>, - lvalue: &mir::Lvalue<'tcx>) -> ValueRef + place: &mir::Place<'tcx>) -> ValueRef { // ZST are passed as operands and require special handling - // because trans_lvalue() panics if Local is operand. - if let mir::Lvalue::Local(index) = *lvalue { + // because trans_place() panics if Local is operand. + if let mir::Place::Local(index) = *place { if let LocalRef::Operand(Some(op)) = self.locals[index] { - if common::type_is_zero_size(bcx.ccx, op.ty) { - if let ty::TyArray(_, n) = op.ty.sty { - let n = n.val.to_const_int().unwrap().to_u64().unwrap(); - return common::C_usize(bcx.ccx, n); - } + if let ty::TyArray(_, n) = op.layout.ty.sty { + let n = n.val.to_const_int().unwrap().to_u64().unwrap(); + return common::C_usize(bcx.ccx, n); } } } // use common size calculation for non zero-sized types - let tr_value = self.trans_lvalue(&bcx, lvalue); + let tr_value = self.trans_place(&bcx, place); return tr_value.len(bcx.ccx); } @@ -728,7 +699,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Rvalue::Aggregate(..) => { let ty = rvalue.ty(self.mir, self.ccx.tcx()); let ty = self.monomorphize(&ty); - common::type_is_zero_size(self.ccx, ty) + self.ccx.layout_of(ty).is_zst() } } @@ -815,3 +786,158 @@ fn get_overflow_intrinsic(oop: OverflowOp, bcx: &Builder, ty: Ty) -> ValueRef { bcx.ccx.get_intrinsic(&name) } + +fn cast_int_to_float(bcx: &Builder, + signed: bool, + x: ValueRef, + int_ty: Type, + float_ty: Type) -> ValueRef { + // Most integer types, even i128, fit into [-f32::MAX, f32::MAX] after rounding. + // It's only u128 -> f32 that can cause overflows (i.e., should yield infinity). + // LLVM's uitofp produces undef in those cases, so we manually check for that case. + let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32; + if is_u128_to_f32 { + // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity, + // and for everything else LLVM's uitofp works just fine. + let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP); + let overflow = bcx.icmp(llvm::IntUGE, x, max); + let infinity_bits = C_u32(bcx.ccx, ieee::Single::INFINITY.to_bits() as u32); + let infinity = consts::bitcast(infinity_bits, float_ty); + bcx.select(overflow, infinity, bcx.uitofp(x, float_ty)) + } else { + if signed { + bcx.sitofp(x, float_ty) + } else { + bcx.uitofp(x, float_ty) + } + } +} + +fn cast_float_to_int(bcx: &Builder, + signed: bool, + x: ValueRef, + float_ty: Type, + int_ty: Type) -> ValueRef { + let fptosui_result = if signed { + bcx.fptosi(x, int_ty) + } else { + bcx.fptoui(x, int_ty) + }; + + if !bcx.sess().opts.debugging_opts.saturating_float_casts { + return fptosui_result; + } + // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the + // destination integer type after rounding towards zero. This `undef` value can cause UB in + // safe code (see issue #10184), so we implement a saturating conversion on top of it: + // Semantically, the mathematical value of the input is rounded towards zero to the next + // mathematical integer, and then the result is clamped into the range of the destination + // integer type. Positive and negative infinity are mapped to the maximum and minimum value of + // the destination integer type. NaN is mapped to 0. + // + // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to + // a value representable in int_ty. + // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. + // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. + // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly + // representable. Note that this only works if float_ty's exponent range is sufficently large. + // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 + // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. + // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because + // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). + // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. + fn compute_clamp_bounds(signed: bool, int_ty: Type) -> (u128, u128) { + let rounded_min = F::from_i128_r(int_min(signed, int_ty), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = F::from_u128_r(int_max(signed, int_ty), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + } + fn int_max(signed: bool, int_ty: Type) -> u128 { + let shift_amount = 128 - int_ty.int_width(); + if signed { + i128::MAX as u128 >> shift_amount + } else { + u128::MAX >> shift_amount + } + } + fn int_min(signed: bool, int_ty: Type) -> i128 { + if signed { + i128::MIN >> (128 - int_ty.int_width()) + } else { + 0 + } + } + let float_bits_to_llval = |bits| { + let bits_llval = match float_ty.float_width() { + 32 => C_u32(bcx.ccx, bits as u32), + 64 => C_u64(bcx.ccx, bits as u64), + n => bug!("unsupported float width {}", n), + }; + consts::bitcast(bits_llval, float_ty) + }; + let (f_min, f_max) = match float_ty.float_width() { + 32 => compute_clamp_bounds::(signed, int_ty), + 64 => compute_clamp_bounds::(signed, int_ty), + n => bug!("unsupported float width {}", n), + }; + let f_min = float_bits_to_llval(f_min); + let f_max = float_bits_to_llval(f_max); + // To implement saturation, we perform the following steps: + // + // 1. Cast x to an integer with fpto[su]i. This may result in undef. + // 2. Compare x to f_min and f_max, and use the comparison results to select: + // a) int_ty::MIN if x < f_min or x is NaN + // b) int_ty::MAX if x > f_max + // c) the result of fpto[su]i otherwise + // 3. If x is NaN, return 0.0, otherwise return the result of step 2. + // + // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the + // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of + // undef does not introduce any non-determinism either. + // More importantly, the above procedure correctly implements saturating conversion. + // Proof (sketch): + // If x is NaN, 0 is returned by definition. + // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. + // This yields three cases to consider: + // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with + // saturating conversion for inputs in that range. + // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded + // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger + // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX + // is correct. + // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals + // int_ty::MIN and therefore the return value of int_ty::MIN is correct. + // QED. + + // Step 1 was already performed above. + + // Step 2: We use two comparisons and two selects, with %s1 being the result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the + // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 + // becomes int_ty::MIN if x is NaN. + // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a + // negation, and the negation can be merged into the select. Therefore, it not necessarily any + // more expensive than a ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does perform them. + let less_or_nan = bcx.fcmp(llvm::RealULT, x, f_min); + let greater = bcx.fcmp(llvm::RealOGT, x, f_max); + let int_max = C_uint_big(int_ty, int_max(signed, int_ty)); + let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128); + let s0 = bcx.select(less_or_nan, int_min, fptosui_result); + let s1 = bcx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + bcx.select(bcx.fcmp(llvm::RealOEQ, x, x), s1, C_uint(int_ty, 0)) + } else { + s1 + } +} diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index bbf661ae9a735..e0ca5dcc9d082 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -10,14 +10,11 @@ use rustc::mir; -use base; use asm; -use common; use builder::Builder; use super::MirContext; use super::LocalRef; -use super::super::adt; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_statement(&mut self, @@ -28,10 +25,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { self.set_debug_loc(&bcx, statement.source_info); match statement.kind { - mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - if let mir::Lvalue::Local(index) = *lvalue { + mir::StatementKind::Assign(ref place, ref rvalue) => { + if let mir::Place::Local(index) = *place { match self.locals[index] { - LocalRef::Lvalue(tr_dest) => { + LocalRef::Place(tr_dest) => { self.trans_rvalue(bcx, tr_dest, rvalue) } LocalRef::Operand(None) => { @@ -39,44 +36,43 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { self.locals[index] = LocalRef::Operand(Some(operand)); bcx } - LocalRef::Operand(Some(_)) => { - let ty = self.monomorphized_lvalue_ty(lvalue); - - if !common::type_is_zero_size(bcx.ccx, ty) { + LocalRef::Operand(Some(op)) => { + if !op.layout.is_zst() { span_bug!(statement.source_info.span, "operand {:?} already assigned", rvalue); - } else { - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bcx, rvalue).0 } + + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we translate the operand + self.trans_rvalue_operand(bcx, rvalue).0 } } } else { - let tr_dest = self.trans_lvalue(&bcx, lvalue); + let tr_dest = self.trans_place(&bcx, place); self.trans_rvalue(bcx, tr_dest, rvalue) } } - mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => { - let ty = self.monomorphized_lvalue_ty(lvalue); - let lvalue_transed = self.trans_lvalue(&bcx, lvalue); - adt::trans_set_discr(&bcx, - ty, - lvalue_transed.llval, - variant_index as u64); + mir::StatementKind::SetDiscriminant{ref place, variant_index} => { + self.trans_place(&bcx, place) + .trans_set_discr(&bcx, variant_index); bcx } mir::StatementKind::StorageLive(local) => { - self.trans_storage_liveness(bcx, local, base::Lifetime::Start) + if let LocalRef::Place(tr_place) = self.locals[local] { + tr_place.storage_live(&bcx); + } + bcx } mir::StatementKind::StorageDead(local) => { - self.trans_storage_liveness(bcx, local, base::Lifetime::End) + if let LocalRef::Place(tr_place) = self.locals[local] { + tr_place.storage_dead(&bcx); + } + bcx } mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { let outputs = outputs.iter().map(|output| { - let lvalue = self.trans_lvalue(&bcx, output); - (lvalue.llval, lvalue.ty.to_ty(bcx.tcx())) + self.trans_place(&bcx, output) }).collect(); let input_vals = inputs.iter().map(|input| { @@ -91,15 +87,4 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::StatementKind::Nop => bcx, } } - - fn trans_storage_liveness(&self, - bcx: Builder<'a, 'tcx>, - index: mir::Local, - intrinsic: base::Lifetime) - -> Builder<'a, 'tcx> { - if let LocalRef::Lvalue(tr_lval) = self.locals[index] { - intrinsic.call(&bcx, tr_lval.llval); - } - bcx - } } diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs deleted file mode 100644 index 62ccd55b483ca..0000000000000 --- a/src/librustc_trans/monomorphize.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use abi::Abi; -use common::*; - -use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::DropInPlaceFnLangItem; -use rustc::traits; -use rustc::ty::adjustment::CustomCoerceUnsized; -use rustc::ty::subst::{Kind, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt}; - -use syntax::codemap::DUMMY_SP; - -pub use rustc::ty::Instance; - -fn fn_once_adapter_instance<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - closure_did: DefId, - substs: ty::ClosureSubsts<'tcx>, - ) -> Instance<'tcx> { - debug!("fn_once_adapter_shim({:?}, {:?})", - closure_did, - substs); - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); - let call_once = tcx.associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap().def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once }; - - let self_ty = tcx.mk_closure_from_closure_substs( - closure_did, substs); - - let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.inputs().len(), 1); - let substs = tcx.mk_substs([ - Kind::from(self_ty), - Kind::from(sig.inputs()[0]), - ].iter().cloned()); - - debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); - Instance { def, substs } -} - -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. - 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. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - -pub fn resolve_closure<'a, 'tcx> ( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - requested_kind: ty::ClosureKind) - -> Instance<'tcx> -{ - let actual_kind = tcx.closure_kind(def_id); - - match needs_fn_once_adapter_shim(actual_kind, requested_kind) { - Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), - _ => Instance::new(def_id, substs.substs) - } -} - -fn resolve_associated_item<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - trait_item: &ty::AssociatedItem, - trait_id: DefId, - rcvr_substs: &'tcx Substs<'tcx> -) -> Instance<'tcx> { - let def_id = trait_item.def_id; - debug!("resolve_associated_item(trait_item={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, trait_id, rcvr_substs); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.trans_fulfill_obligation( - DUMMY_SP, ty::ParamEnv::empty(traits::Reveal::All), ty::Binder(trait_ref)); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - traits::VtableImpl(impl_data) => { - let (def_id, substs) = traits::find_associated_item( - tcx, trait_item, rcvr_substs, &impl_data); - let substs = tcx.erase_regions(&substs); - ty::Instance::new(def_id, substs) - } - traits::VtableGenerator(closure_data) => { - Instance { - def: ty::InstanceDef::Item(closure_data.closure_def_id), - substs: closure_data.substs.substs - } - } - traits::VtableClosure(closure_data) => { - let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap(); - resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs, - trait_closure_kind) - } - traits::VtableFnPointer(ref data) => { - Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs - } - } - traits::VtableObject(ref data) => { - let index = tcx.get_vtable_index_of_object_method(data, def_id); - Instance { - def: ty::InstanceDef::Virtual(def_id, index), - substs: rcvr_substs - } - } - traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items().clone_trait() => { - Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), - substs: rcvr_substs - } - } - _ => { - bug!("static call to invalid vtable: {:?}", vtbl) - } - } -} - -/// The point where linking happens. Resolve a (def_id, substs) -/// pair to an instance. -pub fn resolve<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx> -) -> Instance<'tcx> { - debug!("resolve(def_id={:?}, substs={:?})", - def_id, substs); - let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { - debug!(" => associated item, attempting to find impl"); - let item = tcx.associated_item(def_id); - resolve_associated_item(tcx, &item, trait_def_id, substs) - } else { - let item_type = def_ty(tcx, def_id, substs); - let def = match item_type.sty { - ty::TyFnDef(..) if { - let f = item_type.fn_sig(tcx); - f.abi() == Abi::RustIntrinsic || - f.abi() == Abi::PlatformIntrinsic - } => - { - debug!(" => intrinsic"); - ty::InstanceDef::Intrinsic(def_id) - } - _ => { - if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - let ty = substs.type_at(0); - if type_needs_drop(tcx, ty) { - debug!(" => nontrivial drop glue"); - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) - } - } else { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) - } - } - }; - Instance { def, substs } - }; - debug!("resolve(def_id={:?}, substs={:?}) = {}", - def_id, substs, result); - result -} - -pub fn resolve_drop_in_place<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>) - -> ty::Instance<'tcx> -{ - let def_id = tcx.require_lang_item(DropInPlaceFnLangItem); - let substs = tcx.intern_substs(&[Kind::from(ty)]); - resolve(tcx, def_id, substs) -} - -pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>) - -> CustomCoerceUnsized { - let trait_ref = ty::Binder(ty::TraitRef { - def_id: tcx.lang_items().coerce_unsized_trait().unwrap(), - substs: tcx.mk_substs_trait(source_ty, &[target_ty]) - }); - - match tcx.trans_fulfill_obligation( - DUMMY_SP, ty::ParamEnv::empty(traits::Reveal::All), trait_ref) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { - tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() - } - vtable => { - bug!("invalid CoerceUnsized vtable: {:?}", vtable); - } - } -} - -/// Returns the normalized type of a struct field -pub fn field_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_substs: &Substs<'tcx>, - f: &'tcx ty::FieldDef) - -> Ty<'tcx> -{ - tcx.normalize_associated_type(&f.ty(tcx, param_substs)) -} - diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 7c29186f4657f..03c0f13e2f5f7 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -104,21 +104,17 @@ use collector::InliningMap; use common; -use rustc::dep_graph::{DepNode, WorkProductId}; +use rustc::dep_graph::WorkProductId; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::middle::trans::{Linkage, Visibility}; -use rustc::ich::Fingerprint; -use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER; use rustc::ty::{self, TyCtxt, InstanceDef}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::StableHasher; use std::collections::hash_map::Entry; -use std::hash::Hash; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; -use trans_item::{TransItem, TransItemExt, InstantiationMode}; +use trans_item::{TransItem, BaseTransItemExt, TransItemExt, InstantiationMode}; pub use rustc::middle::trans::CodegenUnit; @@ -151,23 +147,6 @@ pub trait CodegenUnitExt<'tcx> { WorkProductId::from_cgu_name(self.name()) } - fn work_product_dep_node(&self) -> DepNode { - self.work_product_id().to_dep_node() - } - - fn compute_symbol_name_hash<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> u64 { - let mut state: StableHasher = StableHasher::new(); - let all_items = self.items_in_deterministic_order(tcx); - for (item, (linkage, visibility)) in all_items { - let symbol_name = item.symbol_name(tcx); - symbol_name.len().hash(&mut state); - symbol_name.hash(&mut state); - linkage.hash(&mut state); - visibility.hash(&mut state); - } - state.finish().to_smaller_hash() - } - fn items_in_deterministic_order<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<(TransItem<'tcx>, @@ -180,10 +159,27 @@ pub trait CodegenUnitExt<'tcx> { fn item_sort_key<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item: TransItem<'tcx>) -> ItemSortKey { ItemSortKey(match item { - TransItem::Fn(instance) => { - tcx.hir.as_local_node_id(instance.def_id()) + TransItem::Fn(ref instance) => { + match instance.def { + // We only want to take NodeIds of user-defined + // instances into account. The others don't matter for + // the codegen tests and can even make item order + // unstable. + InstanceDef::Item(def_id) => { + tcx.hir.as_local_node_id(def_id) + } + InstanceDef::Intrinsic(..) | + InstanceDef::FnPtrShim(..) | + InstanceDef::Virtual(..) | + InstanceDef::ClosureOnceShim { .. } | + InstanceDef::DropGlue(..) | + InstanceDef::CloneShim(..) => { + None + } + } } - TransItem::Static(node_id) | TransItem::GlobalAsm(node_id) => { + TransItem::Static(node_id) | + TransItem::GlobalAsm(node_id) => { Some(node_id) } }, item.symbol_name(tcx)) @@ -253,14 +249,6 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cgu1.name().cmp(cgu2.name()) }); - if tcx.sess.opts.enable_dep_node_debug_strs() { - for cgu in &result { - let dep_node = cgu.work_product_dep_node(); - tcx.dep_graph.register_dep_node_debug_str(dep_node, - || cgu.name().to_string()); - } - } - result } @@ -296,75 +284,74 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut internalization_candidates = FxHashSet(); for trans_item in trans_items { - let is_root = trans_item.instantiation_mode(tcx) == InstantiationMode::GloballyShared; + match trans_item.instantiation_mode(tcx) { + InstantiationMode::GloballyShared { .. } => {} + InstantiationMode::LocalCopy => continue, + } - if is_root { - let characteristic_def_id = characteristic_def_id_of_trans_item(tcx, trans_item); - let is_volatile = is_incremental_build && - trans_item.is_generic_fn(); + let characteristic_def_id = characteristic_def_id_of_trans_item(tcx, trans_item); + 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), - None => Symbol::intern(FALLBACK_CODEGEN_UNIT).as_str(), - }; + let codegen_unit_name = match characteristic_def_id { + Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile), + None => Symbol::intern(FALLBACK_CODEGEN_UNIT).as_str(), + }; - let make_codegen_unit = || { - CodegenUnit::new(codegen_unit_name.clone()) - }; + let make_codegen_unit = || { + CodegenUnit::new(codegen_unit_name.clone()) + }; - let codegen_unit = codegen_units.entry(codegen_unit_name.clone()) - .or_insert_with(make_codegen_unit); - - let (linkage, visibility) = match trans_item.explicit_linkage(tcx) { - Some(explicit_linkage) => (explicit_linkage, Visibility::Default), - None => { - match trans_item { - TransItem::Fn(ref instance) => { - let visibility = match instance.def { - InstanceDef::Item(def_id) => { - if def_id.is_local() { - if tcx.is_exported_symbol(def_id) { - Visibility::Default - } else { - internalization_candidates.insert(trans_item); - Visibility::Hidden - } + let codegen_unit = codegen_units.entry(codegen_unit_name.clone()) + .or_insert_with(make_codegen_unit); + + let (linkage, visibility) = match trans_item.explicit_linkage(tcx) { + Some(explicit_linkage) => (explicit_linkage, Visibility::Default), + None => { + match trans_item { + TransItem::Fn(ref instance) => { + let visibility = match instance.def { + InstanceDef::Item(def_id) => { + if def_id.is_local() { + if tcx.is_exported_symbol(def_id) { + Visibility::Default } else { - internalization_candidates.insert(trans_item); Visibility::Hidden } + } else { + Visibility::Hidden } - InstanceDef::FnPtrShim(..) | - InstanceDef::Virtual(..) | - InstanceDef::Intrinsic(..) | - InstanceDef::ClosureOnceShim { .. } | - InstanceDef::DropGlue(..) | - InstanceDef::CloneShim(..) => { - bug!("partitioning: Encountered unexpected - root translation item: {:?}", - trans_item) - } - }; - (Linkage::External, visibility) - } - TransItem::Static(node_id) | - TransItem::GlobalAsm(node_id) => { - let def_id = tcx.hir.local_def_id(node_id); - let visibility = if tcx.is_exported_symbol(def_id) { - Visibility::Default - } else { - internalization_candidates.insert(trans_item); + } + InstanceDef::FnPtrShim(..) | + InstanceDef::Virtual(..) | + InstanceDef::Intrinsic(..) | + InstanceDef::ClosureOnceShim { .. } | + InstanceDef::DropGlue(..) | + InstanceDef::CloneShim(..) => { Visibility::Hidden - }; - (Linkage::External, visibility) - } + } + }; + (Linkage::External, visibility) + } + TransItem::Static(node_id) | + TransItem::GlobalAsm(node_id) => { + let def_id = tcx.hir.local_def_id(node_id); + let visibility = if tcx.is_exported_symbol(def_id) { + Visibility::Default + } else { + Visibility::Hidden + }; + (Linkage::External, visibility) } } - }; - - codegen_unit.items_mut().insert(trans_item, (linkage, visibility)); - roots.insert(trans_item); + } + }; + if visibility == Visibility::Hidden { + internalization_candidates.insert(trans_item); } + + codegen_unit.items_mut().insert(trans_item, (linkage, visibility)); + roots.insert(trans_item); } // always ensure we have at least one CGU; otherwise, if we have a @@ -407,15 +394,6 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< for (index, cgu) in codegen_units.iter_mut().enumerate() { cgu.set_name(numbered_codegen_unit_name(crate_name, index)); } - - // If the initial partitioning contained less than target_cgu_count to begin - // with, we won't have enough codegen units here, so add a empty units until - // we reach the target count - while codegen_units.len() < target_cgu_count { - let index = codegen_units.len(); - let name = numbered_codegen_unit_name(crate_name, index); - codegen_units.push(CodegenUnit::new(name)); - } } fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, @@ -643,7 +621,7 @@ fn compute_codegen_unit_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn numbered_codegen_unit_name(crate_name: &str, index: usize) -> InternedString { - Symbol::intern(&format!("{}{}{}", crate_name, NUMBERED_CODEGEN_UNIT_MARKER, index)).as_str() + Symbol::intern(&format!("{}{}", crate_name, index)).as_str() } fn debug_dump<'a, 'b, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_trans/time_graph.rs b/src/librustc_trans/time_graph.rs index ead6e43256129..a8502682a806b 100644 --- a/src/librustc_trans/time_graph.rs +++ b/src/librustc_trans/time_graph.rs @@ -9,21 +9,24 @@ // except according to those terms. use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; use std::marker::PhantomData; +use std::mem; use std::sync::{Arc, Mutex}; use std::time::Instant; -use std::io::prelude::*; -use std::fs::File; const OUTPUT_WIDTH_IN_PX: u64 = 1000; -const TIME_LINE_HEIGHT_IN_PX: u64 = 7; -const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 10; +const TIME_LINE_HEIGHT_IN_PX: u64 = 20; +const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30; #[derive(Clone)] struct Timing { start: Instant, end: Instant, work_package_kind: WorkPackageKind, + name: String, + events: Vec<(String, Instant)>, } #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] @@ -32,7 +35,7 @@ pub struct TimelineId(pub usize); #[derive(Clone)] struct PerThread { timings: Vec, - open_work_package: Option<(Instant, WorkPackageKind)>, + open_work_package: Option<(Instant, WorkPackageKind, String)>, } #[derive(Clone)] @@ -43,9 +46,14 @@ pub struct TimeGraph { #[derive(Clone, Copy)] pub struct WorkPackageKind(pub &'static [&'static str]); -pub struct RaiiToken { +pub struct Timeline { + token: Option, +} + +struct RaiiToken { graph: TimeGraph, timeline: TimelineId, + events: Vec<(String, Instant)>, // The token must not be Send: _marker: PhantomData<*const ()> } @@ -53,7 +61,7 @@ pub struct RaiiToken { impl Drop for RaiiToken { fn drop(&mut self) { - self.graph.end(self.timeline); + self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); } } @@ -66,7 +74,8 @@ impl TimeGraph { pub fn start(&self, timeline: TimelineId, - work_package_kind: WorkPackageKind) -> RaiiToken { + work_package_kind: WorkPackageKind, + name: &str) -> Timeline { { let mut table = self.data.lock().unwrap(); @@ -76,33 +85,36 @@ impl TimeGraph { }); assert!(data.open_work_package.is_none()); - data.open_work_package = Some((Instant::now(), work_package_kind)); + data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); } - RaiiToken { - graph: self.clone(), - timeline, - _marker: PhantomData, + Timeline { + token: Some(RaiiToken { + graph: self.clone(), + timeline, + events: Vec::new(), + _marker: PhantomData, + }), } } - fn end(&self, timeline: TimelineId) { + fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { let end = Instant::now(); let mut table = self.data.lock().unwrap(); let data = table.get_mut(&timeline).unwrap(); - if let Some((start, work_package_kind)) = data.open_work_package { + if let Some((start, work_package_kind, name)) = data.open_work_package.take() { data.timings.push(Timing { start, end, work_package_kind, + name, + events, }); } else { bug!("end timing without start?") } - - data.open_work_package = None; } pub fn dump(&self, output_filename: &str) { @@ -112,13 +124,13 @@ impl TimeGraph { assert!(data.open_work_package.is_none()); } - let mut timelines: Vec = + let mut threads: Vec = table.values().map(|data| data.clone()).collect(); - timelines.sort_by_key(|timeline| timeline.timings[0].start); + threads.sort_by_key(|timeline| timeline.timings[0].start); - let earliest_instant = timelines[0].timings[0].start; - let latest_instant = timelines.iter() + let earliest_instant = threads[0].timings[0].start; + let latest_instant = threads.iter() .map(|timeline| timeline.timings .last() .unwrap() @@ -129,16 +141,46 @@ impl TimeGraph { let mut file = File::create(format!("{}.html", output_filename)).unwrap(); - writeln!(file, "").unwrap(); - writeln!(file, "").unwrap(); - writeln!(file, "").unwrap(); + writeln!(file, " + + + + + +

+ ").unwrap(); + + let mut idx = 0; + for thread in threads.iter() { + for timing in &thread.timings { + let colors = timing.work_package_kind.0; + let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); + writeln!(file, "
", + idx, + colors[idx % colors.len()], + height).unwrap(); + idx += 1; + let max = distance(timing.start, timing.end); + for (i, &(ref event, time)) in timing.events.iter().enumerate() { + let i = i as u64; + let time = distance(timing.start, time); + let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); + writeln!(file, "{}", + at, + TIME_LINE_HEIGHT_IN_PX * i, + event).unwrap(); + } + writeln!(file, "
").unwrap(); + } + } + + writeln!(file, " + + + ").unwrap(); + } +} + +impl Timeline { + pub fn noop() -> Timeline { + Timeline { token: None } + } + + /// Record an event which happened at this moment on this timeline. + /// + /// Events are displayed in the eventual HTML output where you can click on + /// a particular timeline and it'll expand to all of the events that + /// happened on that timeline. This can then be used to drill into a + /// particular timeline and see what events are happening and taking the + /// most time. + pub fn record(&mut self, name: &str) { + if let Some(ref mut token) = self.token { + token.events.push((name.to_string(), Instant::now())); + } } } diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 526b61303e153..991f99e0f6c99 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -23,37 +23,23 @@ use common; use declare; use llvm; use monomorphize::Instance; +use type_of::LayoutLlvmExt; use rustc::hir; -use rustc::hir::def_id::DefId; use rustc::middle::trans::{Linkage, Visibility}; -use rustc::traits; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::subst::{Subst, Substs}; +use rustc::ty::{self, TyCtxt, TypeFoldable}; +use rustc::ty::layout::LayoutOf; use syntax::ast; use syntax::attr; use syntax_pos::Span; use syntax_pos::symbol::Symbol; -use type_of; -use std::fmt::{self, Write}; -use std::iter; +use std::fmt; pub use rustc::middle::trans::TransItem; -/// 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, -} - -pub trait TransItemExt<'a, 'tcx>: fmt::Debug { - fn as_trans_item(&self) -> &TransItem<'tcx>; +pub use rustc_trans_utils::trans_item::*; +pub use rustc_trans_utils::trans_item::TransItemExt as BaseTransItemExt; +pub trait TransItemExt<'a, 'tcx>: fmt::Debug + BaseTransItemExt<'a, 'tcx> { fn define(&self, ccx: &CrateContext<'a, 'tcx>) { debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", self.to_string(ccx.tcx()), @@ -151,24 +137,6 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { }.map(|node_id| tcx.hir.span(node_id)) } - fn instantiation_mode(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> InstantiationMode { - match *self.as_trans_item() { - TransItem::Fn(ref instance) => { - if self.explicit_linkage(tcx).is_none() && - common::requests_inline(tcx, instance) - { - InstantiationMode::LocalCopy - } else { - InstantiationMode::GloballyShared - } - } - TransItem::Static(..) => InstantiationMode::GloballyShared, - TransItem::GlobalAsm(..) => InstantiationMode::GloballyShared, - } - } - fn is_generic_fn(&self) -> bool { match *self.as_trans_item() { TransItem::Fn(ref instance) => { @@ -179,97 +147,6 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { } } - fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { - let def_id = match *self.as_trans_item() { - TransItem::Fn(ref instance) => instance.def_id(), - TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), - TransItem::GlobalAsm(..) => return None, - }; - - let attributes = tcx.get_attrs(def_id); - if let Some(name) = attr::first_attr_value_str_by_name(&attributes, "linkage") { - if let Some(linkage) = base::linkage_by_name(&name.as_str()) { - Some(linkage) - } else { - let span = tcx.hir.span_if_local(def_id); - if let Some(span) = span { - tcx.sess.span_fatal(span, "invalid linkage specified") - } else { - tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) - } - } - } else { - None - } - } - - /// Returns whether this instance is instantiable - whether it has no unsatisfied - /// predicates. - /// - /// In order to translate an item, all of its predicates must hold, because - /// otherwise the item does not make sense. Type-checking ensures that - /// the predicates of every item that is *used by* a valid item *do* - /// hold, so we can rely on that. - /// - /// However, we translate collector roots (reachable items) and functions - /// in vtables when they are seen, even if they are not used, and so they - /// might not be instantiable. For example, a programmer can define this - /// public function: - /// - /// pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone { - /// <&mut () as Clone>::clone(&s); - /// } - /// - /// That function can't be translated, because the method `<&mut () as Clone>::clone` - /// does not exist. Luckily for us, that function can't ever be used, - /// because that would require for `&'a mut (): Clone` to hold, so we - /// can just not emit any code, or even a linker reference for it. - /// - /// Similarly, if a vtable method has such a signature, and therefore can't - /// be used, we can just not emit it and have a placeholder (a null pointer, - /// which will never be accessed) in its place. - fn is_instantiable(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { - debug!("is_instantiable({:?})", self); - let (def_id, substs) = match *self.as_trans_item() { - TransItem::Fn(ref instance) => (instance.def_id(), instance.substs), - TransItem::Static(node_id) => (tcx.hir.local_def_id(node_id), Substs::empty()), - // global asm never has predicates - TransItem::GlobalAsm(..) => return true - }; - - let predicates = tcx.predicates_of(def_id).predicates.subst(tcx, substs); - traits::normalize_and_test_predicates(tcx, predicates) - } - - fn to_string(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { - let hir_map = &tcx.hir; - - return match *self.as_trans_item() { - TransItem::Fn(instance) => { - to_string_internal(tcx, "fn ", instance) - }, - TransItem::Static(node_id) => { - let def_id = hir_map.local_def_id(node_id); - let instance = Instance::new(def_id, tcx.intern_substs(&[])); - to_string_internal(tcx, "static ", instance) - }, - TransItem::GlobalAsm(..) => { - "global_asm".to_string() - } - }; - - fn to_string_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - prefix: &str, - instance: Instance<'tcx>) - -> String { - let mut result = String::with_capacity(32); - result.push_str(prefix); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_instance_as_string(instance, &mut result); - result - } - } - fn to_raw_string(&self) -> String { match *self.as_trans_item() { TransItem::Fn(instance) => { @@ -287,11 +164,7 @@ pub trait TransItemExt<'a, 'tcx>: fmt::Debug { } } -impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> { - fn as_trans_item(&self) -> &TransItem<'tcx> { - self - } -} +impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> {} fn predefine_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, node_id: ast::NodeId, @@ -301,7 +174,7 @@ fn predefine_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let def_id = ccx.tcx().hir.local_def_id(node_id); let instance = Instance::mono(ccx.tcx(), def_id); let ty = common::instance_ty(ccx.tcx(), &instance); - let llty = type_of::type_of(ccx, ty); + let llty = ccx.layout_of(ty).llvm_type(ccx); let g = declare::define_global(ccx, symbol_name, llty).unwrap_or_else(|| { ccx.sess().span_fatal(ccx.tcx().hir.span(node_id), @@ -358,236 +231,3 @@ fn predefine_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.instances().borrow_mut().insert(instance, lldecl); } - -//=----------------------------------------------------------------------------- -// TransItem String Keys -//=----------------------------------------------------------------------------- - -// The code below allows for producing a unique string key for a trans item. -// These keys are used by the handwritten auto-tests, so they need to be -// predictable and human-readable. -// -// Note: A lot of this could looks very similar to what's already in the -// ppaux module. It would be good to refactor things so we only have one -// parameterizable implementation for printing types. - -/// Same as `unique_type_name()` but with the result pushed onto the given -/// `output` parameter. -pub struct DefPathBasedNames<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool, -} - -impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool) - -> Self { - DefPathBasedNames { - tcx, - omit_disambiguators, - omit_local_crate_name, - } - } - - pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String) { - match t.sty { - ty::TyBool => output.push_str("bool"), - ty::TyChar => output.push_str("char"), - ty::TyStr => output.push_str("str"), - ty::TyNever => output.push_str("!"), - ty::TyInt(ast::IntTy::Is) => output.push_str("isize"), - ty::TyInt(ast::IntTy::I8) => output.push_str("i8"), - ty::TyInt(ast::IntTy::I16) => output.push_str("i16"), - ty::TyInt(ast::IntTy::I32) => output.push_str("i32"), - ty::TyInt(ast::IntTy::I64) => output.push_str("i64"), - ty::TyInt(ast::IntTy::I128) => output.push_str("i128"), - ty::TyUint(ast::UintTy::Us) => output.push_str("usize"), - ty::TyUint(ast::UintTy::U8) => output.push_str("u8"), - ty::TyUint(ast::UintTy::U16) => output.push_str("u16"), - ty::TyUint(ast::UintTy::U32) => output.push_str("u32"), - ty::TyUint(ast::UintTy::U64) => output.push_str("u64"), - ty::TyUint(ast::UintTy::U128) => output.push_str("u128"), - ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"), - ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"), - ty::TyAdt(adt_def, substs) => { - self.push_def_path(adt_def.did, output); - self.push_type_params(substs, iter::empty(), output); - }, - ty::TyTuple(component_types, _) => { - output.push('('); - for &component_type in component_types { - self.push_type_name(component_type, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { - output.push('*'); - match mutbl { - hir::MutImmutable => output.push_str("const "), - hir::MutMutable => output.push_str("mut "), - } - - self.push_type_name(inner_type, output); - }, - ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { - output.push('&'); - if mutbl == hir::MutMutable { - output.push_str("mut "); - } - - self.push_type_name(inner_type, output); - }, - ty::TyArray(inner_type, len) => { - output.push('['); - self.push_type_name(inner_type, output); - write!(output, "; {}", - len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); - output.push(']'); - }, - ty::TySlice(inner_type) => { - output.push('['); - self.push_type_name(inner_type, output); - output.push(']'); - }, - ty::TyDynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - self.push_def_path(principal.def_id(), output); - self.push_type_params(principal.skip_binder().substs, - trait_data.projection_bounds(), - output); - } - }, - ty::TyFnDef(..) | - ty::TyFnPtr(_) => { - let sig = t.fn_sig(self.tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } - - let abi = sig.abi(); - if abi != ::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); - - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - self.push_type_name(parameter_type, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_nil() { - output.push_str(" -> "); - self.push_type_name(sig.output(), output); - } - }, - ty::TyGenerator(def_id, ref closure_substs, _) | - ty::TyClosure(def_id, ref closure_substs) => { - self.push_def_path(def_id, output); - let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); - let substs = closure_substs.substs.truncate_to(self.tcx, generics); - self.push_type_params(substs, iter::empty(), output); - } - ty::TyError | - ty::TyInfer(_) | - ty::TyProjection(..) | - ty::TyParam(_) | - ty::TyAnon(..) => { - bug!("DefPathBasedNames: Trying to create type name for \ - unexpected type: {:?}", t); - } - } - } - - pub fn push_def_path(&self, - def_id: DefId, - output: &mut String) { - let def_path = self.tcx.def_path(def_id); - - // some_crate:: - if !(self.omit_local_crate_name && def_id.is_local()) { - output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); - output.push_str("::"); - } - - // foo::bar::ItemName:: - for part in self.tcx.def_path(def_id).data { - if self.omit_disambiguators { - write!(output, "{}::", part.data.as_interned_str()).unwrap(); - } else { - write!(output, "{}[{}]::", - part.data.as_interned_str(), - part.disambiguator).unwrap(); - } - } - - // remove final "::" - output.pop(); - output.pop(); - } - - fn push_type_params(&self, - substs: &Substs<'tcx>, - projections: I, - output: &mut String) - where I: Iterator> - { - let mut projections = projections.peekable(); - if substs.types().next().is_none() && projections.peek().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - self.push_type_name(type_parameter, output); - output.push_str(", "); - } - - for projection in projections { - let projection = projection.skip_binder(); - let name = &self.tcx.associated_item(projection.item_def_id).name.as_str(); - output.push_str(name); - output.push_str("="); - self.push_type_name(projection.ty, output); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } - - pub fn push_instance_as_string(&self, - instance: Instance<'tcx>, - output: &mut String) { - self.push_def_path(instance.def_id(), output); - self.push_type_params(instance.substs, iter::empty(), output); - } -} diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs deleted file mode 100644 index da4a4e55a67f4..0000000000000 --- a/src/librustc_trans/tvec.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use builder::Builder; -use llvm::{BasicBlockRef, ValueRef}; -use common::*; -use rustc::ty::Ty; - -pub fn slice_for_each<'a, 'tcx, F>( - bcx: &Builder<'a, 'tcx>, - data_ptr: ValueRef, - unit_ty: Ty<'tcx>, - len: ValueRef, - f: F -) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef, BasicBlockRef) { - // 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: &Builder, a, b| if zst { - bcx.add(a, b) - } else { - bcx.inbounds_gep(a, &[b]) - }; - - let body_bcx = bcx.build_sibling_block("slice_loop_body"); - let header_bcx = bcx.build_sibling_block("slice_loop_header"); - let next_bcx = bcx.build_sibling_block("slice_loop_next"); - - let start = if zst { - C_usize(bcx.ccx, 1) - } else { - data_ptr - }; - let end = add(&bcx, start, len); - - bcx.br(header_bcx.llbb()); - let current = header_bcx.phi(val_ty(start), &[start], &[bcx.llbb()]); - - let keep_going = header_bcx.icmp(llvm::IntNE, current, end); - header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb()); - - let next = add(&body_bcx, current, C_usize(bcx.ccx, 1)); - f(&body_bcx, if zst { data_ptr } else { current }, header_bcx.llbb()); - header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb()); - next_bcx -} diff --git a/src/librustc_trans/type_.rs b/src/librustc_trans/type_.rs index e5e532703d7d0..1775e53284963 100644 --- a/src/librustc_trans/type_.rs +++ b/src/librustc_trans/type_.rs @@ -17,7 +17,7 @@ use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; use context::CrateContext; use syntax::ast; -use rustc::ty::layout; +use rustc::ty::layout::{self, Align}; use std::ffi::CString; use std::fmt; @@ -66,10 +66,6 @@ impl Type { ty!(llvm::LLVMVoidTypeInContext(ccx.llcx())) } - pub fn nil(ccx: &CrateContext) -> Type { - Type::empty_struct(ccx) - } - pub fn metadata(ccx: &CrateContext) -> Type { ty!(llvm::LLVMRustMetadataTypeInContext(ccx.llcx())) } @@ -140,6 +136,15 @@ impl Type { } } + pub fn c_int(ccx: &CrateContext) -> Type { + match &ccx.tcx().sess.target.target.target_c_int_width[..] { + "16" => Type::i16(ccx), + "32" => Type::i32(ccx), + "64" => Type::i64(ccx), + width => bug!("Unsupported target_c_int_width: {}", width), + } + } + pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type { match t { ast::IntTy::Is => ccx.isize_ty(), @@ -193,9 +198,6 @@ impl Type { ty!(llvm::LLVMStructCreateNamed(ccx.llcx(), name.as_ptr())) } - pub fn empty_struct(ccx: &CrateContext) -> Type { - Type::struct_(ccx, &[], false) - } pub fn array(ty: &Type, len: u64) -> Type { ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) @@ -205,20 +207,6 @@ impl Type { ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) } - pub fn vec(ccx: &CrateContext, ty: &Type) -> Type { - Type::struct_(ccx, - &[Type::array(ty, 0), Type::isize(ccx)], - false) - } - - pub fn opaque_vec(ccx: &CrateContext) -> Type { - Type::vec(ccx, &Type::i8(ccx)) - } - - pub fn vtable_ptr(ccx: &CrateContext) -> Type { - Type::func(&[Type::i8p(ccx)], &Type::void(ccx)).ptr_to().ptr_to() - } - pub fn kind(&self) -> TypeKind { unsafe { llvm::LLVMRustGetTypeKind(self.to_ref()) @@ -250,19 +238,6 @@ impl Type { } } - pub fn field_types(&self) -> Vec { - unsafe { - let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as usize; - if n_elts == 0 { - return Vec::new(); - } - let mut elts = vec![Type { rf: ptr::null_mut() }; n_elts]; - llvm::LLVMGetStructElementTypes(self.to_ref(), - elts.as_mut_ptr() as *mut TypeRef); - elts - } - } - pub fn func_params(&self) -> Vec { unsafe { let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize; @@ -293,7 +268,6 @@ impl Type { pub fn from_integer(cx: &CrateContext, i: layout::Integer) -> Type { use rustc::ty::layout::Integer::*; match i { - I1 => Type::i1(cx), I8 => Type::i8(cx), I16 => Type::i16(cx), I32 => Type::i32(cx), @@ -301,4 +275,19 @@ impl Type { I128 => Type::i128(cx), } } + + /// Return a LLVM type that has at most the required alignment, + /// as a conservative approximation for unknown pointee types. + pub fn pointee_for_abi_align(ccx: &CrateContext, align: Align) -> Type { + if let Some(ity) = layout::Integer::for_abi_align(ccx, align) { + Type::from_integer(ccx, ity) + } else { + // FIXME(eddyb) We could find a better approximation here. + Type::i8(ccx) + } + } + + pub fn x86_mmx(ccx: &CrateContext) -> Type { + ty!(llvm::LLVMX86MMXTypeInContext(ccx.llcx())) + } } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index 992c74b9020c3..9ee4e7d4922e2 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -9,231 +9,499 @@ // except according to those terms. use abi::FnType; -use adt; use common::*; -use machine; +use rustc::hir; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::LayoutTyper; +use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; +use rustc_back::PanicStrategy; use trans_item::DefPathBasedNames; use type_::Type; -use syntax::ast; +use std::fmt::Write; -pub fn fat_ptr_base_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type { - match ty.sty { - ty::TyRef(_, ty::TypeAndMut { ty: t, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) if !ccx.shared().type_is_sized(t) => { - in_memory_type_of(ccx, t).ptr_to() +fn uncached_llvm_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + layout: TyLayout<'tcx>, + defer: &mut Option<(Type, TyLayout<'tcx>)>) + -> Type { + match layout.abi { + layout::Abi::Scalar(_) => bug!("handled elsewhere"), + layout::Abi::Vector => { + // LLVM has a separate type for 64-bit SIMD vectors on X86 called + // `x86_mmx` which is needed for some SIMD operations. As a bit of a + // hack (all SIMD definitions are super unstable anyway) we + // recognize any one-element SIMD vector as "this should be an + // x86_mmx" type. In general there shouldn't be a need for other + // one-element SIMD vectors, so it's assumed this won't clash with + // much else. + let use_x86_mmx = layout.fields.count() == 1 && + layout.size.bits() == 64 && + (ccx.sess().target.target.arch == "x86" || + ccx.sess().target.target.arch == "x86_64"); + if use_x86_mmx { + return Type::x86_mmx(ccx) + } else { + return Type::vector(&layout.field(ccx, 0).llvm_type(ccx), + layout.fields.count() as u64); + } } - ty::TyAdt(def, _) if def.is_box() => { - in_memory_type_of(ccx, ty.boxed_ty()).ptr_to() + layout::Abi::ScalarPair(..) => { + return Type::struct_(ccx, &[ + layout.scalar_pair_element_llvm_type(ccx, 0), + layout.scalar_pair_element_llvm_type(ccx, 1), + ], false); } - _ => bug!("expected fat ptr ty but got {:?}", ty) + layout::Abi::Uninhabited | + layout::Abi::Aggregate { .. } => {} } -} -pub fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type { - let unsized_part = ccx.tcx().struct_tail(ty); - match unsized_part.sty { - ty::TyStr | ty::TyArray(..) | ty::TySlice(_) => { - Type::uint_from_ty(ccx, ast::UintTy::Us) + let name = match layout.ty.sty { + ty::TyClosure(..) | + ty::TyGenerator(..) | + ty::TyAdt(..) | + ty::TyDynamic(..) | + ty::TyForeign(..) | + ty::TyStr => { + let mut name = String::with_capacity(32); + let printer = DefPathBasedNames::new(ccx.tcx(), true, true); + printer.push_type_name(layout.ty, &mut name); + match (&layout.ty.sty, &layout.variants) { + (&ty::TyAdt(def, _), &layout::Variants::Single { index }) => { + if def.is_enum() && !def.variants.is_empty() { + write!(&mut name, "::{}", def.variants[index].name).unwrap(); + } + } + _ => {} + } + Some(name) + } + _ => None + }; + + match layout.fields { + layout::FieldPlacement::Union(_) => { + let size = layout.size.bytes(); + let fill = Type::array(&Type::i8(ccx), size); + match name { + None => { + Type::struct_(ccx, &[fill], layout.is_packed()) + } + Some(ref name) => { + let mut llty = Type::named_struct(ccx, name); + llty.set_struct_body(&[fill], layout.is_packed()); + llty + } + } + } + layout::FieldPlacement::Array { count, .. } => { + Type::array(&layout.field(ccx, 0).llvm_type(ccx), count) + } + layout::FieldPlacement::Arbitrary { .. } => { + match name { + None => { + Type::struct_(ccx, &struct_llfields(ccx, layout), layout.is_packed()) + } + Some(ref name) => { + let llty = Type::named_struct(ccx, name); + *defer = Some((llty, layout)); + llty + } + } } - ty::TyDynamic(..) => Type::vtable_ptr(ccx), - _ => bug!("Unexpected tail in unsized_info_ty: {:?} for ty={:?}", - unsized_part, ty) } } -pub fn immediate_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - if t.is_bool() { - Type::i1(cx) +fn struct_llfields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + layout: TyLayout<'tcx>) -> Vec { + debug!("struct_llfields: {:#?}", layout); + let field_count = layout.fields.count(); + + let mut offset = Size::from_bytes(0); + let mut result: Vec = Vec::with_capacity(1 + field_count * 2); + for i in layout.fields.index_by_increasing_offset() { + let field = layout.field(ccx, i); + let target_offset = layout.fields.offset(i as usize); + debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", + i, field, offset, target_offset); + assert!(target_offset >= offset); + let padding = target_offset - offset; + result.push(Type::array(&Type::i8(ccx), padding.bytes())); + debug!(" padding before: {:?}", padding); + + result.push(field.llvm_type(ccx)); + + if layout.is_packed() { + assert_eq!(padding.bytes(), 0); + } else { + assert!(field.align.abi() <= layout.align.abi(), + "non-packed type has field with larger align ({}): {:#?}", + field.align.abi(), layout); + } + + offset = target_offset + field.size; + } + if !layout.is_unsized() && field_count > 0 { + if offset > layout.size { + bug!("layout: {:#?} stride: {:?} offset: {:?}", + layout, layout.size, offset); + } + let padding = layout.size - offset; + debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", + padding, offset, layout.size); + result.push(Type::array(&Type::i8(ccx), padding.bytes())); + assert!(result.len() == 1 + field_count * 2); } else { - type_of(cx, t) + debug!("struct_llfields: offset: {:?} stride: {:?}", + offset, layout.size); } -} -/// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. -/// This is the right LLVM type for an alloca containing a value of that type, -/// and the pointee of an Lvalue Datum (which is always a LLVM pointer). -/// For unsized types, the returned type is a fat pointer, thus the resulting -/// LLVM type for a `Trait` Lvalue is `{ i8*, void(i8*)** }*`, which is a double -/// indirection to the actual data, unlike a `i8` Lvalue, which is just `i8*`. -/// This is needed due to the treatment of immediate values, as a fat pointer -/// is too large for it to be placed in SSA value (by our rules). -/// For the raw type without far pointer indirection, see `in_memory_type_of`. -pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type { - let ty = if !cx.shared().type_is_sized(ty) { - cx.tcx().mk_imm_ptr(ty) - } else { - ty - }; - in_memory_type_of(cx, ty) + result } -/// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. -/// This is the right LLVM type for a field/array element of that type, -/// and is the same as `type_of` for all Sized types. -/// Unsized types, however, are represented by a "minimal unit", e.g. -/// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this -/// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. -/// If the type is an unsized struct, the regular layout is generated, -/// with the inner-most trailing unsized field using the "minimal unit" -/// of that field's type - this is useful for taking the address of -/// that field and ensuring the struct has the right alignment. -/// For the LLVM type of a value as a whole, see `type_of`. -pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - // Check the cache. - if let Some(&llty) = cx.lltypes().borrow().get(&t) { - return llty; +impl<'a, 'tcx> CrateContext<'a, 'tcx> { + pub fn align_of(&self, ty: Ty<'tcx>) -> Align { + self.layout_of(ty).align + } + + pub fn size_of(&self, ty: Ty<'tcx>) -> Size { + self.layout_of(ty).size } - debug!("type_of {:?}", t); + pub fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { + self.layout_of(ty).size_and_align() + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned +} + +#[derive(Copy, Clone)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option, +} - assert!(!t.has_escaping_regions(), "{:?} has escaping regions", t); +pub trait LayoutLlvmExt<'tcx> { + fn is_llvm_immediate(&self) -> bool; + fn is_llvm_scalar_pair<'a>(&self) -> bool; + fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type; + fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type; + fn scalar_pair_element_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, + index: usize) -> Type; + fn llvm_field_index(&self, index: usize) -> u64; + fn pointee_info_at<'a>(&self, ccx: &CrateContext<'a, 'tcx>, offset: Size) + -> Option; +} - // Replace any typedef'd types with their equivalent non-typedef - // type. This ensures that all LLVM nominal types that contain - // Rust types are defined as the same LLVM types. If we don't do - // this then, e.g. `Option<{myfield: bool}>` would be a different - // type than `Option`. - let t_norm = cx.tcx().erase_regions(&t); +impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { + fn is_llvm_immediate(&self) -> bool { + match self.abi { + layout::Abi::Uninhabited | + layout::Abi::Scalar(_) | + layout::Abi::Vector => true, + layout::Abi::ScalarPair(..) => false, + layout::Abi::Aggregate { .. } => self.is_zst() + } + } - if t != t_norm { - let llty = in_memory_type_of(cx, t_norm); - debug!("--> normalized {:?} to {:?} llty={:?}", t, t_norm, llty); - cx.lltypes().borrow_mut().insert(t, llty); - return llty; + fn is_llvm_scalar_pair<'a>(&self) -> bool { + match self.abi { + layout::Abi::ScalarPair(..) => true, + layout::Abi::Uninhabited | + layout::Abi::Scalar(_) | + layout::Abi::Vector | + layout::Abi::Aggregate { .. } => false + } } - let ptr_ty = |ty: Ty<'tcx>| { - if !cx.shared().type_is_sized(ty) { - if let ty::TyStr = ty.sty { - // This means we get a nicer name in the output (str is always - // unsized). - cx.str_slice_type() - } else { - let ptr_ty = in_memory_type_of(cx, ty).ptr_to(); - let info_ty = unsized_info_ty(cx, ty); - Type::struct_(cx, &[ptr_ty, info_ty], false) + /// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. + /// The pointee type of the pointer in `PlaceRef` is always this type. + /// For sized types, it is also the right LLVM type for an `alloca` + /// containing a value of that type, and most immediates (except `bool`). + /// Unsized types, however, are represented by a "minimal unit", e.g. + /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this + /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. + /// If the type is an unsized struct, the regular layout is generated, + /// with the inner-most trailing unsized field using the "minimal unit" + /// of that field's type - this is useful for taking the address of + /// that field and ensuring the struct has the right alignment. + fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + // Use a different cache for scalars because pointers to DSTs + // can be either fat or thin (data pointers of fat pointers). + if let Some(&llty) = ccx.scalar_lltypes().borrow().get(&self.ty) { + return llty; } - } else { - in_memory_type_of(cx, ty).ptr_to() + let llty = match scalar.value { + layout::Int(i, _) => Type::from_integer(ccx, i), + layout::F32 => Type::f32(ccx), + layout::F64 => Type::f64(ccx), + layout::Pointer => { + let pointee = match self.ty.sty { + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + ccx.layout_of(ty).llvm_type(ccx) + } + ty::TyAdt(def, _) if def.is_box() => { + ccx.layout_of(self.ty.boxed_ty()).llvm_type(ccx) + } + ty::TyFnPtr(sig) => { + let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig); + FnType::new(ccx, sig, &[]).llvm_type(ccx) + } + _ => { + // If we know the alignment, pick something better than i8. + if let Some(pointee) = self.pointee_info_at(ccx, Size::from_bytes(0)) { + Type::pointee_for_abi_align(ccx, pointee.align) + } else { + Type::i8(ccx) + } + } + }; + pointee.ptr_to() + } + }; + ccx.scalar_lltypes().borrow_mut().insert(self.ty, llty); + return llty; } - }; - let mut llty = match t.sty { - ty::TyBool => Type::bool(cx), - ty::TyChar => Type::char(cx), - ty::TyInt(t) => Type::int_from_ty(cx, t), - ty::TyUint(t) => Type::uint_from_ty(cx, t), - ty::TyFloat(t) => Type::float_from_ty(cx, t), - ty::TyNever => Type::nil(cx), - ty::TyClosure(..) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. - adt::incomplete_type_of(cx, t, "closure") - } - ty::TyGenerator(..) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. - adt::incomplete_type_of(cx, t, "generator") - } - - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => { - ptr_ty(ty) - } - ty::TyAdt(def, _) if def.is_box() => { - ptr_ty(t.boxed_ty()) - } - - ty::TyArray(ty, size) => { - let llty = in_memory_type_of(cx, ty); - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); - Type::array(&llty, size) - } - - // Unsized slice types (and str) have the type of their element, and - // traits have the type of u8. This is so that the data pointer inside - // fat pointers is of the right type (e.g. for array accesses), even - // when taking the address of an unsized field in a struct. - ty::TySlice(ty) => in_memory_type_of(cx, ty), - ty::TyStr | ty::TyDynamic(..) => Type::i8(cx), - - ty::TyFnDef(..) => Type::nil(cx), - ty::TyFnPtr(sig) => { - let sig = cx.tcx().erase_late_bound_regions_and_normalize(&sig); - FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() - } - ty::TyTuple(ref tys, _) if tys.is_empty() => Type::nil(cx), - ty::TyTuple(..) => { - adt::type_of(cx, t) - } - ty::TyAdt(..) if t.is_simd() => { - let e = t.simd_type(cx.tcx()); - if !e.is_machine() { - cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - t, e)) - } - let llet = in_memory_type_of(cx, e); - let n = t.simd_size(cx.tcx()) as u64; - Type::vector(&llet, n) - } - ty::TyAdt(..) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. This - // avoids creating more than one copy of the enum when one - // of the enum's variants refers to the enum itself. - let name = llvm_type_name(cx, t); - adt::incomplete_type_of(cx, t, &name[..]) - } - - ty::TyInfer(..) | - ty::TyProjection(..) | - ty::TyParam(..) | - ty::TyAnon(..) | - ty::TyError => bug!("type_of with {:?}", t), - }; - debug!("--> mapped t={:?} to llty={:?}", t, llty); + // Check the cache. + let variant_index = match self.variants { + layout::Variants::Single { index } => Some(index), + _ => None + }; + if let Some(&llty) = ccx.lltypes().borrow().get(&(self.ty, variant_index)) { + return llty; + } + + debug!("llvm_type({:#?})", self); - cx.lltypes().borrow_mut().insert(t, llty); + assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); - // If this was an enum or struct, fill in the type now. - match t.sty { - ty::TyAdt(..) | ty::TyClosure(..) | ty::TyGenerator(..) if !t.is_simd() && !t.is_box() => { - adt::finish_type_of(cx, t, &mut llty); + // Make sure lifetimes are erased, to avoid generating distinct LLVM + // types for Rust types that only differ in the choice of lifetimes. + let normal_ty = ccx.tcx().erase_regions(&self.ty); + + let mut defer = None; + let llty = if self.ty != normal_ty { + let mut layout = ccx.layout_of(normal_ty); + if let Some(v) = variant_index { + layout = layout.for_variant(ccx, v); + } + layout.llvm_type(ccx) + } else { + uncached_llvm_type(ccx, *self, &mut defer) + }; + debug!("--> mapped {:#?} to llty={:?}", self, llty); + + ccx.lltypes().borrow_mut().insert((self.ty, variant_index), llty); + + if let Some((mut llty, layout)) = defer { + llty.set_struct_body(&struct_llfields(ccx, layout), layout.is_packed()) } - _ => () - } - llty -} + llty + } -impl<'a, 'tcx> CrateContext<'a, 'tcx> { - pub fn align_of(&self, ty: Ty<'tcx>) -> machine::llalign { - self.layout_of(ty).align(self).abi() as machine::llalign + fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + if scalar.is_bool() { + return Type::i1(ccx); + } + } + self.llvm_type(ccx) } - pub fn size_of(&self, ty: Ty<'tcx>) -> machine::llsize { - self.layout_of(ty).size(self).bytes() as machine::llsize + fn scalar_pair_element_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, + index: usize) -> Type { + // HACK(eddyb) special-case fat pointers until LLVM removes + // pointee types, to avoid bitcasting every `OperandRef::deref`. + match self.ty.sty { + ty::TyRef(..) | + ty::TyRawPtr(_) => { + return self.field(ccx, index).llvm_type(ccx); + } + ty::TyAdt(def, _) if def.is_box() => { + let ptr_ty = ccx.tcx().mk_mut_ptr(self.ty.boxed_ty()); + return ccx.layout_of(ptr_ty).scalar_pair_element_llvm_type(ccx, index); + } + _ => {} + } + + let (a, b) = match self.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self) + }; + let scalar = [a, b][index]; + + // Make sure to return the same type `immediate_llvm_type` would, + // to avoid dealing with two types and the associated conversions. + // This means that `(bool, bool)` is represented as `{i1, i1}`, + // both in memory and as an immediate, while `bool` is typically + // `i8` in memory and only `i1` when immediate. While we need to + // load/store `bool` as `i8` to avoid crippling LLVM optimizations, + // `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`. + if scalar.is_bool() { + return Type::i1(ccx); + } + + match scalar.value { + layout::Int(i, _) => Type::from_integer(ccx, i), + layout::F32 => Type::f32(ccx), + layout::F64 => Type::f64(ccx), + layout::Pointer => { + // If we know the alignment, pick something better than i8. + let offset = if index == 0 { + Size::from_bytes(0) + } else { + a.value.size(ccx).abi_align(b.value.align(ccx)) + }; + let pointee = if let Some(pointee) = self.pointee_info_at(ccx, offset) { + Type::pointee_for_abi_align(ccx, pointee.align) + } else { + Type::i8(ccx) + }; + pointee.ptr_to() + } + } } - pub fn over_align_of(&self, t: Ty<'tcx>) - -> Option { - let layout = self.layout_of(t); - if let Some(align) = layout.over_align(&self.tcx().data_layout) { - Some(align as machine::llalign) - } else { - None + fn llvm_field_index(&self, index: usize) -> u64 { + match self.abi { + layout::Abi::Scalar(_) | + layout::Abi::ScalarPair(..) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + _ => {} + } + match self.fields { + layout::FieldPlacement::Union(_) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + + layout::FieldPlacement::Array { .. } => { + index as u64 + } + + layout::FieldPlacement::Arbitrary { .. } => { + 1 + (self.fields.memory_index(index) as u64) * 2 + } } } -} -fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> String { - let mut name = String::with_capacity(32); - let printer = DefPathBasedNames::new(cx.tcx(), true, true); - printer.push_type_name(ty, &mut name); - name + fn pointee_info_at<'a>(&self, ccx: &CrateContext<'a, 'tcx>, offset: Size) + -> Option { + if let Some(&pointee) = ccx.pointee_infos().borrow().get(&(self.ty, offset)) { + return pointee; + } + + let mut result = None; + match self.ty.sty { + ty::TyRawPtr(mt) if offset.bytes() == 0 => { + let (size, align) = ccx.size_and_align_of(mt.ty); + result = Some(PointeeInfo { + size, + align, + safe: None + }); + } + + ty::TyRef(_, mt) if offset.bytes() == 0 => { + let (size, align) = ccx.size_and_align_of(mt.ty); + + let kind = match mt.mutbl { + hir::MutImmutable => if ccx.shared().type_is_freeze(mt.ty) { + PointerKind::Frozen + } else { + PointerKind::Shared + }, + hir::MutMutable => { + if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias || + ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + result = Some(PointeeInfo { + size, + align, + safe: Some(kind) + }); + } + + _ => { + let mut data_variant = match self.variants { + layout::Variants::NicheFilling { dataful_variant, .. } => { + // Only the niche itself is always initialized, + // so only check for a pointer at its offset. + // + // If the niche is a pointer, it's either valid + // (according to its type), or null (which the + // niche field's scalar validity range encodes). + // This allows using `dereferenceable_or_null` + // for e.g. `Option<&T>`, and this will continue + // to work as long as we don't start using more + // niches than just null (e.g. the first page + // of the address space, or unaligned pointers). + if self.fields.offset(0) == offset { + Some(self.for_variant(ccx, dataful_variant)) + } else { + None + } + } + _ => Some(*self) + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let layout::FieldPlacement::Union(_) = variant.fields { + data_variant = None; + } + } + + if let Some(variant) = data_variant { + let ptr_end = offset + layout::Pointer.size(ccx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(ccx, i); + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + result = field.pointee_info_at(ccx, offset - field_start); + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::TyAdt(def, _) = self.ty.sty { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + } + } + + ccx.pointee_infos().borrow_mut().insert((self.ty, offset), result); + result + } } diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml index bedbea0068874..7d9d7cea9335d 100644 --- a/src/librustc_trans_utils/Cargo.toml +++ b/src/librustc_trans_utils/Cargo.toml @@ -19,3 +19,4 @@ syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } +rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans_utils/collector.rs similarity index 92% rename from src/librustc_trans/collector.rs rename to src/librustc_trans_utils/collector.rs index 6fa69de74b0a1..e5a97966723d2 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans_utils/collector.rs @@ -195,6 +195,7 @@ use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; use rustc::middle::lang_items::{ExchangeMallocFnLangItem}; +use rustc::middle::trans::TransItem; use rustc::traits; use rustc::ty::subst::Substs; use rustc::ty::{self, TypeFoldable, Ty, TyCtxt}; @@ -202,14 +203,16 @@ use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::mir::{self, Location}; use rustc::mir::visit::Visitor as MirVisitor; -use common::{def_ty, instance_ty, type_is_sized}; +use common::{def_ty, instance_ty, type_has_metadata}; use monomorphize::{self, Instance}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; -use trans_item::{TransItem, TransItemExt, DefPathBasedNames, InstantiationMode}; +use trans_item::{TransItemExt, DefPathBasedNames, InstantiationMode}; use rustc_data_structures::bitvec::BitVector; +use syntax::attr; + #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum TransItemCollectionMode { Eager, @@ -296,26 +299,22 @@ pub fn collect_crate_translation_items<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mode: TransItemCollectionMode) -> (FxHashSet>, InliningMap<'tcx>) { - // We are not tracking dependencies of this pass as it has to be re-executed - // every time no matter what. - tcx.dep_graph.with_ignore(|| { - let roots = collect_roots(tcx, mode); - - debug!("Building translation item graph, beginning at roots"); - let mut visited = FxHashSet(); - let mut recursion_depths = DefIdMap(); - let mut inlining_map = InliningMap::new(); - - for root in roots { - collect_items_rec(tcx, - root, - &mut visited, - &mut recursion_depths, - &mut inlining_map); - } + let roots = collect_roots(tcx, mode); + + debug!("Building translation item graph, beginning at roots"); + let mut visited = FxHashSet(); + let mut recursion_depths = DefIdMap(); + let mut inlining_map = InliningMap::new(); + + for root in roots { + collect_items_rec(tcx, + root, + &mut visited, + &mut recursion_depths, + &mut inlining_map); + } - (visited, inlining_map) - }) + (visited, inlining_map) } // Find all non-generic items by walking the HIR. These items serve as roots to @@ -327,9 +326,14 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut roots = Vec::new(); { + let entry_fn = tcx.sess.entry_fn.borrow().map(|(node_id, _)| { + tcx.hir.local_def_id(node_id) + }); + let mut visitor = RootCollector { tcx, mode, + entry_fn, output: &mut roots, }; @@ -405,9 +409,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn record_accesses<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - caller: TransItem<'tcx>, - callees: &[TransItem<'tcx>], - inlining_map: &mut InliningMap<'tcx>) { + caller: TransItem<'tcx>, + callees: &[TransItem<'tcx>], + inlining_map: &mut InliningMap<'tcx>) { let is_inlining_candidate = |trans_item: &TransItem<'tcx>| { trans_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy }; @@ -566,7 +570,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { if let ConstVal::Unevaluated(def_id, substs) = constant.val { let substs = self.tcx.trans_apply_param_substs(self.param_substs, &substs); - let instance = monomorphize::resolve(self.tcx, def_id, substs); + let instance = ty::Instance::resolve(self.tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); collect_neighbours(self.tcx, instance, true, self.output); } @@ -587,7 +594,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let constness = match (self.const_context, &callee_ty.sty) { (true, &ty::TyFnDef(def_id, substs)) if self.tcx.is_const_fn(def_id) => { - let instance = monomorphize::resolve(self.tcx, def_id, substs); + let instance = + ty::Instance::resolve(self.tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); Some(instance) } _ => None @@ -619,7 +630,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Unreachable | mir::TerminatorKind::Assert { .. } => {} mir::TerminatorKind::GeneratorDrop | - mir::TerminatorKind::Yield { .. } => bug!(), + mir::TerminatorKind::Yield { .. } | + mir::TerminatorKind::FalseEdges { .. } => bug!(), } self.super_terminator_kind(block, kind, location); @@ -627,7 +639,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_static(&mut self, static_: &mir::Static<'tcx>, - context: mir::visit::LvalueContext<'tcx>, + context: mir::visit::PlaceContext<'tcx>, location: Location) { debug!("visiting static {:?} @ {:?}", static_.def_id, location); @@ -657,7 +669,10 @@ fn visit_fn_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, output: &mut Vec>) { if let ty::TyFnDef(def_id, substs) = ty.sty { - let instance = monomorphize::resolve(tcx, def_id, substs); + let instance = ty::Instance::resolve(tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap(); visit_instance_use(tcx, instance, is_direct_call, output); } } @@ -775,7 +790,7 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, target_ty: Ty<'tcx>) -> (Ty<'tcx>, Ty<'tcx>) { let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { - if !type_is_sized(tcx, inner_source) { + if type_has_metadata(tcx, inner_source) { (inner_source, inner_target) } else { tcx.struct_lockstep_tails(inner_source, inner_target) @@ -843,9 +858,13 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, assert!(!poly_trait_ref.has_escaping_regions()); // Walk all methods of the trait, including those of its supertraits - let methods = traits::get_vtable_methods(tcx, poly_trait_ref); - let methods = methods.filter_map(|method| method) - .map(|(def_id, substs)| monomorphize::resolve(tcx, def_id, substs)) + let methods = tcx.vtable_methods(poly_trait_ref); + let methods = methods.iter().cloned().filter_map(|method| method) + .map(|(def_id, substs)| ty::Instance::resolve( + tcx, + ty::ParamEnv::empty(traits::Reveal::All), + def_id, + substs).unwrap()) .filter(|&instance| should_trans_locally(tcx, &instance)) .map(|instance| create_fn_trans_item(instance)); output.extend(methods); @@ -863,6 +882,7 @@ struct RootCollector<'b, 'a: 'b, 'tcx: 'a + 'b> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mode: TransItemCollectionMode, output: &'b mut Vec>, + entry_fn: Option, } impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { @@ -872,7 +892,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { hir::ItemUse(..) | hir::ItemForeignMod(..) | hir::ItemTy(..) | - hir::ItemDefaultImpl(..) | + hir::ItemAutoImpl(..) | hir::ItemTrait(..) | hir::ItemMod(..) => { // Nothing to do, just keep recursing... @@ -920,10 +940,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { let tcx = self.tcx; let def_id = tcx.hir.local_def_id(item.id); - if (self.mode == TransItemCollectionMode::Eager || - !tcx.is_const_fn(def_id) || tcx.is_exported_symbol(def_id)) && - !item_has_type_parameters(tcx, def_id) { - + if self.is_root(def_id) { debug!("RootCollector: ItemFn({})", def_id_to_string(tcx, def_id)); @@ -945,10 +962,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { let tcx = self.tcx; let def_id = tcx.hir.local_def_id(ii.id); - if (self.mode == TransItemCollectionMode::Eager || - !tcx.is_const_fn(def_id) || - tcx.is_exported_symbol(def_id)) && - !item_has_type_parameters(tcx, def_id) { + if self.is_root(def_id) { debug!("RootCollector: MethodImplItem({})", def_id_to_string(tcx, def_id)); @@ -961,6 +975,22 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { } } +impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> { + fn is_root(&self, def_id: DefId) -> bool { + !item_has_type_parameters(self.tcx, def_id) && match self.mode { + TransItemCollectionMode::Eager => { + true + } + TransItemCollectionMode::Lazy => { + self.entry_fn == Some(def_id) || + self.tcx.is_exported_symbol(def_id) || + attr::contains_name(&self.tcx.get_attrs(def_id), + "rustc_std_internal_symbol") + } + } + } +} + fn item_has_type_parameters<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { let generics = tcx.generics_of(def_id); generics.parent_types as usize + generics.types.len() > 0 @@ -1000,8 +1030,10 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, continue; } - let instance = - monomorphize::resolve(tcx, method.def_id, callee_substs); + let instance = ty::Instance::resolve(tcx, + ty::ParamEnv::empty(traits::Reveal::All), + method.def_id, + callee_substs).unwrap(); let trans_item = create_fn_trans_item(instance); if trans_item.is_instantiable(tcx) && should_trans_locally(tcx, &instance) { diff --git a/src/librustc_trans_utils/common.rs b/src/librustc_trans_utils/common.rs new file mode 100644 index 0000000000000..47968afd70d97 --- /dev/null +++ b/src/librustc_trans_utils/common.rs @@ -0,0 +1,92 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types, non_snake_case)] + +//! Code that is useful in various trans modules. + +use rustc::hir::def_id::DefId; +use rustc::hir::map::DefPathData; +use rustc::traits; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::Substs; + +use syntax::attr; +use syntax_pos::DUMMY_SP; + +pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_sized(tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP) +} + +pub fn type_has_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + if type_is_sized(tcx, ty) { + return false; + } + + let tail = tcx.struct_tail(ty); + match tail.sty { + ty::TyForeign(..) => false, + ty::TyStr | ty::TySlice(..) | ty::TyDynamic(..) => true, + _ => bug!("unexpected unsized tail: {:?}", tail.sty), + } +} + +pub fn requests_inline<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + if is_inline_instance(tcx, instance) { + return true + } + if let ty::InstanceDef::DropGlue(..) = instance.def { + // Drop glue wants to be instantiated at every translation + // unit, but without an #[inline] hint. We should make this + // available to normal end-users. + return true + } + attr::requests_inline(&instance.def.attrs(tcx)[..]) || + tcx.is_const_fn(instance.def.def_id()) +} + +pub fn is_inline_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx> +) -> bool { + let def_id = match instance.def { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, + _ => return true + }; + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => true, + _ => false + } +} + +/// Given a DefId and some Substs, produces the monomorphic item type. +pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> Ty<'tcx> +{ + let ty = tcx.type_of(def_id); + tcx.trans_apply_param_substs(substs, &ty) +} + +/// Return the substituted type of an instance. +pub fn instance_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: &ty::Instance<'tcx>) + -> Ty<'tcx> +{ + let ty = instance.def.def_ty(tcx); + tcx.trans_apply_param_substs(instance.substs, &ty) +} diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 6873befd2bfca..d6f8707b8747a 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -27,8 +27,6 @@ #![feature(slice_patterns)] #![feature(conservative_impl_trait)] -#![cfg_attr(stage0, feature(const_fn))] - extern crate ar; extern crate flate2; extern crate owning_ref; @@ -38,18 +36,21 @@ extern crate log; #[macro_use] extern crate rustc; extern crate rustc_back; +extern crate rustc_data_structures; extern crate syntax; extern crate syntax_pos; -use rustc::ty::TyCtxt; +use rustc::ty::{TyCtxt, Instance}; use rustc::hir; use rustc::hir::def_id::LOCAL_CRATE; use rustc::hir::map as hir_map; use rustc::util::nodemap::NodeSet; -use syntax::attr; - +pub mod common; pub mod link; +pub mod collector; +pub mod trans_item; +pub mod monomorphize; pub mod trans_crate; /// check for the #[rustc_error] annotation, which forces an @@ -74,7 +75,7 @@ pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { /// /// This list is later used by linkers to determine the set of symbols needed to /// be exposed from a dynamic library and it's also encoded into the metadata. -pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet { +pub fn find_exported_symbols<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> NodeSet { tcx.reachable_set(LOCAL_CRATE).0.iter().cloned().filter(|&id| { // Next, we want to ignore some FFI functions that are not exposed from // this crate. Reachable FFI functions can be lumped into two @@ -104,11 +105,10 @@ pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet { node: hir::ImplItemKind::Method(..), .. }) => { let def_id = tcx.hir.local_def_id(id); let generics = tcx.generics_of(def_id); - let attributes = tcx.get_attrs(def_id); (generics.parent_types == 0 && generics.types.is_empty()) && // Functions marked with #[inline] are only ever translated // with "internal" linkage and are never exported. - !attr::requests_inline(&attributes) + !common::requests_inline(tcx, &Instance::mono(tcx, def_id)) } _ => false diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs index 47484488fb8e8..c1e670cc7caf5 100644 --- a/src/librustc_trans_utils/link.rs +++ b/src/librustc_trans_utils/link.rs @@ -163,15 +163,30 @@ pub fn default_output_for_target(sess: &Session) -> config::CrateType { /// Checks if target supports crate_type as output pub fn invalid_output_for_target(sess: &Session, crate_type: config::CrateType) -> bool { - match (sess.target.target.options.dynamic_linking, - sess.target.target.options.executables, crate_type) { - (false, _, config::CrateTypeCdylib) | - (false, _, config::CrateTypeDylib) | - (false, _, config::CrateTypeProcMacro) => true, - (true, _, config::CrateTypeCdylib) | - (true, _, config::CrateTypeDylib) => sess.crt_static() && - !sess.target.target.options.crt_static_allows_dylibs, - (_, false, config::CrateTypeExecutable) => true, - _ => false + match crate_type { + config::CrateTypeCdylib | + config::CrateTypeDylib | + config::CrateTypeProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true + } + if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { + return true + } + } + _ => {} } + if sess.target.target.options.only_cdylib { + match crate_type { + config::CrateTypeProcMacro | config::CrateTypeDylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == config::CrateTypeExecutable { + return true + } + } + + false } diff --git a/src/librustc_trans_utils/monomorphize.rs b/src/librustc_trans_utils/monomorphize.rs new file mode 100644 index 0000000000000..66833a1a7c2f1 --- /dev/null +++ b/src/librustc_trans_utils/monomorphize.rs @@ -0,0 +1,127 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::def_id::DefId; +use rustc::middle::lang_items::DropInPlaceFnLangItem; +use rustc::traits; +use rustc::ty::adjustment::CustomCoerceUnsized; +use rustc::ty::subst::{Kind, Subst}; +use rustc::ty::{self, Ty, TyCtxt}; + +pub use rustc::ty::Instance; + +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items().fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } +} + +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. + 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. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} + +pub fn resolve_closure<'a, 'tcx> ( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind) + -> Instance<'tcx> +{ + let actual_kind = substs.closure_kind(def_id, tcx); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), + _ => Instance::new(def_id, substs.substs) + } +} + +pub fn resolve_drop_in_place<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>) + -> ty::Instance<'tcx> +{ + let def_id = tcx.require_lang_item(DropInPlaceFnLangItem); + let substs = tcx.intern_substs(&[Kind::from(ty)]); + Instance::resolve(tcx, ty::ParamEnv::empty(traits::Reveal::All), def_id, substs).unwrap() +} + +pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>) + -> CustomCoerceUnsized { + let def_id = tcx.lang_items().coerce_unsized_trait().unwrap(); + + let trait_ref = ty::Binder(ty::TraitRef { + def_id: def_id, + substs: tcx.mk_substs_trait(source_ty, &[target_ty]) + }); + + match tcx.trans_fulfill_obligation( (ty::ParamEnv::empty(traits::Reveal::All), trait_ref)) { + traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() + } + vtable => { + bug!("invalid CoerceUnsized vtable: {:?}", vtable); + } + } +} + diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs index f51a463fcc23e..645898601614b 100644 --- a/src/librustc_trans_utils/trans_crate.rs +++ b/src/librustc_trans_utils/trans_crate.rs @@ -51,7 +51,7 @@ pub trait TransCrate { type TranslatedCrate; fn metadata_loader() -> Box; - fn provide_local(_providers: &mut Providers); + fn provide(_providers: &mut Providers); fn provide_extern(_providers: &mut Providers); fn trans_crate<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -77,8 +77,8 @@ impl TransCrate for DummyTransCrate { box DummyMetadataLoader(()) } - fn provide_local(_providers: &mut Providers) { - bug!("DummyTransCrate::provide_local"); + fn provide(_providers: &mut Providers) { + bug!("DummyTransCrate::provide"); } fn provide_extern(_providers: &mut Providers) { @@ -185,7 +185,7 @@ impl TransCrate for MetadataOnlyTransCrate { box NoLlvmMetadataLoader } - fn provide_local(_providers: &mut Providers) {} + fn provide(_providers: &mut Providers) {} fn provide_extern(_providers: &mut Providers) {} fn trans_crate<'a, 'tcx>( @@ -201,7 +201,7 @@ impl TransCrate for MetadataOnlyTransCrate { .fingerprint_of(&DepNode::new_no_params(DepKind::Krate)); let link_meta = build_link_meta(crate_hash); let exported_symbols = ::find_exported_symbols(tcx); - let (metadata, _hashes) = tcx.encode_metadata(&link_meta, &exported_symbols); + let metadata = tcx.encode_metadata(&link_meta, &exported_symbols); OngoingCrateTranslation { metadata: metadata, diff --git a/src/librustc_trans_utils/trans_item.rs b/src/librustc_trans_utils/trans_item.rs new file mode 100644 index 0000000000000..817ceefeb7fe9 --- /dev/null +++ b/src/librustc_trans_utils/trans_item.rs @@ -0,0 +1,465 @@ +// 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. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use common; +use monomorphize::Instance; +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::middle::trans::Linkage; +use rustc::session::config::OptLevel; +use rustc::traits; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::{Subst, Substs}; +use syntax::ast; +use syntax::attr::{self, InlineAttr}; +use std::fmt::{self, Write}; +use std::iter; + +pub use rustc::middle::trans::TransItem; + +pub fn linkage_by_name(name: &str) -> Option { + use rustc::middle::trans::Linkage::*; + + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but whitelist them anyway and trust that + // the user knows what s/he's doing. Who knows, unanticipated use cases + // may pop up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Some(Appending), + "available_externally" => Some(AvailableExternally), + "common" => Some(Common), + "extern_weak" => Some(ExternalWeak), + "external" => Some(External), + "internal" => Some(Internal), + "linkonce" => Some(LinkOnceAny), + "linkonce_odr" => Some(LinkOnceODR), + "private" => Some(Private), + "weak" => Some(WeakAny), + "weak_odr" => Some(WeakODR), + _ => None, + } +} + +/// 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 { + /// In some compilation scenarios we may decide to take functions that + /// are typically `LocalCopy` and instead move them to `GloballyShared` + /// to avoid translating them a bunch of times. In this situation, + /// however, our local copy may conflict with other crates also + /// inlining the same function. + /// + /// This flag indicates that this situation is occuring, and informs + /// symbol name calculation that some extra mangling is needed to + /// avoid conflicts. Note that this may eventually go away entirely if + /// ThinLTO enables us to *always* have a globally shared instance of a + /// function within one crate's compilation. + may_conflict: bool, + }, + + /// Each codegen unit containing a reference to the given TransItem will + /// have its own private copy of the function (with internal linkage). + LocalCopy, +} + +pub trait TransItemExt<'a, 'tcx>: fmt::Debug { + fn as_trans_item(&self) -> &TransItem<'tcx>; + + fn instantiation_mode(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> InstantiationMode { + let inline_in_all_cgus = + tcx.sess.opts.debugging_opts.inline_in_all_cgus.unwrap_or_else(|| { + tcx.sess.opts.optimize != OptLevel::No + }); + + match *self.as_trans_item() { + TransItem::Fn(ref instance) => { + // If this function isn't inlined or otherwise has explicit + // linkage, then we'll be creating a globally shared version. + if self.explicit_linkage(tcx).is_some() || + !common::requests_inline(tcx, instance) + { + return InstantiationMode::GloballyShared { may_conflict: false } + } + + // At this point we don't have explicit linkage and we're an + // inlined function. If we're inlining into all CGUs then we'll + // be creating a local copy per CGU + if inline_in_all_cgus { + return InstantiationMode::LocalCopy + } + + // Finally, if this is `#[inline(always)]` we're sure to respect + // that with an inline copy per CGU, but otherwise we'll be + // creating one copy of this `#[inline]` function which may + // conflict with upstream crates as it could be an exported + // symbol. + let attrs = instance.def.attrs(tcx); + match attr::find_inline_attr(Some(tcx.sess.diagnostic()), &attrs) { + InlineAttr::Always => InstantiationMode::LocalCopy, + _ => { + InstantiationMode::GloballyShared { may_conflict: true } + } + } + } + TransItem::Static(..) => { + InstantiationMode::GloballyShared { may_conflict: false } + } + TransItem::GlobalAsm(..) => { + InstantiationMode::GloballyShared { may_conflict: false } + } + } + } + + fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { + let def_id = match *self.as_trans_item() { + TransItem::Fn(ref instance) => instance.def_id(), + TransItem::Static(node_id) => tcx.hir.local_def_id(node_id), + TransItem::GlobalAsm(..) => return None, + }; + + let attributes = tcx.get_attrs(def_id); + if let Some(name) = attr::first_attr_value_str_by_name(&attributes, "linkage") { + if let Some(linkage) = linkage_by_name(&name.as_str()) { + Some(linkage) + } else { + let span = tcx.hir.span_if_local(def_id); + if let Some(span) = span { + tcx.sess.span_fatal(span, "invalid linkage specified") + } else { + tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) + } + } + } else { + None + } + } + + /// Returns whether this instance is instantiable - whether it has no unsatisfied + /// predicates. + /// + /// In order to translate an item, all of its predicates must hold, because + /// otherwise the item does not make sense. Type-checking ensures that + /// the predicates of every item that is *used by* a valid item *do* + /// hold, so we can rely on that. + /// + /// However, we translate collector roots (reachable items) and functions + /// in vtables when they are seen, even if they are not used, and so they + /// might not be instantiable. For example, a programmer can define this + /// public function: + /// + /// pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone { + /// <&mut () as Clone>::clone(&s); + /// } + /// + /// That function can't be translated, because the method `<&mut () as Clone>::clone` + /// does not exist. Luckily for us, that function can't ever be used, + /// because that would require for `&'a mut (): Clone` to hold, so we + /// can just not emit any code, or even a linker reference for it. + /// + /// Similarly, if a vtable method has such a signature, and therefore can't + /// be used, we can just not emit it and have a placeholder (a null pointer, + /// which will never be accessed) in its place. + fn is_instantiable(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { + debug!("is_instantiable({:?})", self); + let (def_id, substs) = match *self.as_trans_item() { + TransItem::Fn(ref instance) => (instance.def_id(), instance.substs), + TransItem::Static(node_id) => (tcx.hir.local_def_id(node_id), Substs::empty()), + // global asm never has predicates + TransItem::GlobalAsm(..) => return true + }; + + let predicates = tcx.predicates_of(def_id).predicates.subst(tcx, substs); + traits::normalize_and_test_predicates(tcx, predicates) + } + + fn to_string(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> String { + let hir_map = &tcx.hir; + + return match *self.as_trans_item() { + TransItem::Fn(instance) => { + to_string_internal(tcx, "fn ", instance) + }, + TransItem::Static(node_id) => { + let def_id = hir_map.local_def_id(node_id); + let instance = Instance::new(def_id, tcx.intern_substs(&[])); + to_string_internal(tcx, "static ", instance) + }, + TransItem::GlobalAsm(..) => { + "global_asm".to_string() + } + }; + + fn to_string_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + prefix: &str, + instance: Instance<'tcx>) + -> String { + let mut result = String::with_capacity(32); + result.push_str(prefix); + let printer = DefPathBasedNames::new(tcx, false, false); + printer.push_instance_as_string(instance, &mut result); + result + } + } +} + +impl<'a, 'tcx> TransItemExt<'a, 'tcx> for TransItem<'tcx> { + fn as_trans_item(&self) -> &TransItem<'tcx> { + self + } +} + +//=----------------------------------------------------------------------------- +// TransItem String Keys +//=----------------------------------------------------------------------------- + +// The code below allows for producing a unique string key for a trans item. +// These keys are used by the handwritten auto-tests, so they need to be +// predictable and human-readable. +// +// Note: A lot of this could looks very similar to what's already in the +// ppaux module. It would be good to refactor things so we only have one +// parameterizable implementation for printing types. + +/// Same as `unique_type_name()` but with the result pushed onto the given +/// `output` parameter. +pub struct DefPathBasedNames<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + omit_disambiguators: bool, + omit_local_crate_name: bool, +} + +impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + omit_disambiguators: bool, + omit_local_crate_name: bool) + -> Self { + DefPathBasedNames { + tcx, + omit_disambiguators, + omit_local_crate_name, + } + } + + pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String) { + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyNever => output.push_str("!"), + ty::TyInt(ast::IntTy::Is) => output.push_str("isize"), + ty::TyInt(ast::IntTy::I8) => output.push_str("i8"), + ty::TyInt(ast::IntTy::I16) => output.push_str("i16"), + ty::TyInt(ast::IntTy::I32) => output.push_str("i32"), + ty::TyInt(ast::IntTy::I64) => output.push_str("i64"), + ty::TyInt(ast::IntTy::I128) => output.push_str("i128"), + ty::TyUint(ast::UintTy::Us) => output.push_str("usize"), + ty::TyUint(ast::UintTy::U8) => output.push_str("u8"), + ty::TyUint(ast::UintTy::U16) => output.push_str("u16"), + ty::TyUint(ast::UintTy::U32) => output.push_str("u32"), + ty::TyUint(ast::UintTy::U64) => output.push_str("u64"), + ty::TyUint(ast::UintTy::U128) => output.push_str("u128"), + ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"), + ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"), + ty::TyAdt(adt_def, substs) => { + self.push_def_path(adt_def.did, output); + self.push_type_params(substs, iter::empty(), output); + }, + ty::TyTuple(component_types, _) => { + output.push('('); + for &component_type in component_types { + self.push_type_name(component_type, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + self.push_type_name(inner_type, output); + }, + ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + self.push_type_name(inner_type, output); + }, + ty::TyArray(inner_type, len) => { + output.push('['); + self.push_type_name(inner_type, output); + write!(output, "; {}", + len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap(); + output.push(']'); + }, + ty::TySlice(inner_type) => { + output.push('['); + self.push_type_name(inner_type, output); + output.push(']'); + }, + ty::TyDynamic(ref trait_data, ..) => { + if let Some(principal) = trait_data.principal() { + self.push_def_path(principal.def_id(), output); + self.push_type_params(principal.skip_binder().substs, + trait_data.projection_bounds(), + output); + } + }, + ty::TyForeign(did) => self.push_def_path(did, output), + ty::TyFnDef(..) | + ty::TyFnPtr(_) => { + let sig = t.fn_sig(self.tcx); + if sig.unsafety() == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + let abi = sig.abi(); + if abi != ::syntax::abi::Abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); + + if !sig.inputs().is_empty() { + for ¶meter_type in sig.inputs() { + self.push_type_name(parameter_type, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs().is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + if !sig.output().is_nil() { + output.push_str(" -> "); + self.push_type_name(sig.output(), output); + } + }, + ty::TyGenerator(def_id, ref closure_substs, _) | + ty::TyClosure(def_id, ref closure_substs) => { + self.push_def_path(def_id, output); + let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); + let substs = closure_substs.substs.truncate_to(self.tcx, generics); + self.push_type_params(substs, iter::empty(), output); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyParam(_) | + ty::TyAnon(..) => { + bug!("DefPathBasedNames: Trying to create type name for \ + unexpected type: {:?}", t); + } + } + } + + pub fn push_def_path(&self, + def_id: DefId, + output: &mut String) { + let def_path = self.tcx.def_path(def_id); + + // some_crate:: + if !(self.omit_local_crate_name && def_id.is_local()) { + output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); + output.push_str("::"); + } + + // foo::bar::ItemName:: + for part in self.tcx.def_path(def_id).data { + if self.omit_disambiguators { + write!(output, "{}::", part.data.as_interned_str()).unwrap(); + } else { + write!(output, "{}[{}]::", + part.data.as_interned_str(), + part.disambiguator).unwrap(); + } + } + + // remove final "::" + output.pop(); + output.pop(); + } + + fn push_type_params(&self, + substs: &Substs<'tcx>, + projections: I, + output: &mut String) + where I: Iterator> + { + let mut projections = projections.peekable(); + if substs.types().next().is_none() && projections.peek().is_none() { + return; + } + + output.push('<'); + + for type_parameter in substs.types() { + self.push_type_name(type_parameter, output); + output.push_str(", "); + } + + for projection in projections { + let projection = projection.skip_binder(); + let name = &self.tcx.associated_item(projection.item_def_id).name.as_str(); + output.push_str(name); + output.push_str("="); + self.push_type_name(projection.ty, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } + + pub fn push_instance_as_string(&self, + instance: Instance<'tcx>, + output: &mut String) { + self.push_def_path(instance.def_id(), output); + self.push_type_params(instance.substs, iter::empty(), output); + } +} diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index 194d37dcb81c2..c3245842b4256 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -15,7 +15,6 @@ syntax = { path = "../libsyntax" } arena = { path = "../libarena" } fmt_macros = { path = "../libfmt_macros" } rustc = { path = "../librustc" } -rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } diff --git a/src/librustc_typeck/README.md b/src/librustc_typeck/README.md index a38f04e304b6c..1abc914e7d661 100644 --- a/src/librustc_typeck/README.md +++ b/src/librustc_typeck/README.md @@ -12,7 +12,7 @@ The `rustc_typeck` crate contains the source for "type collection" and ## Type collection -Type "collection" is the process of convering the types found in the +Type "collection" is the process of converting the types found in the HIR (`hir::Ty`), which represent the syntactic things that the user wrote, into the **internal representation** used by the compiler (`Ty<'tcx>`) -- we also do similar conversions for where-clauses and diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 54fd070e93cbc..e20706a0d5abb 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -18,17 +18,19 @@ use hir; use hir::def::Def; use hir::def_id::DefId; use middle::resolve_lifetime as rl; +use namespace::Namespace; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits; -use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable}; +use rustc::ty::{self, RegionKind, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::wf::object_region_bounds; -use rustc_back::slice; +use std::slice; use require_c_abi_if_variadic; use util::common::ErrorReported; use util::nodemap::FxHashSet; use std::iter; use syntax::{abi, ast}; +use syntax::symbol::Symbol; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax_pos::Span; @@ -108,7 +110,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { tcx.types.re_static } - Some(rl::Region::LateBound(debruijn, id)) => { + Some(rl::Region::LateBound(debruijn, id, _)) => { let name = lifetime_name(id); tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) @@ -118,7 +120,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) } - Some(rl::Region::EarlyBound(index, id)) => { + Some(rl::Region::EarlyBound(index, id, _)) => { let name = lifetime_name(id); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, @@ -356,9 +358,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { poly_projections.extend(assoc_bindings.iter().filter_map(|binding| { // specify type to assert that error was already reported in Err case: let predicate: Result<_, ErrorReported> = - self.ast_type_binding_to_poly_projection_predicate(trait_ref.ref_id, - poly_trait_ref, - binding); + self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref, binding); predicate.ok() // ok to ignore Err() because ErrorReported (see above) })); @@ -423,13 +423,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { -> bool { self.tcx().associated_items(trait_def_id).any(|item| { - item.kind == ty::AssociatedKind::Type && item.name == assoc_name + item.kind == ty::AssociatedKind::Type && + self.tcx().hygienic_eq(assoc_name, item.name, trait_def_id) }) } fn ast_type_binding_to_poly_projection_predicate( &self, - _path_id: ast::NodeId, trait_ref: ty::PolyTraitRef<'tcx>, binding: &ConvertedBinding<'tcx>) -> Result, ErrorReported> @@ -504,7 +504,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let candidate = self.one_bound_for_assoc_type(candidates, &trait_ref.to_string(), - &binding.item_name.as_str(), + binding.item_name, binding.span)?; Ok(candidate.map_bound(|trait_ref| { @@ -573,8 +573,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let b = &trait_bounds[0]; let span = b.trait_ref.path.span; struct_span_err!(self.tcx().sess, span, E0225, - "only Send/Sync traits can be used as additional traits in a trait object") - .span_label(span, "non-Send/Sync additional trait") + "only auto traits can be used as additional traits in a trait object") + .span_label(span, "non-auto additional trait") .emit(); } @@ -702,7 +702,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let param_name = tcx.hir.ty_param_name(param_node_id); self.one_bound_for_assoc_type(suitable_bounds, ¶m_name.as_str(), - &assoc_name.as_str(), + assoc_name, span) } @@ -712,7 +712,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { fn one_bound_for_assoc_type(&self, mut bounds: I, ty_param_name: &str, - assoc_name: &str, + assoc_name: ast::Name, span: Span) -> Result, ErrorReported> where I: Iterator> @@ -741,7 +741,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { for bound in bounds { let bound_span = self.tcx().associated_items(bound.def_id()).find(|item| { - item.kind == ty::AssociatedKind::Type && item.name == assoc_name + item.kind == ty::AssociatedKind::Type && + self.tcx().hygienic_eq(assoc_name, item.name, bound.def_id()) }) .and_then(|item| self.tcx().hir.span_if_local(item.def_id)); @@ -781,7 +782,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { debug!("associated_path_def_to_ty: {:?}::{}", ty, assoc_name); - self.prohibit_type_params(slice::ref_slice(item_segment)); + self.prohibit_type_params(slice::from_ref(item_segment)); // Find the type of the associated item, and the trait where the associated // item is declared. @@ -802,10 +803,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); - match self.one_bound_for_assoc_type(candidates, - "Self", - &assoc_name.as_str(), - span) { + match self.one_bound_for_assoc_type(candidates, "Self", assoc_name, span) { Ok(bound) => bound, Err(ErrorReported) => return (tcx.types.err, Def::Err), } @@ -830,14 +828,17 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { }; let trait_did = bound.0.def_id; - let item = tcx.associated_items(trait_did).find(|i| i.name == assoc_name) - .expect("missing associated type"); + let (assoc_ident, def_scope) = tcx.adjust(assoc_name, trait_did, ref_id); + let item = tcx.associated_items(trait_did).find(|i| { + Namespace::from(i.kind) == Namespace::Type && + i.name.to_ident() == assoc_ident + }) + .expect("missing associated type"); let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, bound); let ty = self.normalize_ty(span, ty); let def = Def::AssociatedTy(item.def_id); - let def_scope = tcx.adjust(assoc_name, item.container.id(), ref_id).1; if !item.vis.is_accessible_from(def_scope, tcx) { let msg = format!("{} `{}` is private", def.kind_name(), assoc_name); tcx.sess.span_err(span, &msg); @@ -858,7 +859,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let tcx = self.tcx(); let trait_def_id = tcx.parent_def_id(item_def_id).unwrap(); - self.prohibit_type_params(slice::ref_slice(item_segment)); + self.prohibit_type_params(slice::from_ref(item_segment)); let self_ty = if let Some(ty) = opt_self_ty { ty @@ -928,7 +929,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let span = path.span; match path.def { - Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) | Def::Union(did) => { + Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) | + Def::Union(did) | Def::TyForeign(did) => { assert_eq!(opt_self_ty, None); self.prohibit_type_params(path.segments.split_last().unwrap().1); self.ast_path_to_ty(span, did, path.segments.last().unwrap()) @@ -988,16 +990,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } Def::Err => { - for segment in &path.segments { - segment.with_parameters(|parameters| { - for ty in ¶meters.types { - self.ast_ty_to_ty(ty); - } - for binding in ¶meters.bindings { - self.ast_ty_to_ty(&binding.ty); - } - }); - } self.set_tainted_by_errors(); return self.tcx().types.err; } @@ -1042,53 +1034,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { hir::TyTraitObject(ref bounds, ref lifetime) => { self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) } - hir::TyImplTrait(_) => { - // Figure out if we can allow an `impl Trait` here, by walking up - // to a `fn` or inherent `impl` method, going only through `Ty` - // or `TraitRef` nodes (as nothing else should be in types) and - // ensuring that we reach the `fn`/method signature's return type. - let mut node_id = ast_ty.id; - let fn_decl = loop { - let parent = tcx.hir.get_parent_node(node_id); - match tcx.hir.get(parent) { - hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(ref fn_decl, ..), .. - }) => break Some(fn_decl), - - hir::map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(ref sig, _), .. - }) => { - match tcx.hir.expect_item(tcx.hir.get_parent(parent)).node { - hir::ItemImpl(.., None, _, _) => { - break Some(&sig.decl) - } - _ => break None - } - } - - hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => {} - - _ => break None - } - node_id = parent; - }; - let allow = fn_decl.map_or(false, |fd| { - match fd.output { - hir::DefaultReturn(_) => false, - hir::Return(ref ty) => ty.id == node_id - } - }); - - // Create the anonymized type. - if allow { - let def_id = tcx.hir.local_def_id(ast_ty.id); - tcx.mk_anon(def_id, Substs::identity_for_item(tcx, def_id)) - } else { - span_err!(tcx.sess, ast_ty.span, E0562, - "`impl Trait` not allowed outside of function \ - and inherent method return types"); - tcx.types.err - } + hir::TyImplTraitExistential(_, ref lifetimes) => { + let def_id = tcx.hir.local_def_id(ast_ty.id); + self.impl_trait_ty_to_ty(def_id, lifetimes) + } + hir::TyImplTraitUniversal(fn_def_id, _) => { + let impl_trait_def_id = tcx.hir.local_def_id(ast_ty.id); + let generics = tcx.generics_of(fn_def_id); + let index = generics.type_param_to_index[&impl_trait_def_id.index]; + tcx.mk_param(index, + Symbol::intern(&tcx.hir.node_to_pretty_string(ast_ty.id))) } hir::TyPath(hir::QPath::Resolved(ref maybe_qself, ref path)) => { debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); @@ -1142,6 +1097,43 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { result_ty } + pub fn impl_trait_ty_to_ty(&self, def_id: DefId, lifetimes: &[hir::Lifetime]) -> Ty<'tcx> { + debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); + let tcx = self.tcx(); + let generics = tcx.generics_of(def_id); + + // Fill in the substs of the parent generics + debug!("impl_trait_ty_to_ty: generics={:?}", generics); + let mut substs = Vec::with_capacity(generics.count()); + if let Some(parent_id) = generics.parent { + let parent_generics = tcx.generics_of(parent_id); + Substs::fill_item( + &mut substs, tcx, parent_generics, + &mut |def, _| tcx.mk_region( + ty::ReEarlyBound(def.to_early_bound_region_data())), + &mut |def, _| tcx.mk_param_from_def(def) + ); + + // Replace all lifetimes with 'static + for subst in &mut substs { + if let Some(_) = subst.as_region() { + *subst = Kind::from(&RegionKind::ReStatic); + } + } + debug!("impl_trait_ty_to_ty: substs from parent = {:?}", substs); + } + assert_eq!(substs.len(), generics.parent_count()); + + // Fill in our own generics with the resolved lifetimes + assert_eq!(lifetimes.len(), generics.own_count()); + substs.extend(lifetimes.iter().map(|lt| + Kind::from(self.ast_region_to_region(lt, None)))); + + debug!("impl_trait_ty_to_ty: final substs = {:?}", substs); + + tcx.mk_anon(def_id, tcx.intern_substs(&substs)) + } + pub fn ty_of_arg(&self, ty: &hir::Ty, expected_ty: Option>) @@ -1215,60 +1207,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { bare_fn_ty } - pub fn ty_of_closure(&self, - unsafety: hir::Unsafety, - decl: &hir::FnDecl, - abi: abi::Abi, - expected_sig: Option>) - -> ty::PolyFnSig<'tcx> - { - debug!("ty_of_closure(expected_sig={:?})", - expected_sig); - - let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| { - let expected_arg_ty = expected_sig.as_ref().and_then(|e| { - // no guarantee that the correct number of expected args - // were supplied - if i < e.inputs().len() { - Some(e.inputs()[i]) - } else { - None - } - }); - self.ty_of_arg(a, expected_arg_ty) - }); - - let expected_ret_ty = expected_sig.as_ref().map(|e| e.output()); - - let output_ty = match decl.output { - hir::Return(ref output) => { - if let (&hir::TyInfer, Some(expected_ret_ty)) = (&output.node, expected_ret_ty) { - self.record_ty(output.hir_id, expected_ret_ty, output.span); - expected_ret_ty - } else { - self.ast_ty_to_ty(&output) - } - } - hir::DefaultReturn(span) => { - if let Some(expected_ret_ty) = expected_ret_ty { - expected_ret_ty - } else { - self.ty_infer(span) - } - } - }; - - debug!("ty_of_closure: output_ty={:?}", output_ty); - - ty::Binder(self.tcx().mk_fn_sig( - input_tys, - output_ty, - decl.variadic, - unsafety, - abi - )) - } - /// Given the bounds on an object, determines what single region bound (if any) we can /// use to summarize this type. The basic idea is that we will use the bound the user /// provided, if they provided one, and otherwise search the supertypes of trait bounds @@ -1320,27 +1258,10 @@ fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, -> (Vec, Vec<&'b hir::PolyTraitRef>) { let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.iter().partition(|bound| { + // Checks whether `trait_did` is an auto trait and adds it to `auto_traits` if so. match bound.trait_ref.path.def { - Def::Trait(trait_did) => { - // Checks whether `trait_did` refers to one of the builtin - // traits, like `Send`, and adds it to `auto_traits` if so. - if Some(trait_did) == tcx.lang_items().send_trait() || - Some(trait_did) == tcx.lang_items().sync_trait() { - let segments = &bound.trait_ref.path.segments; - segments[segments.len() - 1].with_parameters(|parameters| { - if !parameters.types.is_empty() { - check_type_argument_count(tcx, bound.trait_ref.path.span, - parameters.types.len(), &[]); - } - if !parameters.lifetimes.is_empty() { - report_lifetime_number_error(tcx, bound.trait_ref.path.span, - parameters.lifetimes.len(), 0); - } - }); - true - } else { - false - } + Def::Trait(trait_did) if tcx.trait_is_auto(trait_did) => { + true } _ => false } @@ -1466,64 +1387,3 @@ impl<'a, 'gcx, 'tcx> Bounds<'tcx> { vec } } - -pub enum ExplicitSelf<'tcx> { - ByValue, - ByReference(ty::Region<'tcx>, hir::Mutability), - ByBox -} - -impl<'tcx> ExplicitSelf<'tcx> { - /// We wish to (for now) categorize an explicit self - /// declaration like `self: SomeType` into either `self`, - /// `&self`, `&mut self`, or `Box`. We do this here - /// by some simple pattern matching. A more precise check - /// is done later in `check_method_self_type()`. - /// - /// Examples: - /// - /// ``` - /// impl Foo for &T { - /// // Legal declarations: - /// fn method1(self: &&T); // ExplicitSelf::ByReference - /// fn method2(self: &T); // ExplicitSelf::ByValue - /// fn method3(self: Box<&T>); // ExplicitSelf::ByBox - /// - /// // Invalid cases will be caught later by `check_method_self_type`: - /// fn method_err1(self: &mut T); // ExplicitSelf::ByReference - /// } - /// ``` - /// - /// To do the check we just count the number of "modifiers" - /// on each type and compare them. If they are the same or - /// the impl has more, we call it "by value". Otherwise, we - /// look at the outermost modifier on the method decl and - /// call it by-ref, by-box as appropriate. For method1, for - /// example, the impl type has one modifier, but the method - /// type has two, so we end up with - /// ExplicitSelf::ByReference. - pub fn determine(untransformed_self_ty: Ty<'tcx>, - self_arg_ty: Ty<'tcx>) - -> ExplicitSelf<'tcx> { - fn count_modifiers(ty: Ty) -> usize { - match ty.sty { - ty::TyRef(_, mt) => count_modifiers(mt.ty) + 1, - ty::TyAdt(def, _) if def.is_box() => count_modifiers(ty.boxed_ty()) + 1, - _ => 0, - } - } - - let impl_modifiers = count_modifiers(untransformed_self_ty); - let method_modifiers = count_modifiers(self_arg_ty); - - if impl_modifiers >= method_modifiers { - ExplicitSelf::ByValue - } else { - match self_arg_ty.sty { - ty::TyRef(r, mt) => ExplicitSelf::ByReference(r, mt.mutbl), - ty::TyAdt(def, _) if def.is_box() => ExplicitSelf::ByBox, - _ => ExplicitSelf::ByValue, - } - } - } -} diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index d942b2d123070..d5e4aa69c5b4e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -23,29 +23,148 @@ use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::cmp; use syntax::ast; use syntax::codemap::Spanned; +use syntax::feature_gate; use syntax::ptr::P; 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) { + pub fn check_pat_walk( + &self, + pat: &'gcx hir::Pat, + mut expected: Ty<'tcx>, + mut def_bm: ty::BindingMode, + is_arg: bool) + { let tcx = self.tcx; - debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg); + debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?},is_arg={})", + pat, expected, def_bm, is_arg); + + let is_non_ref_pat = match pat.node { + PatKind::Struct(..) | + PatKind::TupleStruct(..) | + PatKind::Tuple(..) | + PatKind::Box(_) | + PatKind::Range(..) | + PatKind::Slice(..) => true, + PatKind::Lit(ref lt) => { + let ty = self.check_expr(lt); + match ty.sty { + ty::TypeVariants::TyRef(..) => false, + _ => true, + } + } + PatKind::Path(ref qpath) => { + let (def, _, _) = self.resolve_ty_and_def_ufcs(qpath, pat.id, pat.span); + match def { + Def::Const(..) | Def::AssociatedConst(..) => false, + _ => true, + } + } + PatKind::Wild | + PatKind::Binding(..) | + PatKind::Ref(..) => false, + }; + if is_non_ref_pat { + debug!("pattern is non reference pattern"); + let mut exp_ty = self.resolve_type_vars_with_obligations(&expected); + + // Peel off as many `&` or `&mut` from the discriminant as possible. For example, + // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches + // the `Some(5)` which is not of type TyRef. + // + // For each ampersand peeled off, update the binding mode and push the original + // type into the adjustments vector. + // + // See the examples in `run-pass/match-defbm*.rs`. + let mut pat_adjustments = vec![]; + expected = loop { + debug!("inspecting {:?} with type {:?}", exp_ty, exp_ty.sty); + match exp_ty.sty { + ty::TypeVariants::TyRef(_, ty::TypeAndMut{ + ty: inner_ty, mutbl: inner_mutability, + }) => { + debug!("current discriminant is TyRef, inserting implicit deref"); + // Preserve the reference type. We'll need it later during HAIR lowering. + pat_adjustments.push(exp_ty); + + exp_ty = inner_ty; + def_bm = match def_bm { + // If default binding mode is by value, make it `ref` or `ref mut` + // (depending on whether we observe `&` or `&mut`). + ty::BindByValue(_) => + ty::BindByReference(inner_mutability), + + // Once a `ref`, always a `ref`. This is because a `& &mut` can't mutate + // the underlying value. + ty::BindByReference(hir::Mutability::MutImmutable) => + ty::BindByReference(hir::Mutability::MutImmutable), + + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` + // (on `&`). + ty::BindByReference(hir::Mutability::MutMutable) => + ty::BindByReference(inner_mutability), + }; + }, + _ => break exp_ty, + } + }; + if pat_adjustments.len() > 0 { + if tcx.sess.features.borrow().match_default_bindings { + debug!("default binding mode is now {:?}", def_bm); + self.inh.tables.borrow_mut() + .pat_adjustments_mut() + .insert(pat.hir_id, pat_adjustments); + } else { + let mut ref_sp = pat.span; + let mut id = pat.id; + loop { // make span include all enclosing `&` to avoid confusing diag output + id = tcx.hir.get_parent_node(id); + let node = tcx.hir.find(id); + if let Some(hir::map::NodePat(pat)) = node { + if let hir::PatKind::Ref(..) = pat.node { + ref_sp = pat.span; + } else { + break; + } + } else { + break; + } + } + let sp = ref_sp.to(pat.span); + let mut err = feature_gate::feature_err( + &tcx.sess.parse_sess, + "match_default_bindings", + sp, + feature_gate::GateIssue::Language, + "non-reference pattern used to match a reference", + ); + if let Ok(snippet) = tcx.sess.codemap().span_to_snippet(sp) { + err.span_suggestion(sp, + "consider using a reference", + format!("&{}", &snippet)); + } + err.emit(); + } + } + } + + // Lose mutability now that we know binding mode and discriminant type. + let def_bm = def_bm; + let expected = expected; let ty = match pat.node { PatKind::Wild => { expected } PatKind::Lit(ref lt) => { - let ty = self.check_expr(<); + // We've already computed the type above (when checking for a non-ref pat), so + // avoid computing it again. + let ty = self.node_ty(lt.hir_id); // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically sized byte arrays @@ -114,10 +233,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { common_type } PatKind::Binding(ba, var_id, _, ref sub) => { - // Note the binding mode in the typeck tables. For now, what we store is always - // identical to what could be scraped from the HIR, but this will change with - // default binding modes (#42640). - let bm = ty::BindingMode::convert(ba); + let bm = if ba == hir::BindingAnnotation::Unannotated { + def_bm + } else { + ty::BindingMode::convert(ba) + }; self.inh .tables .borrow_mut() @@ -155,19 +275,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } if let Some(ref p) = *sub { - self.check_pat(&p, expected); + self.check_pat_walk(&p, expected, def_bm, true); } typ } PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected) + self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected, def_bm) } PatKind::Path(ref qpath) => { self.check_pat_path(pat, qpath, expected) } PatKind::Struct(ref qpath, ref fields, etc) => { - self.check_pat_struct(pat, qpath, fields, etc, expected) + self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm) } PatKind::Tuple(ref elements, ddpos) => { let mut expected_len = elements.len(); @@ -188,7 +308,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let pat_ty = tcx.mk_ty(ty::TyTuple(element_tys, false)); self.demand_eqtype(pat.span, expected, pat_ty); for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &element_tys[i]); + self.check_pat_walk(elem, &element_tys[i], def_bm, true); } pat_ty } @@ -201,10 +321,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // think any errors can be introduced by using // `demand::eqtype`. self.demand_eqtype(pat.span, expected, uniq_ty); - self.check_pat(&inner, inner_ty); + self.check_pat_walk(&inner, inner_ty, def_bm, true); uniq_ty } else { - self.check_pat(&inner, tcx.types.err); + self.check_pat_walk(&inner, tcx.types.err, def_bm, true); tcx.types.err } } @@ -219,7 +339,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); + debug!("check_pat_walk: expected={:?}", expected); let (rptr_ty, inner_ty) = match expected.sty { ty::TyRef(_, mt) if mt.mutbl == mutbl => { (expected, mt.ty) @@ -230,7 +350,7 @@ 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); - debug!("check_pat_arg: demanding {:?} = {:?}", expected, rptr_ty); + debug!("check_pat_walk: 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 @@ -238,8 +358,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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) + if let Ok(snippet) = tcx.sess.codemap() + .span_to_snippet(pat.span) { err.help(&format!("did you mean `{}: &{}`?", &snippet[1..], @@ -253,10 +373,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - self.check_pat(&inner, inner_ty); + self.check_pat_walk(&inner, inner_ty, def_bm, true); rptr_ty } else { - self.check_pat(&inner, tcx.types.err); + self.check_pat_walk(&inner, tcx.types.err, def_bm, true); tcx.types.err } } @@ -314,13 +434,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; for elt in before { - self.check_pat(&elt, inner_ty); + self.check_pat_walk(&elt, inner_ty, def_bm, true); } if let Some(ref slice) = *slice { - self.check_pat(&slice, slice_ty); + self.check_pat_walk(&slice, slice_ty, def_bm, true); } for elt in after { - self.check_pat(&elt, inner_ty); + self.check_pat_walk(&elt, inner_ty, def_bm, true); } expected_ty } @@ -329,7 +449,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.write_ty(pat.hir_id, ty); // (*) In most of the cases above (literals and constants being - // the exception), we relate types using strict equality, evewn + // the exception), we relate types using strict equality, even // though subtyping would be sufficient. There are a few reasons // for this, some of which are fairly subtle and which cost me // (nmatsakis) an hour or two debugging to remember, so I thought @@ -339,7 +459,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // cause some inconvenience. What we are saying is that the type // of `x` becomes *exactly* what is expected. This can cause unnecessary // errors in some cases, such as this one: - // it will cause errors in a case like this: // // ``` // fn foo<'x>(x: &'x int) { @@ -370,7 +489,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // // 2. Things go horribly wrong if we use subtype. The reason for // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_inference`, as well as the test + // `givens` field in `region_constraints`, as well as the test // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, // for details. Short version is that we must sometimes detect // relationships between specific region variables and regions @@ -409,11 +528,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // use the *precise* type of the discriminant, *not* some supertype, as // the "discriminant type" (issue #23116). // - // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which - // is problematic as the HIR is being scraped, but ref bindings may be - // implicit after #42640. We need to make sure that pat_adjustments - // (once introduced) is populated by the time we get here. - // // arielb1 [writes here in this comment thread][c] that there // is certainly *some* potential danger, e.g. for an example // like: @@ -455,7 +569,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // assert_eq!(foo.0.0, 42); // } // ``` - + // + // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which + // is problematic as the HIR is being scraped, but ref bindings may be + // implicit after #42640. We need to make sure that pat_adjustments + // (once introduced) is populated by the time we get here. + // + // See #44848. let contains_ref_bindings = arms.iter() .filter_map(|a| a.contains_explicit_ref_binding()) .max_by_key(|m| match *m { @@ -495,7 +615,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut all_pats_diverge = Diverges::WarnedAlways; for p in &arm.pats { self.diverges.set(Diverges::Maybe); - self.check_pat(&p, discrim_ty); + self.check_pat_walk(&p, discrim_ty, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); all_pats_diverge &= self.diverges.get(); } @@ -576,14 +697,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { qpath: &hir::QPath, fields: &'gcx [Spanned], etc: bool, - expected: Ty<'tcx>) -> Ty<'tcx> + expected: Ty<'tcx>, + def_bm: ty::BindingMode) -> Ty<'tcx> { // Resolve the path and check the definition for errors. let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.id) { variant_ty } else { for field in fields { - self.check_pat(&field.node.pat, self.tcx.types.err); + self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, true); } return self.tcx.types.err; }; @@ -592,7 +714,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.demand_eqtype(pat.span, expected, pat_ty); // Type check subpatterns. - self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc); + self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc, def_bm); pat_ty } @@ -637,12 +759,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { qpath: &hir::QPath, subpats: &'gcx [P], ddpos: Option, - expected: Ty<'tcx>) -> Ty<'tcx> + expected: Ty<'tcx>, + def_bm: ty::BindingMode) -> Ty<'tcx> { let tcx = self.tcx; let on_error = || { for pat in subpats { - self.check_pat(&pat, tcx.types.err); + self.check_pat_walk(&pat, tcx.types.err, def_bm, true); } }; let report_unexpected_def = |def: Def| { @@ -677,7 +800,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let pat_ty = self.instantiate_value_path(segments, opt_ty, def, pat.span, pat.id); // Replace constructor type with constructed type for tuple struct patterns. let pat_ty = pat_ty.fn_sig(tcx).output(); - let pat_ty = tcx.no_late_bound_regions(&pat_ty).expect("expected fn type"); + let pat_ty = pat_ty.no_late_bound_regions().expect("expected fn type"); + self.demand_eqtype(pat.span, expected, pat_ty); // Type check subpatterns. @@ -689,7 +813,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(&subpat, field_ty); + self.check_pat_walk(&subpat, field_ty, def_bm, true); self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span); } @@ -715,13 +839,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, fields: &'gcx [Spanned], - etc: bool) { + etc: bool, + def_bm: ty::BindingMode) { let tcx = self.tcx; - let (substs, kind_name) = match adt_ty.sty { - ty::TyAdt(adt, substs) => (substs, adt.variant_descr()), + let (substs, adt) = match adt_ty.sty { + ty::TyAdt(adt, substs) => (substs, adt), _ => span_bug!(span, "struct pattern is not an ADT") }; + let kind_name = adt.variant_descr(); // Index the struct fields' types. let field_map = variant.fields @@ -772,7 +898,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - self.check_pat(&field.pat, field_ty); + self.check_pat_walk(&field.pat, field_ty, def_bm, true); + } + + // Require `..` if struct has non_exhaustive attribute. + if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc { + span_err!(tcx.sess, span, E0638, + "`..` required with {} marked as non-exhaustive", + kind_name); } // Report an error if incorrect number of the fields were specified. diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 7461df8bda509..8f409b687526b 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -108,7 +108,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Check whether this is a call to a closure where we // haven't yet decided on whether the closure is fn vs // fnmut vs fnonce. If so, we have to defer further processing. - if self.closure_kind(def_id).is_none() { + if self.closure_kind(def_id, substs).is_none() { let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs); let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span, infer::FnCall, @@ -122,6 +122,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { adjustments, fn_sig, closure_def_id: def_id, + closure_substs: substs, }); return Some(CallStep::DeferredClosure(fn_sig)); } @@ -271,6 +272,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn_sig.output(), fn_sig.inputs()); self.check_argument_types(call_expr.span, + call_expr.span, fn_sig.inputs(), &expected_arg_tys[..], arg_exprs, @@ -298,6 +300,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn_sig.inputs()); self.check_argument_types(call_expr.span, + call_expr.span, fn_sig.inputs(), &expected_arg_tys, arg_exprs, @@ -315,6 +318,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { method_callee: MethodCallee<'tcx>) -> Ty<'tcx> { let output_type = self.check_method_argument_types(call_expr.span, + call_expr.span, Ok(method_callee), arg_exprs, TupleArgumentsFlag::TupleArguments, @@ -333,6 +337,7 @@ pub struct DeferredCallResolution<'gcx: 'tcx, 'tcx> { adjustments: Vec>, fn_sig: ty::FnSig<'tcx>, closure_def_id: DefId, + closure_substs: ty::ClosureSubsts<'tcx>, } impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> { @@ -341,7 +346,7 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> { // we should not be invoked until the closure kind has been // determined by upvar inference - assert!(fcx.closure_kind(self.closure_def_id).is_some()); + assert!(fcx.closure_kind(self.closure_def_id, self.closure_substs).is_some()); // We may now know enough to figure out fn vs fnmut etc. match fcx.try_overloaded_call_traits(self.call_expr, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 9c6cacb9d25f9..d68c139894b92 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -13,7 +13,7 @@ //! A cast `e as U` is valid if one of the following holds: //! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* //! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or -//! unsize_kind(`T`) = unsize_kind(`U_0`); *ptr-ptr-cast* +//! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* //! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* //! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* //! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* @@ -26,7 +26,7 @@ //! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* //! //! where `&.T` and `*T` are references of either mutability, -//! and where unsize_kind(`T`) is the kind of the unsize info +//! and where pointer_kind(`T`) is the kind of the unsize info //! in `T` - the vtable for a trait definition (e.g. `fmt::Display` or //! `Iterator`, not `Iterator`) or a length (or `()` if `T: Sized`). //! @@ -64,11 +64,16 @@ pub struct CastCheck<'tcx> { span: Span, } -/// The kind of the unsize info (length or vtable) - we only allow casts between -/// fat pointers if their unsize-infos have the same kind. +/// The kind of pointer and associated metadata (thin, length or vtable) - we +/// only allow casts between fat pointers if their metadata have the same +/// kind. #[derive(Copy, Clone, PartialEq, Eq)] -enum UnsizeKind<'tcx> { +enum PointerKind<'tcx> { + /// No metadata attached, ie pointer to sized type or foreign type + Thin, + /// A trait object Vtable(Option), + /// Slice Length, /// The unsize info of this projection OfProjection(&'tcx ty::ProjectionTy<'tcx>), @@ -78,23 +83,31 @@ enum UnsizeKind<'tcx> { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Returns the kind of unsize information of t, or None - /// if t is sized or it is unknown. - fn unsize_kind(&self, t: Ty<'tcx>) -> Option> { + /// if t is unknown. + fn pointer_kind(&self, t: Ty<'tcx>, span: Span) -> Option> { + if self.type_is_known_to_be_sized(t, span) { + return Some(PointerKind::Thin); + } + match t.sty { - ty::TySlice(_) | ty::TyStr => Some(UnsizeKind::Length), + ty::TySlice(_) | ty::TyStr => Some(PointerKind::Length), ty::TyDynamic(ref tty, ..) => - Some(UnsizeKind::Vtable(tty.principal().map(|p| p.def_id()))), + Some(PointerKind::Vtable(tty.principal().map(|p| p.def_id()))), ty::TyAdt(def, substs) if def.is_struct() => { // FIXME(arielb1): do some kind of normalization match def.struct_variant().fields.last() { - None => None, - Some(f) => self.unsize_kind(f.ty(self.tcx, substs)), + None => Some(PointerKind::Thin), + Some(f) => self.pointer_kind(f.ty(self.tcx, substs), span), } } + // Pointers to foreign types are thin, despite being unsized + ty::TyForeign(..) => Some(PointerKind::Thin), // We should really try to normalize here. - ty::TyProjection(ref pi) => Some(UnsizeKind::OfProjection(pi)), - ty::TyParam(ref p) => Some(UnsizeKind::OfParam(p)), - _ => None, + ty::TyProjection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::TyParam(ref p) => Some(PointerKind::OfParam(p)), + // Insufficient type information. + ty::TyInfer(_) => None, + _ => panic!(), } } } @@ -112,6 +125,8 @@ enum CastError { NeedViaThinPtr, NeedViaInt, NonScalar, + UnknownExprPtrKind, + UnknownCastPtrKind, } fn make_invalid_casting_error<'a, 'gcx, 'tcx>(sess: &'a Session, @@ -230,6 +245,25 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { self.expr_ty, fcx.ty_to_string(self.cast_ty)).emit(); } + CastError::UnknownCastPtrKind | + CastError::UnknownExprPtrKind => { + let unknown_cast_to = match e { + CastError::UnknownCastPtrKind => true, + CastError::UnknownExprPtrKind => false, + _ => bug!(), + }; + let mut err = struct_span_err!(fcx.tcx.sess, self.span, E0641, + "cannot cast {} a pointer of an unknown kind", + if unknown_cast_to { "to" } else { "from" }); + err.note("The type information given here is insufficient to check whether \ + the pointer cast is valid"); + if unknown_cast_to { + err.span_suggestion_short(self.cast_span, + "consider giving more type information", + String::new()); + } + err.emit(); + } } } @@ -446,20 +480,36 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); // ptr-ptr cast. vtables must match. - // Cast to sized is OK - if fcx.type_is_known_to_be_sized(m_cast.ty, self.span) { + let expr_kind = fcx.pointer_kind(m_expr.ty, self.span); + let cast_kind = fcx.pointer_kind(m_cast.ty, self.span); + + let cast_kind = match cast_kind { + // We can't cast if target pointer kind is unknown + None => return Err(CastError::UnknownCastPtrKind), + Some(cast_kind) => cast_kind, + }; + + // Cast to thin pointer is OK + if cast_kind == PointerKind::Thin { return Ok(CastKind::PtrPtrCast); } - // sized -> unsized? report invalid cast (don't complain about vtable kinds) - if fcx.type_is_known_to_be_sized(m_expr.ty, self.span) { + let expr_kind = match expr_kind { + // We can't cast to fat pointer if source pointer kind is unknown + None => return Err(CastError::UnknownExprPtrKind), + Some(expr_kind) => expr_kind, + }; + + // thin -> fat? report invalid cast (don't complain about vtable kinds) + if expr_kind == PointerKind::Thin { return Err(CastError::SizedUnsizedCast); } // vtable kinds must match - match (fcx.unsize_kind(m_cast.ty), fcx.unsize_kind(m_expr.ty)) { - (Some(a), Some(b)) if a == b => Ok(CastKind::PtrPtrCast), - _ => Err(CastError::DifferingKinds), + if cast_kind == expr_kind { + Ok(CastKind::PtrPtrCast) + } else { + Err(CastError::DifferingKinds) } } @@ -467,12 +517,12 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'gcx, 'tcx>, m_cast: &'tcx ty::TypeAndMut<'tcx>) -> Result { - // fptr-ptr cast. must be to sized ptr + // fptr-ptr cast. must be to thin ptr - if fcx.type_is_known_to_be_sized(m_cast.ty, self.span) { - Ok(CastKind::FnPtrPtrCast) - } else { - Err(CastError::IllegalCast) + match fcx.pointer_kind(m_cast.ty, self.span) { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), + _ => Err(CastError::IllegalCast), } } @@ -480,12 +530,12 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'gcx, 'tcx>, m_expr: &'tcx ty::TypeAndMut<'tcx>) -> Result { - // ptr-addr cast. must be from sized ptr + // ptr-addr cast. must be from thin ptr - if fcx.type_is_known_to_be_sized(m_expr.ty, self.span) { - Ok(CastKind::PtrAddrCast) - } else { - Err(CastError::NeedViaThinPtr) + match fcx.pointer_kind(m_expr.ty, self.span) { + None => Err(CastError::UnknownExprPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), + _ => Err(CastError::NeedViaThinPtr), } } @@ -519,10 +569,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { m_cast: &'tcx ty::TypeAndMut<'tcx>) -> Result { // ptr-addr cast. pointer must be thin. - if fcx.type_is_known_to_be_sized(m_cast.ty, self.span) { - Ok(CastKind::AddrPtrCast) - } else { - Err(CastError::IllegalCast) + match fcx.pointer_kind(m_cast.ty, self.span) { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), + _ => Err(CastError::IllegalCast), } } diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 07159770d5ba2..147347a75abe8 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -10,28 +10,40 @@ //! Code for type-checking closure expressions. -use super::{check_fn, Expectation, FnCtxt}; +use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use astconv::AstConv; +use rustc::hir::def_id::DefId; +use rustc::infer::{InferOk, InferResult}; +use rustc::infer::LateBoundRegionConversionTime; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::ty::{self, ToPolyTraitRef, Ty}; use rustc::ty::subst::Substs; +use rustc::ty::TypeFoldable; use std::cmp; use std::iter; use syntax::abi::Abi; use rustc::hir; +struct ClosureSignatures<'tcx> { + bound_sig: ty::PolyFnSig<'tcx>, + liberated_sig: ty::FnSig<'tcx>, +} + impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn check_expr_closure(&self, - expr: &hir::Expr, - _capture: hir::CaptureClause, - decl: &'gcx hir::FnDecl, - body_id: hir::BodyId, - expected: Expectation<'tcx>) - -> Ty<'tcx> { - debug!("check_expr_closure(expr={:?},expected={:?})", - expr, - expected); + pub fn check_expr_closure( + &self, + expr: &hir::Expr, + _capture: hir::CaptureClause, + decl: &'gcx hir::FnDecl, + body_id: hir::BodyId, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + debug!( + "check_expr_closure(expr={:?},expected={:?})", + expr, + expected + ); // It's always helpful for inference if we know the kind of // closure sooner rather than later, so first examine the expected @@ -44,101 +56,125 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_closure(expr, expected_kind, decl, body, expected_sig) } - fn check_closure(&self, - expr: &hir::Expr, - opt_kind: Option, - decl: &'gcx hir::FnDecl, - body: &'gcx hir::Body, - expected_sig: Option>) - -> Ty<'tcx> { - debug!("check_closure opt_kind={:?} expected_sig={:?}", - opt_kind, - expected_sig); + fn check_closure( + &self, + expr: &hir::Expr, + opt_kind: Option, + decl: &'gcx hir::FnDecl, + body: &'gcx hir::Body, + expected_sig: Option>, + ) -> Ty<'tcx> { + debug!( + "check_closure(opt_kind={:?}, expected_sig={:?})", + opt_kind, + expected_sig + ); let expr_def_id = self.tcx.hir.local_def_id(expr.id); - let sig = AstConv::ty_of_closure(self, - hir::Unsafety::Normal, - decl, - Abi::RustCall, - expected_sig); - // `deduce_expectations_from_expected_type` introduces late-bound - // lifetimes defined elsewhere, which we need to anonymize away. - let sig = self.tcx.anonymize_late_bound_regions(&sig); + + let ClosureSignatures { + bound_sig, + liberated_sig, + } = self.sig_of_closure(expr_def_id, decl, body, expected_sig); + + debug!("check_closure: ty_of_closure returns {:?}", liberated_sig); + + let generator_types = check_fn( + self, + self.param_env, + liberated_sig, + decl, + expr.id, + body, + true, + ).1; // Create type variables (for now) to represent the transformed // types of upvars. These will be unified during the upvar // inference phase (`upvar.rs`). - let base_substs = Substs::identity_for_item(self.tcx, - self.tcx.closure_base_def_id(expr_def_id)); - let substs = base_substs.extend_to(self.tcx, expr_def_id, - |_, _| span_bug!(expr.span, "closure has region param"), - |_, _| self.infcx.next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span)) + let base_substs = + Substs::identity_for_item(self.tcx, self.tcx.closure_base_def_id(expr_def_id)); + let substs = base_substs.extend_to( + self.tcx, + expr_def_id, + |_, _| span_bug!(expr.span, "closure has region param"), + |_, _| { + self.infcx + .next_ty_var(TypeVariableOrigin::ClosureSynthetic(expr.span)) + }, ); + let substs = ty::ClosureSubsts { substs }; + let closure_type = self.tcx.mk_closure(expr_def_id, substs); - let fn_sig = self.liberate_late_bound_regions(expr_def_id, &sig); - let fn_sig = self.inh.normalize_associated_types_in(body.value.span, - body.value.id, - self.param_env, - &fn_sig); - - let interior = check_fn(self, self.param_env, fn_sig, decl, expr.id, body, true).1; - - if let Some(interior) = interior { - let closure_substs = ty::ClosureSubsts { - substs: substs, - }; - return self.tcx.mk_generator(expr_def_id, closure_substs, interior); + if let Some(GeneratorTypes { yield_ty, interior }) = generator_types { + self.demand_eqtype(expr.span, + yield_ty, + substs.generator_yield_ty(expr_def_id, self.tcx)); + self.demand_eqtype(expr.span, + liberated_sig.output(), + substs.generator_return_ty(expr_def_id, self.tcx)); + return self.tcx.mk_generator(expr_def_id, substs, interior); } - let closure_type = self.tcx.mk_closure(expr_def_id, substs); - - debug!("check_closure: expr.id={:?} closure_type={:?}", expr.id, closure_type); + debug!( + "check_closure: expr.id={:?} closure_type={:?}", + expr.id, + closure_type + ); // Tuple up the arguments and insert the resulting function type into // the `closures` table. - let sig = sig.map_bound(|sig| self.tcx.mk_fn_sig( - iter::once(self.tcx.intern_tup(sig.inputs(), false)), - sig.output(), - sig.variadic, - sig.unsafety, - sig.abi - )); + let sig = bound_sig.map_bound(|sig| { + self.tcx.mk_fn_sig( + iter::once(self.tcx.intern_tup(sig.inputs(), false)), + sig.output(), + sig.variadic, + sig.unsafety, + sig.abi, + ) + }); + + debug!( + "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}", + expr_def_id, + sig, + opt_kind + ); - debug!("closure for {:?} --> sig={:?} opt_kind={:?}", - expr_def_id, - sig, - opt_kind); - - { - let mut tables = self.tables.borrow_mut(); - tables.closure_tys_mut().insert(expr.hir_id, sig); - match opt_kind { - Some(kind) => { - tables.closure_kinds_mut().insert(expr.hir_id, (kind, None)); - } - None => {} - } + let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig); + self.demand_eqtype(expr.span, + sig_fn_ptr_ty, + substs.closure_sig_ty(expr_def_id, self.tcx)); + + if let Some(kind) = opt_kind { + self.demand_eqtype(expr.span, + kind.to_ty(self.tcx), + substs.closure_kind_ty(expr_def_id, self.tcx)); } closure_type } - fn deduce_expectations_from_expected_type - (&self, - expected_ty: Ty<'tcx>) - -> (Option>, Option) { - debug!("deduce_expectations_from_expected_type(expected_ty={:?})", - expected_ty); + fn deduce_expectations_from_expected_type( + &self, + expected_ty: Ty<'tcx>, + ) -> (Option>, Option) { + debug!( + "deduce_expectations_from_expected_type(expected_ty={:?})", + expected_ty + ); match expected_ty.sty { ty::TyDynamic(ref object_type, ..) => { - let sig = object_type.projection_bounds() + let sig = object_type + .projection_bounds() .filter_map(|pb| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.err); self.deduce_sig_from_projection(&pb) }) .next(); - let kind = object_type.principal() + let kind = object_type + .principal() .and_then(|p| self.tcx.lang_items().fn_trait_kind(p.def_id())); (sig, kind) } @@ -148,19 +184,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - fn deduce_expectations_from_obligations - (&self, - expected_vid: ty::TyVid) - -> (Option>, Option) { + fn deduce_expectations_from_obligations( + &self, + expected_vid: ty::TyVid, + ) -> (Option>, Option) { let fulfillment_cx = self.fulfillment_cx.borrow(); // Here `expected_ty` is known to be a type inference variable. - let expected_sig = fulfillment_cx.pending_obligations() + let expected_sig = fulfillment_cx + .pending_obligations() .iter() .map(|obligation| &obligation.obligation) .filter_map(|obligation| { - debug!("deduce_expectations_from_obligations: obligation.predicate={:?}", - obligation.predicate); + debug!( + "deduce_expectations_from_obligations: obligation.predicate={:?}", + obligation.predicate + ); match obligation.predicate { // Given a Projection predicate, we can potentially infer @@ -179,7 +218,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // infer the kind. This can occur if there is a trait-reference // like `F : Fn`. Note that due to subtyping we could encounter // many viable options, so pick the most restrictive. - let expected_kind = fulfillment_cx.pending_obligations() + let expected_kind = fulfillment_cx + .pending_obligations() .iter() .map(|obligation| &obligation.obligation) .filter_map(|obligation| { @@ -204,20 +244,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // inference variable. ty::Predicate::ClosureKind(..) => None, }; - opt_trait_ref.and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid)) + opt_trait_ref + .and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid)) .and_then(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id())) }) - .fold(None, - |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); + .fold(None, |best, cur| { + Some(best.map_or(cur, |best| cmp::min(best, cur))) + }); (expected_sig, expected_kind) } /// Given a projection like "::Result == Y", we can deduce /// everything we need to know about a closure. - fn deduce_sig_from_projection(&self, - projection: &ty::PolyProjectionPredicate<'tcx>) - -> Option> { + fn deduce_sig_from_projection( + &self, + projection: &ty::PolyProjectionPredicate<'tcx>, + ) -> Option> { let tcx = self.tcx; debug!("deduce_sig_from_projection({:?})", projection); @@ -230,8 +273,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let arg_param_ty = trait_ref.substs().type_at(1); let arg_param_ty = self.resolve_type_vars_if_possible(&arg_param_ty); - debug!("deduce_sig_from_projection: arg_param_ty {:?}", - arg_param_ty); + debug!( + "deduce_sig_from_projection: arg_param_ty {:?}", + arg_param_ty + ); let input_tys = match arg_param_ty.sty { ty::TyTuple(tys, _) => tys.into_iter(), @@ -242,31 +287,291 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let ret_param_ty = projection.0.ty; let ret_param_ty = self.resolve_type_vars_if_possible(&ret_param_ty); - debug!("deduce_sig_from_projection: ret_param_ty {:?}", ret_param_ty); + debug!( + "deduce_sig_from_projection: ret_param_ty {:?}", + ret_param_ty + ); let fn_sig = self.tcx.mk_fn_sig( input_tys.cloned(), ret_param_ty, false, hir::Unsafety::Normal, - Abi::Rust + Abi::Rust, ); debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig); Some(fn_sig) } - fn self_type_matches_expected_vid(&self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid) - -> Option> { + fn self_type_matches_expected_vid( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid, + ) -> Option> { let self_ty = self.shallow_resolve(trait_ref.self_ty()); - debug!("self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})", - trait_ref, - self_ty); + debug!( + "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})", + trait_ref, + self_ty + ); match self_ty.sty { ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref), _ => None, } } + + fn sig_of_closure( + &self, + expr_def_id: DefId, + decl: &hir::FnDecl, + body: &hir::Body, + expected_sig: Option>, + ) -> ClosureSignatures<'tcx> { + if let Some(e) = expected_sig { + self.sig_of_closure_with_expectation(expr_def_id, decl, body, e) + } else { + self.sig_of_closure_no_expectation(expr_def_id, decl, body) + } + } + + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. + fn sig_of_closure_no_expectation( + &self, + expr_def_id: DefId, + decl: &hir::FnDecl, + body: &hir::Body, + ) -> ClosureSignatures<'tcx> { + debug!("sig_of_closure_no_expectation()"); + + let bound_sig = self.supplied_sig_of_closure(decl); + + self.closure_sigs(expr_def_id, body, bound_sig) + } + + /// Invoked to compute the signature of a closure expression. This + /// combines any user-provided type annotations (e.g., `|x: u32| + /// -> u32 { .. }`) with the expected signature. + /// + /// The approach is as follows: + /// + /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. + /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. + /// - If we have no expectation `E`, then the signature of the closure is `S`. + /// - Otherwise, the signature of the closure is E. Moreover: + /// - Skolemize the late-bound regions in `E`, yielding `E'`. + /// - Instantiate all the late-bound regions bound in the closure within `S` + /// with fresh (existential) variables, yielding `S'` + /// - Require that `E' = S'` + /// - We could use some kind of subtyping relationship here, + /// I imagine, but equality is easier and works fine for + /// our purposes. + /// + /// The key intuition here is that the user's types must be valid + /// from "the inside" of the closure, but the expectation + /// ultimately drives the overall signature. + /// + /// # Examples + /// + /// ``` + /// fn with_closure(_: F) + /// where F: Fn(&u32) -> &u32 { .. } + /// + /// with_closure(|x: &u32| { ... }) + /// ``` + /// + /// Here: + /// - E would be `fn(&u32) -> &u32`. + /// - S would be `fn(&u32) -> + /// - E' is `&'!0 u32 -> &'!0 u32` + /// - S' is `&'?0 u32 -> ?T` + /// + /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. + /// + /// # Arguments + /// + /// - `expr_def_id`: the def-id of the closure expression + /// - `decl`: the HIR declaration of the closure + /// - `body`: the body of the closure + /// - `expected_sig`: the expected signature (if any). Note that + /// this is missing a binder: that is, there may be late-bound + /// regions with depth 1, which are bound then by the closure. + fn sig_of_closure_with_expectation( + &self, + expr_def_id: DefId, + decl: &hir::FnDecl, + body: &hir::Body, + expected_sig: ty::FnSig<'tcx>, + ) -> ClosureSignatures<'tcx> { + debug!( + "sig_of_closure_with_expectation(expected_sig={:?})", + expected_sig + ); + + // Watch out for some surprises and just ignore the + // expectation if things don't see to match up with what we + // expect. + if expected_sig.variadic != decl.variadic { + return self.sig_of_closure_no_expectation(expr_def_id, decl, body); + } else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 { + // we could probably handle this case more gracefully + return self.sig_of_closure_no_expectation(expr_def_id, decl, body); + } + + // Create a `PolyFnSig`. Note the oddity that late bound + // regions appearing free in `expected_sig` are now bound up + // in this binder we are creating. + assert!(!expected_sig.has_regions_escaping_depth(1)); + let bound_sig = ty::Binder(self.tcx.mk_fn_sig( + expected_sig.inputs().iter().cloned(), + expected_sig.output(), + decl.variadic, + hir::Unsafety::Normal, + Abi::RustCall, + )); + + // `deduce_expectations_from_expected_type` introduces + // late-bound lifetimes defined elsewhere, which we now + // anonymize away, so as not to confuse the user. + let bound_sig = self.tcx.anonymize_late_bound_regions(&bound_sig); + + let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig); + + // Up till this point, we have ignored the annotations that the user + // gave. This function will check that they unify successfully. + // Along the way, it also writes out entries for types that the user + // wrote into our tables, which are then later used by the privacy + // check. + match self.check_supplied_sig_against_expectation(decl, &closure_sigs) { + Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), + Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body), + } + + closure_sigs + } + + /// Enforce the user's types against the expectation. See + /// `sig_of_closure_with_expectation` for details on the overall + /// strategy. + fn check_supplied_sig_against_expectation( + &self, + decl: &hir::FnDecl, + expected_sigs: &ClosureSignatures<'tcx>, + ) -> InferResult<'tcx, ()> { + // Get the signature S that the user gave. + // + // (See comment on `sig_of_closure_with_expectation` for the + // meaning of these letters.) + let supplied_sig = self.supplied_sig_of_closure(decl); + + debug!( + "check_supplied_sig_against_expectation: supplied_sig={:?}", + supplied_sig + ); + + // FIXME(#45727): As discussed in [this comment][c1], naively + // forcing equality here actually results in suboptimal error + // messages in some cases. For now, if there would have been + // an obvious error, we fallback to declaring the type of the + // closure to be the one the user gave, which allows other + // error message code to trigger. + // + // However, I think [there is potential to do even better + // here][c2], since in *this* code we have the precise span of + // the type parameter in question in hand when we report the + // error. + // + // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 + // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 + self.infcx.commit_if_ok(|_| { + let mut all_obligations = vec![]; + + // The liberated version of this signature should be be a subtype + // of the liberated form of the expectation. + for ((hir_ty, &supplied_ty), expected_ty) in decl.inputs.iter() + .zip(*supplied_sig.inputs().skip_binder()) // binder moved to (*) below + .zip(expected_sigs.liberated_sig.inputs()) + // `liberated_sig` is E'. + { + // Instantiate (this part of..) S to S', i.e., with fresh variables. + let (supplied_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var( + hir_ty.span, + LateBoundRegionConversionTime::FnCall, + &ty::Binder(supplied_ty), + ); // recreated from (*) above + + // Check that E' = S'. + let cause = &self.misc(hir_ty.span); + let InferOk { + value: (), + obligations, + } = self.at(cause, self.param_env) + .eq(*expected_ty, supplied_ty)?; + all_obligations.extend(obligations); + } + + let (supplied_output_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var( + decl.output.span(), + LateBoundRegionConversionTime::FnCall, + &supplied_sig.output(), + ); + let cause = &self.misc(decl.output.span()); + let InferOk { + value: (), + obligations, + } = self.at(cause, self.param_env) + .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; + all_obligations.extend(obligations); + + Ok(InferOk { + value: (), + obligations: all_obligations, + }) + }) + } + + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. + fn supplied_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> { + let astconv: &AstConv = self; + + // First, convert the types that the user supplied (if any). + let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); + let supplied_return = match decl.output { + hir::Return(ref output) => astconv.ast_ty_to_ty(&output), + hir::DefaultReturn(_) => astconv.ty_infer(decl.output.span()), + }; + + let result = ty::Binder(self.tcx.mk_fn_sig( + supplied_arguments, + supplied_return, + decl.variadic, + hir::Unsafety::Normal, + Abi::RustCall, + )); + + debug!("supplied_sig_of_closure: result={:?}", result); + + result + } + + fn closure_sigs( + &self, + expr_def_id: DefId, + body: &hir::Body, + bound_sig: ty::PolyFnSig<'tcx>, + ) -> ClosureSignatures<'tcx> { + let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig); + let liberated_sig = self.inh.normalize_associated_types_in( + body.value.span, + body.value.id, + self.param_env, + &liberated_sig, + ); + ClosureSignatures { + bound_sig, + liberated_sig, + } + } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 94422f93e5922..3e725d7ef415c 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -66,6 +66,7 @@ use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferResult, InferOk}; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::lint; use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::{self, LvaluePreference, TypeAndMut, @@ -754,7 +755,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // type, but only if the source expression diverges. if target.is_never() && expr_diverges.always() { debug!("permit coercion to `!` because expr diverges"); - return Ok(target); + if self.can_eq(self.param_env, source, target).is_err() { + self.tcx.lint_node( + lint::builtin::COERCE_NEVER, + expr.id, + expr.span, + &format!("cannot coerce `{}` to !", source) + ); + return Ok(target); + } } let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index b21d48886120c..70607bf4842a5 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -10,9 +10,8 @@ use rustc::hir::{self, ImplItemKind, TraitItemKind}; use rustc::infer::{self, InferOk}; -use rustc::middle::free_region::FreeRegionMap; -use rustc::middle::region; use rustc::ty::{self, TyCtxt}; +use rustc::ty::util::ExplicitSelf; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use rustc::ty::error::{ExpectedFound, TypeError}; use rustc::ty::subst::{Subst, Substs}; @@ -21,7 +20,6 @@ use rustc::util::common::ErrorReported; use syntax_pos::Span; use super::{Inherited, FnCtxt}; -use astconv::ExplicitSelf; /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -38,8 +36,7 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m_span: Span, trait_m: &ty::AssociatedItem, impl_trait_ref: ty::TraitRef<'tcx>, - trait_item_span: Option, - old_broken_mode: bool) { + trait_item_span: Option) { debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); @@ -67,12 +64,19 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, return; } + if let Err(ErrorReported) = compare_synthetic_generics(tcx, + impl_m, + impl_m_span, + trait_m, + trait_item_span) { + return; + } + if let Err(ErrorReported) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, - impl_trait_ref, - old_broken_mode) { + impl_trait_ref) { return; } } @@ -81,8 +85,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m: &ty::AssociatedItem, impl_m_span: Span, trait_m: &ty::AssociatedItem, - impl_trait_ref: ty::TraitRef<'tcx>, - old_broken_mode: bool) + impl_trait_ref: ty::TraitRef<'tcx>) -> Result<(), ErrorReported> { let trait_to_impl_substs = impl_trait_ref.substs; @@ -98,7 +101,6 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_name: impl_m.name, impl_item_def_id: impl_m.def_id, trait_item_def_id: trait_m.def_id, - lint_id: if !old_broken_mode { Some(impl_m_node_id) } else { None }, }, }; @@ -268,7 +270,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let impl_fty = tcx.mk_fn_ptr(ty::Binder(impl_sig)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); - let trait_sig = inh.liberate_late_bound_regions( + let trait_sig = tcx.liberate_late_bound_regions( impl_m.def_id, &tcx.fn_sig(trait_m.def_id)); let trait_sig = @@ -334,22 +336,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - if old_broken_mode { - // FIXME(#18937) -- this is how the code used to - // work. This is buggy because the fulfillment cx creates - // region obligations that get overlooked. The right - // thing to do is the code below. But we keep this old - // pass around temporarily. - let region_scope_tree = region::ScopeTree::default(); - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates(¶m_env.caller_bounds); - infcx.resolve_regions_and_report_errors(impl_m.def_id, - ®ion_scope_tree, - &free_regions); - } else { - let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); - fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); - } + let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); + fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); Ok(()) }) @@ -503,12 +491,17 @@ fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::TraitContainer(_) => tcx.mk_self_type() }; let self_arg_ty = *tcx.fn_sig(method.def_id).input(0).skip_binder(); - match ExplicitSelf::determine(untransformed_self_ty, self_arg_ty) { - ExplicitSelf::ByValue => "self".to_string(), - ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(), - ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(), - _ => format!("self: {}", self_arg_ty) - } + let param_env = ty::ParamEnv::empty(Reveal::All); + + tcx.infer_ctxt().enter(|infcx| { + let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); + match ExplicitSelf::determine(self_arg_ty, can_eq_self) { + ExplicitSelf::ByValue => "self".to_string(), + ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(), + ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(), + _ => format!("self: {}", self_arg_ty) + } + }) }; match (trait_m.method_has_self_argument, impl_m.method_has_self_argument) { @@ -568,15 +561,11 @@ fn compare_number_of_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let num_trait_m_type_params = trait_m_generics.types.len(); if num_impl_m_type_params != num_trait_m_type_params { let impl_m_node_id = tcx.hir.as_local_node_id(impl_m.def_id).unwrap(); - let span = match tcx.hir.expect_impl_item(impl_m_node_id).node { - ImplItemKind::Method(ref impl_m_sig, _) => { - if impl_m_sig.generics.is_parameterized() { - impl_m_sig.generics.span - } else { - impl_m_span - } - } - _ => bug!("{:?} is not a method", impl_m), + let impl_m_item = tcx.hir.expect_impl_item(impl_m_node_id); + let span = if impl_m_item.generics.is_parameterized() { + impl_m_item.generics.span + } else { + impl_m_span }; let mut err = struct_span_err!(tcx.sess, @@ -707,6 +696,45 @@ fn compare_number_of_method_arguments<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Ok(()) } +fn compare_synthetic_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + impl_m: &ty::AssociatedItem, + _impl_m_span: Span, // FIXME necessary? + trait_m: &ty::AssociatedItem, + _trait_item_span: Option) // FIXME necessary? + -> Result<(), ErrorReported> { + // FIXME(chrisvittal) Clean up this function, list of FIXME items: + // 1. Better messages for the span lables + // 2. Explanation as to what is going on + // 3. Correct the function signature for what we actually use + // If we get here, we already have the same number of generics, so the zip will + // be okay. + let mut error_found = false; + let impl_m_generics = tcx.generics_of(impl_m.def_id); + let trait_m_generics = tcx.generics_of(trait_m.def_id); + for (impl_ty, trait_ty) in impl_m_generics.types.iter().zip(trait_m_generics.types.iter()) { + if impl_ty.synthetic != trait_ty.synthetic { + let impl_node_id = tcx.hir.as_local_node_id(impl_ty.def_id).unwrap(); + let impl_span = tcx.hir.span(impl_node_id); + let trait_node_id = tcx.hir.as_local_node_id(trait_ty.def_id).unwrap(); + let trait_span = tcx.hir.span(trait_node_id); + let mut err = struct_span_err!(tcx.sess, + impl_span, + E0643, + "method `{}` has incompatible signature for trait", + trait_m.name); + err.span_label(trait_span, "annotation in trait"); + err.span_label(impl_span, "annotation in impl"); + err.emit(); + error_found = true; + } + } + if error_found { + Err(ErrorReported) + } else { + Ok(()) + } +} + pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_c: &ty::AssociatedItem, impl_c_span: Span, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 7110a1ba81d8f..4724a0fa51b97 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -74,10 +74,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) { - if let Some(mut err) = self.demand_coerce_diag(expr, checked_ty, expected) { + pub fn demand_coerce(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) + -> Ty<'tcx> { + let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected); + if let Some(mut err) = err { err.emit(); } + ty } // Checks that the type of `expr` can be coerced to `expected`. @@ -88,61 +94,62 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn demand_coerce_diag(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, - expected: Ty<'tcx>) -> Option> { + expected: Ty<'tcx>) + -> (Ty<'tcx>, Option>) { let expected = self.resolve_type_vars_with_obligations(expected); - if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { - let cause = self.misc(expr.span); - let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + let e = match self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { + Ok(ty) => return (ty, None), + Err(e) => e + }; - // If the expected type is an enum with any variants whose sole - // field is of the found type, suggest such variants. See Issue - // #42764. - if let ty::TyAdt(expected_adt, substs) = expected.sty { - let mut compatible_variants = vec![]; - for variant in &expected_adt.variants { - if variant.fields.len() == 1 { - let sole_field = &variant.fields[0]; - let sole_field_ty = sole_field.ty(self.tcx, substs); - if self.can_coerce(expr_ty, sole_field_ty) { - let mut variant_path = self.tcx.item_path_str(variant.did); - variant_path = variant_path.trim_left_matches("std::prelude::v1::") - .to_string(); - compatible_variants.push(variant_path); - } + let cause = self.misc(expr.span); + let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + + // If the expected type is an enum with any variants whose sole + // field is of the found type, suggest such variants. See Issue + // #42764. + if let ty::TyAdt(expected_adt, substs) = expected.sty { + let mut compatible_variants = vec![]; + for variant in &expected_adt.variants { + if variant.fields.len() == 1 { + let sole_field = &variant.fields[0]; + let sole_field_ty = sole_field.ty(self.tcx, substs); + if self.can_coerce(expr_ty, sole_field_ty) { + let mut variant_path = self.tcx.item_path_str(variant.did); + variant_path = variant_path.trim_left_matches("std::prelude::v1::") + .to_string(); + compatible_variants.push(variant_path); } } - if !compatible_variants.is_empty() { - let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr)); - let suggestions = compatible_variants.iter() - .map(|v| format!("{}({})", v, expr_text)).collect::>(); - err.span_suggestions(expr.span, - "try using a variant of the expected type", - suggestions); - } } + if !compatible_variants.is_empty() { + let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr)); + let suggestions = compatible_variants.iter() + .map(|v| format!("{}({})", v, expr_text)).collect::>(); + err.span_suggestions(expr.span, + "try using a variant of the expected type", + suggestions); + } + } - if let Some(suggestion) = self.check_ref(expr, - checked_ty, - expected) { - err.help(&suggestion); - } else { - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - } + if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) { + err.span_suggestion(expr.span, msg, suggestion); + } else { + let mode = probe::Mode::MethodCall; + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n{}", + self.get_best_match(&suggestions).join("\n"))); } - return Some(err); } - None + (expected, Some(err)) } fn format_method_suggestion(&self, method: &AssociatedItem) -> String { @@ -205,7 +212,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) - -> Option { + -> Option<(&'static str, String)> { match (&expected.sty, &checked_ty.sty) { (&ty::TyRef(_, exp), &ty::TyRef(_, check)) => match (&exp.ty.sty, &check.ty.sty) { (&ty::TyStr, &ty::TyArray(arr, _)) | @@ -213,7 +220,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let hir::ExprLit(_) = expr.node { let sp = self.sess().codemap().call_span_if_macro(expr.span); if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) { - return Some(format!("try `{}`", &src[1..])); + return Some(("consider removing the leading `b`", + src[1..].to_string())); } } None @@ -223,7 +231,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let hir::ExprLit(_) = expr.node { let sp = self.sess().codemap().call_span_if_macro(expr.span); if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) { - return Some(format!("try `b{}`", src)); + return Some(("consider adding a leading `b`", + format!("b{}", src))); } } None @@ -251,12 +260,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Use the callsite's span if this is a macro call. #41858 let sp = self.sess().codemap().call_span_if_macro(expr.span); if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) { - return Some(format!("try with `{}{}`", - match mutability.mutbl { - hir::Mutability::MutMutable => "&mut ", - hir::Mutability::MutImmutable => "&", - }, - &src)); + return Some(match mutability.mutbl { + hir::Mutability::MutMutable => { + ("consider mutably borrowing here", format!("&mut {}", src)) + } + hir::Mutability::MutImmutable => { + ("consider borrowing here", format!("&{}", src)) + } + }); } } None @@ -275,7 +286,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Maybe remove `&`? hir::ExprAddrOf(_, ref expr) => { if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) { - return Some(format!("try with `{}`", code)); + return Some(("consider removing the borrow", + code)); } } @@ -286,7 +298,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr.span) { let sp = self.sess().codemap().call_span_if_macro(expr.span); if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(sp) { - return Some(format!("try with `*{}`", code)); + return Some(("consider dereferencing the borrow", + format!("*{}", code))); } } }, diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 610d07efa359d..55700c452e57b 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,12 +11,12 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; -use middle::free_region::FreeRegionMap; use rustc::infer::{self, InferOk}; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{self, ObligationCause}; +use rustc::traits::{self, Reveal, ObligationCause}; use util::common::ErrorReported; use util::nodemap::FxHashSet; @@ -115,8 +115,18 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( } let region_scope_tree = region::ScopeTree::default(); - let free_regions = FreeRegionMap::new(); - infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &free_regions); + + // NB. It seems a bit... suspicious to use an empty param-env + // here. The correct thing, I imagine, would be + // `OutlivesEnvironment::new(impl_param_env)`, which would + // allow region solving to take any `a: 'b` relations on the + // impl into account. But I could not create a test case where + // it did the wrong thing, so I chose to preserve existing + // behavior, since it ought to be simply more + // conservative. -nmatsakis + let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing)); + + infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env); Ok(()) }) } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 3861a358b23e0..23243c3ad66c0 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -318,6 +318,10 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (0, vec![ptr_ty, tcx.types.usize], tcx.types.usize) }, + "nontemporal_store" => { + (1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil()) + } + ref other => { struct_span_err!(tcx.sess, it.span, E0093, "unrecognized intrinsic function: `{}`", @@ -348,7 +352,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => { (2, vec![param(0), param(0)], param(1)) } - "simd_add" | "simd_sub" | "simd_mul" | + "simd_add" | "simd_sub" | "simd_mul" | "simd_rem" | "simd_div" | "simd_shl" | "simd_shr" | "simd_and" | "simd_or" | "simd_xor" => { (1, vec![param(0), param(0)], param(0)) @@ -385,7 +389,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut structural_to_nomimal = FxHashMap(); let sig = tcx.fn_sig(def_id); - let sig = tcx.no_late_bound_regions(&sig).unwrap(); + let sig = sig.no_late_bound_regions().unwrap(); if intr.inputs.len() != sig.inputs().len() { span_err!(tcx.sess, it.span, E0444, "platform-specific intrinsic has invalid number of \ diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index a9830dd5ddece..17ed0aaa30b02 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -16,6 +16,7 @@ use hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty}; +use rustc::ty::subst::Subst; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref}; use rustc::ty::fold::TypeFoldable; use rustc::infer::{self, InferOk}; @@ -84,9 +85,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // Adjust the self expression the user provided and obtain the adjusted type. let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick); - // Make sure nobody calls `drop()` explicitly. - self.enforce_illegal_method_limitations(&pick); - // Create substitutions for the method's type parameters. let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick); let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs); @@ -96,6 +94,22 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // Create the final signature for the method, replacing late-bound regions. let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs); + // Unify the (adjusted) self type with what the method expects. + // + // SUBTLE: if we want good error messages, because of "guessing" while matching + // traits, no trait system method can be called before this point because they + // could alter our Self-type, except for normalizing the receiver from the + // signature (which is also done during probing). + let method_sig_rcvr = + self.normalize_associated_types_in(self.span, &method_sig.inputs()[0]); + self.unify_receivers(self_ty, method_sig_rcvr); + + let (method_sig, method_predicates) = + self.normalize_associated_types_in(self.span, &(method_sig, method_predicates)); + + // Make sure nobody calls `drop()` explicitly. + self.enforce_illegal_method_limitations(&pick); + // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that // something which derefs to `Self` actually implements the trait and the caller // wanted to make a static dispatch on it but forgot to import the trait. @@ -106,9 +120,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // appropriate hint suggesting to import the trait. let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates); - // Unify the (adjusted) self type with what the method expects. - self.unify_receivers(self_ty, method_sig.inputs()[0]); - // Add any trait/regions obligations specified on the method's type parameters. // We won't add these if we encountered an illegal sized bound, so that we can use // a custom error in that case. @@ -338,6 +349,9 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // + // NOTE: this returns the *unnormalized* predicates and method sig. Because of + // inference guessing, the predicates and method signature can't be normalized + // until we unify the `Self` type. fn instantiate_method_sig(&mut self, pick: &probe::Pick<'tcx>, all_substs: &'tcx Substs<'tcx>) @@ -352,8 +366,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { let def_id = pick.item.def_id; let method_predicates = self.tcx.predicates_of(def_id) .instantiate(self.tcx, all_substs); - let method_predicates = self.normalize_associated_types_in(self.span, - &method_predicates); debug!("method_predicates after subst = {:?}", method_predicates); @@ -369,7 +381,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { debug!("late-bound lifetimes from method instantiated, method_sig={:?}", method_sig); - let method_sig = self.instantiate_type_scheme(self.span, all_substs, &method_sig); + let method_sig = method_sig.subst(self.tcx, all_substs); debug!("type scheme substituted, method_sig={:?}", method_sig); (method_sig, method_predicates) diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 4ee0b4cb46f1b..58d72e37d51cf 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -13,6 +13,7 @@ use check::FnCtxt; use hir::def::Def; use hir::def_id::DefId; +use namespace::Namespace; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable}; @@ -24,6 +25,8 @@ use syntax_pos::Span; use rustc::hir; +use std::rc::Rc; + pub use self::MethodError::*; pub use self::CandidateSource::*; @@ -162,7 +165,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some(import_id) = pick.import_id { let import_def_id = self.tcx.hir.local_def_id(import_id); debug!("used_trait_import: {:?}", import_def_id); - self.tables.borrow_mut().used_trait_imports.insert(import_def_id); + Rc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) + .unwrap().insert(import_def_id); } self.tcx.check_stability(pick.item.def_id, call_expr.id, span); @@ -275,7 +279,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Trait must have a method named `m_name` and it should not have // type parameters or early-bound regions. let tcx = self.tcx; - let method_item = self.associated_item(trait_def_id, m_name).unwrap(); + let method_item = self.associated_item(trait_def_id, m_name, Namespace::Value).unwrap(); let def_id = method_item.def_id; let generics = tcx.generics_of(def_id); assert_eq!(generics.types.len(), 0); @@ -360,7 +364,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some(import_id) = pick.import_id { let import_def_id = self.tcx.hir.local_def_id(import_id); debug!("used_trait_import: {:?}", import_def_id); - self.tables.borrow_mut().used_trait_imports.insert(import_def_id); + Rc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) + .unwrap().insert(import_def_id); } let def = pick.item.def(); @@ -371,9 +376,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Find item with name `item_name` defined in impl/trait `def_id` /// and return it, or `None`, if no such item was defined there. - pub fn associated_item(&self, def_id: DefId, item_name: ast::Name) + pub fn associated_item(&self, def_id: DefId, item_name: ast::Name, ns: Namespace) -> Option { - let ident = self.tcx.adjust(item_name, def_id, self.body_id).0; - self.tcx.associated_items(def_id).find(|item| item.name.to_ident() == ident) + self.tcx.associated_items(def_id) + .find(|item| Namespace::from(item.kind) == ns && + self.tcx.hygienic_eq(item_name, item.name, def_id)) } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index a3b196f99d629..81e5b2fe00a6a 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -16,6 +16,7 @@ use super::suggest; use check::FnCtxt; use hir::def_id::DefId; use hir::def::Def; +use namespace::Namespace; use rustc::ty::subst::{Subst, Substs}; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable}; @@ -413,6 +414,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { ty::TyAdt(def, _) => { self.assemble_inherent_impl_candidates_for_type(def.did); } + ty::TyForeign(did) => { + self.assemble_inherent_impl_candidates_for_type(did); + } ty::TyParam(p) => { self.assemble_inherent_candidates_from_param(self_ty, p); } @@ -427,6 +431,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { ty::TySlice(_) => { let lang_def_id = lang_items.slice_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + + let lang_def_id = lang_items.slice_u8_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { let lang_def_id = lang_items.const_ptr_impl(); @@ -1317,11 +1324,14 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.tcx.associated_items(def_id) .filter(|x| { let dist = lev_distance(&*name.as_str(), &x.name.as_str()); - dist > 0 && dist <= max_dist + Namespace::from(x.kind) == Namespace::Value && dist > 0 + && dist <= max_dist }) .collect() } else { - self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x]) + self.fcx + .associated_item(def_id, name, Namespace::Value) + .map_or(Vec::new(), |x| vec![x]) } } else { self.tcx.associated_items(def_id).collect() diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 90c5297b39985..b3a7c32140b2e 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -17,6 +17,7 @@ use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable}; use hir::def::Def; use hir::def_id::{CRATE_DEF_INDEX, DefId}; use middle::lang_items::FnOnceTraitLangItem; +use namespace::Namespace; use rustc::traits::{Obligation, SelectionContext}; use util::nodemap::FxHashSet; @@ -92,12 +93,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { CandidateSource::ImplSource(impl_did) => { // Provide the best span we can. Use the item, if local to crate, else // the impl, if local to crate (item may be defaulted), else nothing. - let item = self.associated_item(impl_did, item_name) + let item = self.associated_item(impl_did, item_name, Namespace::Value) .or_else(|| { self.associated_item( self.tcx.impl_trait_ref(impl_did).unwrap().def_id, - - item_name + item_name, + Namespace::Value, ) }).unwrap(); let note_span = self.tcx.hir.span_if_local(item.def_id).or_else(|| { @@ -127,7 +128,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } CandidateSource::TraitSource(trait_did) => { - let item = self.associated_item(trait_did, item_name).unwrap(); + let item = self + .associated_item(trait_did, item_name, Namespace::Value) + .unwrap(); let item_span = self.tcx.def_span(item.def_id); span_note!(err, item_span, @@ -161,41 +164,63 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; match error { - MethodError::NoMatch(NoMatchData { static_candidates: static_sources, - unsatisfied_predicates, - out_of_scope_traits, - lev_candidate, - mode, - .. }) => { + MethodError::NoMatch(NoMatchData { + static_candidates: static_sources, + unsatisfied_predicates, + out_of_scope_traits, + lev_candidate, + mode, + .. + }) => { let tcx = self.tcx; let actual = self.resolve_type_vars_if_possible(&rcvr_ty); + let ty_string = self.ty_to_string(actual); + let is_method = mode == Mode::MethodCall; + let type_str = if is_method { + "method" + } else if actual.is_enum() { + "variant" + } else { + match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { + (Some(name), false) if name.is_lowercase() => { + "function or associated item" + } + (Some(_), false) => "associated item", + (Some(_), true) | (None, false) => { + "variant or associated item" + } + (None, true) => "variant", + } + }; let mut err = if !actual.references_error() { - struct_span_err!(tcx.sess, span, E0599, - "no {} named `{}` found for type `{}` in the \ - current scope", - if mode == Mode::MethodCall { - "method" - } else { - match item_name.as_str().chars().next() { - Some(name) => { - if name.is_lowercase() { - "function or associated item" - } else { - "associated item" - } - }, - None => { - "" - }, - } - }, - item_name, - self.ty_to_string(actual)) + struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for type `{}` in the current scope", + type_str, + item_name, + ty_string + ) } else { - self.tcx.sess.diagnostic().struct_dummy() + tcx.sess.diagnostic().struct_dummy() }; + if let Some(def) = actual.ty_adt_def() { + if let Some(full_sp) = tcx.hir.span_if_local(def.did) { + let def_sp = tcx.sess.codemap().def_span(full_sp); + err.span_label(def_sp, format!("{} `{}` not found {}", + type_str, + item_name, + if def.is_enum() && !is_method { + "here" + } else { + "for this" + })); + } + } + // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as (x.f)(...). if let Some(expr) = rcvr_expr { @@ -237,6 +262,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => {} } } + } else { + err.span_label(span, format!("{} not found in `{}`", type_str, ty_string)); } if self.is_fn_ty(&rcvr_ty, span) { @@ -337,16 +364,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { err: &mut DiagnosticBuilder, mut msg: String, candidates: Vec) { - let limit = if candidates.len() == 5 { 5 } else { 4 }; - for (i, trait_did) in candidates.iter().take(limit).enumerate() { - msg.push_str(&format!("\ncandidate #{}: `use {};`", - i + 1, - self.tcx.item_path_str(*trait_did))); - } - if candidates.len() > limit { - msg.push_str(&format!("\nand {} others", candidates.len() - limit)); + let module_did = self.tcx.hir.get_module_parent(self.body_id); + let module_id = self.tcx.hir.as_local_node_id(module_did).unwrap(); + let krate = self.tcx.hir.krate(); + let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id); + if let Some(span) = span { + let path_strings = candidates.iter().map(|did| { + // produce an additional newline to separate the new use statement + // from the directly following item. + let additional_newline = if found_use { + "" + } else { + "\n" + }; + format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline) + }).collect(); + + err.span_suggestions(span, &msg, path_strings); + } else { + let limit = if candidates.len() == 5 { 5 } else { 4 }; + for (i, trait_did) in candidates.iter().take(limit).enumerate() { + msg.push_str(&format!("\ncandidate #{}: `use {};`", + i + 1, + self.tcx.item_path_str(*trait_did))); + } + if candidates.len() > limit { + msg.push_str(&format!("\nand {} others", candidates.len() - limit)); + } + err.note(&msg[..]); } - err.note(&msg[..]); } fn suggest_valid_traits(&self, @@ -402,7 +448,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // implementing a trait would be legal but is rejected // here). (type_is_local || info.def_id.is_local()) - && self.associated_item(info.def_id, item_name).is_some() + && self.associated_item(info.def_id, item_name, Namespace::Value).is_some() }) .collect::>(); @@ -448,6 +494,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn is_local(ty: Ty) -> bool { match ty.sty { ty::TyAdt(def, _) => def.did.is_local(), + ty::TyForeign(did) => did.is_local(), ty::TyDynamic(ref tr, ..) => tr.principal() .map_or(false, |p| p.def_id().is_local()), @@ -600,3 +647,83 @@ impl<'a> Iterator for AllTraits<'a> { }) } } + + +struct UsePlacementFinder<'a, 'tcx: 'a, 'gcx: 'tcx> { + target_module: ast::NodeId, + span: Option, + found_use: bool, + tcx: TyCtxt<'a, 'gcx, 'tcx> +} + +impl<'a, 'tcx, 'gcx> UsePlacementFinder<'a, 'tcx, 'gcx> { + fn check( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + krate: &'tcx hir::Crate, + target_module: ast::NodeId, + ) -> (Option, bool) { + let mut finder = UsePlacementFinder { + target_module, + span: None, + found_use: false, + tcx, + }; + hir::intravisit::walk_crate(&mut finder, krate); + (finder.span, finder.found_use) + } +} + +impl<'a, 'tcx, 'gcx> hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'a, 'tcx, 'gcx> { + fn visit_mod( + &mut self, + module: &'tcx hir::Mod, + _: Span, + node_id: ast::NodeId, + ) { + if self.span.is_some() { + return; + } + if node_id != self.target_module { + hir::intravisit::walk_mod(self, module, node_id); + return; + } + // find a use statement + for item_id in &module.item_ids { + let item = self.tcx.hir.expect_item(item_id.id); + match item.node { + hir::ItemUse(..) => { + // don't suggest placing a use before the prelude + // import or other generated ones + if item.span.ctxt().outer().expn_info().is_none() { + self.span = Some(item.span.with_hi(item.span.lo())); + self.found_use = true; + return; + } + }, + // don't place use before extern crate + hir::ItemExternCrate(_) => {} + // but place them before the first other item + _ => if self.span.map_or(true, |span| item.span < span ) { + if item.span.ctxt().outer().expn_info().is_none() { + // don't insert between attributes and an item + if item.attrs.is_empty() { + self.span = Some(item.span.with_hi(item.span.lo())); + } else { + // find the first attribute on the item + for attr in &item.attrs { + if self.span.map_or(true, |span| attr.span < span) { + self.span = Some(attr.span.with_hi(attr.span.lo())); + } + } + } + } + }, + } + } + } + fn nested_visit_map<'this>( + &'this mut self + ) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> { + hir::intravisit::NestedVisitorMap::None + } +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6fe49644fe81e..efcf498b72c14 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -87,7 +87,8 @@ use self::TupleArgumentsFlag::*; use astconv::AstConv; use hir::def::{Def, CtorKind}; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_back::slice::ref_slice; +use std::slice; +use namespace::Namespace; use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::middle::region; @@ -99,15 +100,16 @@ use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt}; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, DiagnosticId}; use require_c_abi_if_variadic; use session::{CompileIncomplete, Session}; use TypeAndSubsts; use lint; use util::common::{ErrorReported, indenter}; -use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; +use util::nodemap::{DefIdMap, DefIdSet, FxHashMap, NodeMap}; use std::cell::{Cell, RefCell, Ref, RefMut}; +use std::rc::Rc; use std::collections::hash_map::Entry; use std::cmp; use std::fmt::Display; @@ -115,6 +117,7 @@ use std::mem::replace; use std::ops::{self, Deref}; use syntax::abi::Abi; use syntax::ast; +use syntax::attr; use syntax::codemap::{self, original_sp, Spanned}; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::ptr::P; @@ -127,14 +130,13 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map::Node; use rustc::hir::{self, PatKind}; use rustc::middle::lang_items; -use rustc_back::slice; use rustc_const_math::ConstInt; mod autoderef; pub mod dropck; pub mod _match; pub mod writeback; -pub mod regionck; +mod regionck; pub mod coercion; pub mod demand; pub mod method; @@ -210,7 +212,7 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // associated fresh inference variable. Writeback resolves these // variables to get the concrete type, which can be used to // deanonymize TyAnon, after typeck is done with all functions. - anon_types: RefCell>>, + anon_types: RefCell>>, /// Each type parameter has an implicit region bound that /// indicates it must outlive at least the function body (the user @@ -223,6 +225,43 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { body_id: Option, } +/// Information about the anonymous, abstract types whose values we +/// are inferring in this function (these are the `impl Trait` that +/// appear in the return type). +#[derive(Debug)] +struct AnonTypeDecl<'tcx> { + /// The substitutions that we apply to the abstract that that this + /// `impl Trait` desugars to. e.g., if: + /// + /// fn foo<'a, 'b, T>() -> impl Trait<'a> + /// + /// winds up desugared to: + /// + /// abstract type Foo<'x, T>: Trait<'x> + /// fn foo<'a, 'b, T>() -> Foo<'a, T> + /// + /// then `substs` would be `['a, T]`. + substs: &'tcx Substs<'tcx>, + + /// The type variable that represents the value of the abstract type + /// that we require. In other words, after we compile this function, + /// we will be created a constraint like: + /// + /// Foo<'a, T> = ?C + /// + /// where `?C` is the value of this type variable. =) It may + /// naturally refer to the type and lifetime parameters in scope + /// in this function, though ultimately it should only reference + /// those that are arguments to `Foo` in the constraint above. (In + /// other words, `?C` should not include `'b`, even though it's a + /// lifetime parameter on `foo`.) + concrete_ty: Ty<'tcx>, + + /// A list of all required region bounds on the impl Trait type, + /// e.g. `'a` and `'b` in `fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b`. + required_region_bounds: Vec>, +} + impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> { type Target = InferCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -619,7 +658,7 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { deferred_call_resolutions: RefCell::new(DefIdMap()), deferred_cast_checks: RefCell::new(Vec::new()), deferred_generator_interiors: RefCell::new(Vec::new()), - anon_types: RefCell::new(NodeMap()), + anon_types: RefCell::new(DefIdMap()), implicit_region_bound, body_id, } @@ -655,44 +694,9 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { value: &T) -> T where T : TypeFoldable<'tcx> { - let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, param_env, value); + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); self.register_infer_ok_obligations(ok) } - - fn normalize_associated_types_in_as_infer_ok(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> - where T : TypeFoldable<'tcx> - { - debug!("normalize_associated_types_in(value={:?})", value); - let mut selcx = traits::SelectionContext::new(self); - let cause = ObligationCause::misc(span, body_id); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - debug!("normalize_associated_types_in: result={:?} predicates={:?}", - value, - obligations); - InferOk { value, obligations } - } - - /// Replace any late-bound regions bound in `value` with - /// free variants attached to `all_outlive_scope`. - fn liberate_late_bound_regions(&self, - all_outlive_scope: DefId, - value: &ty::Binder) - -> T - where T: TypeFoldable<'tcx> - { - self.tcx.replace_late_bound_regions(value, |br| { - self.tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope: all_outlive_scope, - bound_region: br - })) - }).0 - } } struct CheckItemTypesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -728,7 +732,7 @@ fn typeck_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum debug_assert!(crate_num == LOCAL_CRATE); Ok(tcx.sess.track_errors(|| { for body_owner_def_id in tcx.body_owners() { - tcx.typeck_tables_of(body_owner_def_id); + ty::maps::queries::typeck_tables_of::ensure(tcx, body_owner_def_id); } })?) } @@ -738,29 +742,12 @@ pub fn provide(providers: &mut Providers) { typeck_item_bodies, typeck_tables_of, has_typeck_tables, - closure_kind, - generator_sig, adt_destructor, + used_trait_imports, ..*providers }; } -fn generator_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option> { - let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - let hir_id = tcx.hir.node_to_hir_id(node_id); - tcx.typeck_tables_of(def_id).generator_sigs()[hir_id].map(|s| ty::Binder(s)) -} - -fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> ty::ClosureKind { - let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - let hir_id = tcx.hir.node_to_hir_id(node_id); - tcx.typeck_tables_of(def_id).closure_kinds()[hir_id].0 -} - fn adt_destructor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Option { @@ -844,6 +831,12 @@ fn has_typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, primary_body_of(tcx, id).is_some() } +fn used_trait_imports<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Rc { + tcx.typeck_tables_of(def_id).used_trait_imports.clone() +} + fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::TypeckTables<'tcx> { @@ -872,14 +865,17 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Compute the fty from point of view of inside fn. let fn_sig = - inh.liberate_late_bound_regions(def_id, &fn_sig); + tcx.liberate_late_bound_regions(def_id, &fn_sig); let fn_sig = inh.normalize_associated_types_in(body.value.span, body_id.node_id, param_env, &fn_sig); - check_fn(&inh, param_env, fn_sig, decl, id, body, false).0 + let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0; + // Ensure anon_types have been instantiated prior to entering regionck + fcx.instantiate_anon_types(&fn_sig.output()); + fcx } else { let fcx = FnCtxt::new(&inh, param_env, body.value.id); let expected_type = tcx.type_of(def_id); @@ -990,6 +986,17 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> { _: hir::BodyId, _: Span, _: ast::NodeId) { } } +/// When `check_fn` is invoked on a generator (i.e., a body that +/// includes yield), it returns back some information about the yield +/// points. +struct GeneratorTypes<'tcx> { + /// Type of value that is yielded. + yield_ty: ty::Ty<'tcx>, + + /// Types that are captured (see `GeneratorInterior` for more). + interior: ty::GeneratorInterior<'tcx> +} + /// Helper used for fns and closures. Does the grungy work of checking a function /// body and returns the function context used for that purpose, since in the case of a fn item /// there is still a bit more to do. @@ -1003,7 +1010,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fn_id: ast::NodeId, body: &'gcx hir::Body, can_be_generator: bool) - -> (FnCtxt<'a, 'gcx, 'tcx>, Option>) + -> (FnCtxt<'a, 'gcx, 'tcx>, Option>) { let mut fn_sig = fn_sig.clone(); @@ -1037,7 +1044,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // Add formal parameters. for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) { // Check the pattern. - fcx.check_pat_arg(&arg.pat, arg_ty, true); + fcx.check_pat_walk(&arg.pat, arg_ty, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -1052,21 +1060,11 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, let fn_hir_id = fcx.tcx.hir.node_to_hir_id(fn_id); let gen_ty = if can_be_generator && body.is_generator { - let gen_sig = ty::GenSig { - yield_ty: fcx.yield_ty.unwrap(), - return_ty: ret_ty, - }; - inherited.tables.borrow_mut().generator_sigs_mut().insert(fn_hir_id, Some(gen_sig)); - let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)); fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness)); let interior = ty::GeneratorInterior::new(witness); - - inherited.tables.borrow_mut().generator_interiors_mut().insert(fn_hir_id, interior); - - Some(interior) + Some(GeneratorTypes { yield_ty: fcx.yield_ty.unwrap(), interior: interior }) } else { - inherited.tables.borrow_mut().generator_sigs_mut().insert(fn_hir_id, None); None }; inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig); @@ -1248,6 +1246,7 @@ fn report_forbidden_specialization<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn check_specialization_validity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def: &ty::TraitDef, + trait_item: &ty::AssociatedItem, impl_id: DefId, impl_item: &hir::ImplItem) { @@ -1258,7 +1257,8 @@ fn check_specialization_validity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir::ImplItemKind::Method(..) => ty::AssociatedKind::Method, hir::ImplItemKind::Type(_) => ty::AssociatedKind::Type }; - let parent = ancestors.defs(tcx, impl_item.name, kind).skip(1).next() + + let parent = ancestors.defs(tcx, trait_item.name, kind, trait_def.def_id).skip(1).next() .map(|node_item| node_item.map(|parent| parent.defaultness)); if let Some(parent) = parent { @@ -1290,7 +1290,13 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, for impl_item in impl_items() { let ty_impl_item = tcx.associated_item(tcx.hir.local_def_id(impl_item.id)); let ty_trait_item = tcx.associated_items(impl_trait_ref.def_id) - .find(|ac| ac.name == ty_impl_item.name); + .find(|ac| Namespace::from(&impl_item.node) == Namespace::from(ac.kind) && + tcx.hygienic_eq(ty_impl_item.name, ac.name, impl_trait_ref.def_id)) + .or_else(|| { + // Not compatible, but needed for the error message + tcx.associated_items(impl_trait_ref.def_id) + .find(|ac| tcx.hygienic_eq(ty_impl_item.name, ac.name, impl_trait_ref.def_id)) + }); // Check that impl definition matches trait definition if let Some(ty_trait_item) = ty_trait_item { @@ -1321,24 +1327,12 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir::ImplItemKind::Method(..) => { let trait_span = tcx.hir.span_if_local(ty_trait_item.def_id); if ty_trait_item.kind == ty::AssociatedKind::Method { - let err_count = tcx.sess.err_count(); compare_impl_method(tcx, &ty_impl_item, impl_item.span, &ty_trait_item, impl_trait_ref, - trait_span, - true); // start with old-broken-mode - if err_count == tcx.sess.err_count() { - // old broken mode did not report an error. Try with the new mode. - compare_impl_method(tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - trait_span, - false); // use the new mode - } + trait_span); } else { let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324, "item `{}` is an associated method, \ @@ -1371,9 +1365,9 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } } - } - check_specialization_validity(tcx, trait_def, impl_id, impl_item); + check_specialization_validity(tcx, trait_def, &ty_trait_item, impl_id, impl_item); + } } // Check for missing items from trait @@ -1382,7 +1376,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let associated_type_overridden = overridden_associated_type.is_some(); for trait_item in tcx.associated_items(impl_trait_ref.def_id) { let is_implemented = trait_def.ancestors(tcx, impl_id) - .defs(tcx, trait_item.name, trait_item.kind) + .defs(tcx, trait_item.name, trait_item.kind, impl_trait_ref.def_id) .next() .map(|node_item| !node_item.node.is_from_trait()) .unwrap_or(false); @@ -1543,12 +1537,15 @@ pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let def = tcx.adt_def(def_id); def.destructor(tcx); // force the destructor to be evaluated - if vs.is_empty() && tcx.has_attr(def_id, "repr") { - struct_span_err!( - tcx.sess, sp, E0084, - "unsupported representation for zero-variant enum") - .span_label(sp, "unsupported enum representation") - .emit(); + if vs.is_empty() { + let attributes = tcx.get_attrs(def_id); + if let Some(attr) = attr::find_by_name(&attributes, "repr") { + struct_span_err!( + tcx.sess, attr.span, E0084, + "unsupported representation for zero-variant enum") + .span_label(sp, "zero-variant enum") + .emit(); + } } let repr_type_ty = def.repr.discr_type().to_ty(tcx); @@ -1918,20 +1915,34 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Replace all anonymized types with fresh inference variables /// and record them for writeback. fn instantiate_anon_types>(&self, value: &T) -> T { + debug!("instantiate_anon_types(value={:?})", value); value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| { if let ty::TyAnon(def_id, substs) = ty.sty { + debug!("instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", def_id, substs); + // Use the same type variable if the exact same TyAnon appears more // than once in the return type (e.g. if it's passed to a type alias). - let id = self.tcx.hir.as_local_node_id(def_id).unwrap(); - if let Some(ty_var) = self.anon_types.borrow().get(&id) { - return ty_var; + if let Some(anon_defn) = self.anon_types.borrow().get(&def_id) { + return anon_defn.concrete_ty; } let span = self.tcx.def_span(def_id); let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span)); - self.anon_types.borrow_mut().insert(id, ty_var); let predicates_of = self.tcx.predicates_of(def_id); let bounds = predicates_of.instantiate(self.tcx, substs); + debug!("instantiate_anon_types: bounds={:?}", bounds); + + let required_region_bounds = + self.tcx.required_region_bounds(ty, bounds.predicates.clone()); + debug!("instantiate_anon_types: required_region_bounds={:?}", + required_region_bounds); + + self.anon_types.borrow_mut().insert(def_id, AnonTypeDecl { + substs, + concrete_ty: ty_var, + required_region_bounds, + }); + debug!("instantiate_anon_types: ty_var={:?}", ty_var); for predicate in bounds.predicates { // Change the predicate to refer to the type variable, @@ -1940,8 +1951,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let predicate = self.instantiate_anon_types(&predicate); // Require that the predicate holds for the concrete type. - let cause = traits::ObligationCause::new(span, self.body_id, + let cause = traits::ObligationCause::new(span, + self.body_id, traits::SizedReturnType); + + debug!("instantiate_anon_types: predicate={:?}", predicate); self.register_predicate(traits::Obligation::new(cause, self.param_env, predicate)); @@ -1964,10 +1978,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> InferOk<'tcx, T> where T : TypeFoldable<'tcx> { - self.inh.normalize_associated_types_in_as_infer_ok(span, - self.body_id, - self.param_env, - value) + self.inh.partially_normalize_associated_types_in(span, + self.body_id, + self.param_env, + value) } pub fn require_type_meets(&self, @@ -2009,7 +2023,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.tables.borrow().node_types().get(id) { Some(&t) => t, - None if self.err_count_since_creation() != 0 => self.tcx.types.err, + None if self.is_tainted_by_errors() => self.tcx.types.err, None => { let node_id = self.tcx.hir.definitions().find_node_for_hir_id(id); bug!("no type for node {}: {} in fcx {}", @@ -2349,6 +2363,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_method_argument_types(&self, sp: Span, + expr_sp: Span, method: Result, ()>, args_no_rcvr: &'gcx [hir::Expr], tuple_arguments: TupleArgumentsFlag, @@ -2368,7 +2383,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..], false)], }; - self.check_argument_types(sp, &err_inputs[..], &[], args_no_rcvr, + self.check_argument_types(sp, expr_sp, &err_inputs[..], &[], args_no_rcvr, false, tuple_arguments, None); return self.tcx.types.err; } @@ -2381,7 +2396,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { method.sig.output(), &method.sig.inputs()[1..] ); - self.check_argument_types(sp, &method.sig.inputs()[1..], &expected_arg_tys[..], + self.check_argument_types(sp, expr_sp, &method.sig.inputs()[1..], &expected_arg_tys[..], args_no_rcvr, method.sig.variadic, tuple_arguments, self.tcx.hir.span_if_local(method.def_id)); method.sig.output() @@ -2391,6 +2406,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// method calls and overloaded operators. fn check_argument_types(&self, sp: Span, + expr_sp: Span, fn_inputs: &[Ty<'tcx>], expected_arg_tys: &[Ty<'tcx>], args: &'gcx [hir::Expr], @@ -2431,9 +2447,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { sp }; - fn parameter_count_error<'tcx>(sess: &Session, sp: Span, expected_count: usize, - arg_count: usize, error_code: &str, variadic: bool, - def_span: Option, sugg_unit: bool) { + fn parameter_count_error<'tcx>(sess: &Session, + sp: Span, + expr_sp: Span, + expected_count: usize, + arg_count: usize, + error_code: &str, + variadic: bool, + def_span: Option, + sugg_unit: bool) { let mut err = sess.struct_span_err_with_code(sp, &format!("this function takes {}{} parameter{} but {} parameter{} supplied", if variadic {"at least "} else {""}, @@ -2441,18 +2463,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if expected_count == 1 {""} else {"s"}, arg_count, if arg_count == 1 {" was"} else {"s were"}), - error_code); + DiagnosticId::Error(error_code.to_owned())); if let Some(def_s) = def_span { err.span_label(def_s, "defined here"); } if sugg_unit { - let sugg_span = sp.end_point(); + let sugg_span = expr_sp.end_point(); // remove closing `)` from the span let sugg_span = sugg_span.with_hi(sugg_span.lo()); err.span_suggestion( sugg_span, - "expected the unit value `()`. You can create one with a pair of parenthesis", + "expected the unit value `()`; create it with empty parentheses", String::from("()")); } else { err.span_label(sp, format!("expected {}{} parameter{}", @@ -2467,7 +2489,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); match tuple_type.sty { ty::TyTuple(arg_types, _) if arg_types.len() != args.len() => { - parameter_count_error(tcx.sess, sp_args, arg_types.len(), args.len(), + parameter_count_error(tcx.sess, sp_args, expr_sp, arg_types.len(), args.len(), "E0057", false, def_span, false); expected_arg_tys = &[]; self.err_args(args.len()) @@ -2496,7 +2518,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if supplied_arg_count >= expected_arg_count { fn_inputs.to_vec() } else { - parameter_count_error(tcx.sess, sp_args, expected_arg_count, + parameter_count_error(tcx.sess, sp_args, expr_sp, expected_arg_count, supplied_arg_count, "E0060", true, def_span, false); expected_arg_tys = &[]; self.err_args(supplied_arg_count) @@ -2510,7 +2532,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { false }; - parameter_count_error(tcx.sess, sp_args, expected_arg_count, + parameter_count_error(tcx.sess, sp_args, expr_sp, expected_arg_count, supplied_arg_count, "E0061", false, def_span, sugg_unit); expected_arg_tys = &[]; self.err_args(supplied_arg_count) @@ -2729,9 +2751,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_expr_coercable_to_type(&self, expr: &'gcx hir::Expr, expected: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_coerce(expr, ty, expected); - ty + self.check_expr_coercable_to_type_with_lvalue_pref(expr, expected, NoPreference) + } + + fn check_expr_coercable_to_type_with_lvalue_pref(&self, + expr: &'gcx hir::Expr, + expected: Ty<'tcx>, + lvalue_pref: LvaluePreference) + -> Ty<'tcx> { + let ty = self.check_expr_with_expectation_and_lvalue_pref( + expr, + ExpectHasType(expected), + lvalue_pref); + self.demand_coerce(expr, ty, expected) } fn check_expr_with_hint(&self, expr: &'gcx hir::Expr, @@ -2863,7 +2895,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Call the generic checker. - self.check_method_argument_types(span, method, + self.check_method_argument_types(span, + expr.span, + method, &args[1..], DontTupleArguments, expected) @@ -3410,6 +3444,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::QPath::TypeRelative(ref qself, _) => qself.span }; + // Prohibit struct expressions when non exhaustive flag is set. + if let ty::TyAdt(adt, _) = struct_ty.sty { + if !adt.did.is_local() && adt.is_non_exhaustive() { + span_err!(self.tcx.sess, expr.span, E0639, + "cannot create non-exhaustive {} using struct expression", + adt.variant_descr()); + } + } + self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span, variant, fields, base_expr.is_none()); if let &Some(ref base_expr) = base_expr { @@ -4104,6 +4147,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (self.to_ty(qself), segment) } }; + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + if let Some(cached_def) = self.tables.borrow().type_dependent_defs().get(hir_id) { + // Return directly on cache hit. This is useful to avoid doubly reporting + // errors with default match binding modes. See #44614. + return (*cached_def, Some(ty), slice::from_ref(&**item_segment)) + } let item_name = item_segment.name; let def = match self.resolve_ufcs(span, item_name, ty, node_id) { Ok(def) => def, @@ -4120,9 +4169,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Write back the new resolution. - let hir_id = self.tcx.hir.node_to_hir_id(node_id); self.tables.borrow_mut().type_dependent_defs_mut().insert(hir_id, def); - (def, Some(ty), slice::ref_slice(&**item_segment)) + (def, Some(ty), slice::from_ref(&**item_segment)) } pub fn check_decl_initializer(&self, @@ -4130,7 +4178,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { init: &'gcx hir::Expr) -> Ty<'tcx> { // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed - // for #42640. + // for #42640 (default match binding modes). + // + // See #44848. let ref_bindings = local.pat.contains_explicit_ref_binding(); let local_ty = self.local_ty(init.span, local.id); @@ -4162,7 +4212,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - self.check_pat(&local.pat, t); + self.check_pat_walk(&local.pat, t, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), + true); let pat_ty = self.node_ty(local.pat.hir_id); if pat_ty.references_error() { self.write_ty(local.hir_id, pat_ty); @@ -4256,12 +4308,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { CoerceMany::new(coerce_to_ty) } else { let tail_expr: &[P] = match tail_expr { - Some(e) => ref_slice(e), + Some(e) => slice::from_ref(e), None => &[], }; CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) }; + let prev_diverges = self.diverges.get(); let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false, @@ -4311,6 +4364,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }); + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges.set(prev_diverges); + } + let mut ty = ctxt.coerce.unwrap().complete(self); if self.has_errors.get() || ty.references_error() { @@ -4647,6 +4706,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // a problem. self.check_path_parameter_count(span, &mut type_segment, false); self.check_path_parameter_count(span, &mut fn_segment, false); + self.check_impl_trait(span, &mut fn_segment); let (fn_start, has_self) = match (type_segment, fn_segment) { (_, Some((_, generics))) => { @@ -4871,6 +4931,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } + /// Report error if there is an explicit type parameter when using `impl Trait`. + fn check_impl_trait(&self, + span: Span, + segment: &mut Option<(&hir::PathSegment, &ty::Generics)>) { + use hir::SyntheticTyParamKind::*; + + segment.map(|(path_segment, generics)| { + let explicit = !path_segment.infer_types; + let impl_trait = generics.types.iter() + .any(|ty_param| { + match ty_param.synthetic { + Some(ImplTrait) => true, + _ => false, + } + }); + + if explicit && impl_trait { + let mut err = struct_span_err! { + self.tcx.sess, + span, + E0632, + "cannot provide explicit type parameters when `impl Trait` is \ + used in argument position." + }; + + err.emit(); + } + }); + } + fn structurally_resolve_type_or_else(&self, sp: Span, ty: Ty<'tcx>, f: F) -> Ty<'tcx> where F: Fn() -> Ty<'tcx> @@ -4941,6 +5031,10 @@ pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let ty::TyParam(ParamTy {idx, ..}) = leaf_ty.sty { debug!("Found use of ty param num {}", idx); tps_used[idx as usize - generics.lifetimes.len()] = true; + } else if let ty::TyError = leaf_ty.sty { + // If there already another error, do not emit an error for not using a type Parameter + assert!(tcx.sess.err_count() > 0); + return; } } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a3dd81fdddee3..e099d1c0c2531 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -12,7 +12,7 @@ use super::FnCtxt; use super::method::MethodCallee; -use rustc::ty::{self, Ty, TypeFoldable, PreferMutLvalue, TypeVariants}; +use rustc::ty::{self, Ty, TypeFoldable, NoPreference, PreferMutLvalue, TypeVariants}; use rustc::ty::TypeVariants::{TyStr, TyRef}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::infer::type_variable::TypeVariableOrigin; @@ -29,12 +29,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lhs_expr: &'gcx hir::Expr, rhs_expr: &'gcx hir::Expr) -> Ty<'tcx> { - let lhs_ty = self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue); - - let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); - let (rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes); - let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::Yes); let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { @@ -73,27 +69,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lhs_expr, rhs_expr); - let lhs_ty = self.check_expr(lhs_expr); - let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); - match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. + self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool); let lhs_diverges = self.diverges.get(); - self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty); - self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool()); + self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool); // Depending on the LHS' value, the RHS can never execute. self.diverges.set(lhs_diverges); - tcx.mk_bool() + tcx.types.bool } _ => { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, lhs_ty, + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No); // Supply type inference hints if relevant. Probably these @@ -108,7 +101,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // deduce that the result type should be `u32`, even // though we don't know yet what type 2 has and hence // can't pin this down to a specific impl. - let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) @@ -164,17 +156,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn check_overloaded_binop(&self, expr: &'gcx hir::Expr, lhs_expr: &'gcx hir::Expr, - lhs_ty: Ty<'tcx>, rhs_expr: &'gcx hir::Expr, op: hir::BinOp, is_assign: IsAssign) - -> (Ty<'tcx>, Ty<'tcx>) + -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})", + debug!("check_overloaded_binop(expr.id={}, op={:?}, is_assign={:?})", expr.id, - lhs_ty, + op, is_assign); + let lhs_pref = match is_assign { + IsAssign::Yes => PreferMutLvalue, + IsAssign::No => NoPreference + }; + // Find a suitable supertype of the LHS expression's type, by coercing to + // a type variable, to pass as the `Self` to the trait, avoiding invariant + // trait matching creating lifetime constraints that are too strict. + // E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result + // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. + let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(lhs_expr, + self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)), + lhs_pref); + let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); + // NB: As we have not yet type-checked the RHS, we don't have the // type at hand. Make a variable to represent it. The whole reason // for this indirection is so that, below, we can check the expr @@ -187,6 +192,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // see `NB` above let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var); + let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty); let return_ty = match result { Ok(method) => { @@ -283,6 +289,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g. "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted + } else if let ty::TyParam(_) = lhs_ty.sty { + // FIXME: point to span of param + err.note( + &format!("`{}` might need a bound for `{}`", + lhs_ty, missing_trait)); } else { err.note( &format!("an implementation of `{}` might be missing for `{}`", @@ -296,7 +307,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - (rhs_ty_var, return_ty) + (lhs_ty, rhs_ty, return_ty) } fn check_str_addition(&self, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 609af638e97c6..7ef6027772be2 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -84,18 +84,16 @@ use check::dropck; use check::FnCtxt; -use middle::free_region::FreeRegionMap; use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; -use rustc::traits; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; +use rustc::ty::{self, Ty}; +use rustc::infer; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; use rustc::ty::outlives::Component; -use rustc::ty::wf; use std::mem; use std::ops::Deref; @@ -117,7 +115,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn regionck_expr(&self, body: &'gcx hir::Body) { let subject = self.tcx.hir.body_owner_def_id(body.id()); let id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(id), id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(id), + id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_body(body); @@ -126,7 +128,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.resolve_regions_and_report_errors(); assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } /// Region checking during the WF phase for items. `wf_tys` are the @@ -135,39 +137,50 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { item_id: ast::NodeId, span: Span, wf_tys: &[Ty<'tcx>]) { - debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys); + debug!("regionck_item(item.id={:?}, wf_tys={:?})", item_id, wf_tys); let subject = self.tcx.hir.local_def_id(item_id); - let mut rcx = RegionCtxt::new(self, RepeatingScope(item_id), item_id, Subject(subject)); - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.relate_free_regions(wf_tys, item_id, span); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(item_id), + item_id, + Subject(subject), + self.param_env); + rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } + /// Region check a function body. Not invoked on closures, but + /// only on the "root" fn item (in which closures may be + /// embedded). Walks the function body and adds various add'l + /// constraints that are needed for region inference. This is + /// separated both to isolate "pure" region constraints from the + /// rest of type check and because sometimes we need type + /// inference to have completed before we can determine which + /// constraints to add. pub fn regionck_fn(&self, fn_id: ast::NodeId, body: &'gcx hir::Body) { debug!("regionck_fn(id={})", fn_id); let subject = self.tcx.hir.body_owner_def_id(body.id()); let node_id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(node_id), node_id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(node_id), + node_id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.resolve_regions_and_report_errors(); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes // (e.g., `regionck_item`), we don't have an enclosing tables. assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } } @@ -177,11 +190,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, - pub region_scope_tree: Rc, - free_region_map: FreeRegionMap<'tcx>, + outlives_environment: OutlivesEnvironment<'tcx>, // id of innermost fn body id body_id: ast::NodeId, @@ -197,24 +208,6 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. -#[derive(Debug)] -enum ImpliedBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> { type Target = FnCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -229,8 +222,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { pub fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, RepeatingScope(initial_repeating_scope): RepeatingScope, initial_body_id: ast::NodeId, - Subject(subject): Subject) -> RegionCtxt<'a, 'gcx, 'tcx> { + Subject(subject): Subject, + param_env: ty::ParamEnv<'tcx>) + -> RegionCtxt<'a, 'gcx, 'tcx> { let region_scope_tree = fcx.tcx.region_scope_tree(subject); + let outlives_environment = OutlivesEnvironment::new(param_env); RegionCtxt { fcx, region_scope_tree, @@ -238,20 +234,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_id: initial_body_id, call_site_scope: None, subject_def_id: subject, - region_bound_pairs: Vec::new(), - free_region_map: FreeRegionMap::new(), + outlives_environment, } } - fn set_call_site_scope(&mut self, call_site_scope: Option) - -> Option { - mem::replace(&mut self.call_site_scope, call_site_scope) - } - - fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId { - mem::replace(&mut self.body_id, body_id) - } - fn set_repeating_scope(&mut self, scope: ast::NodeId) -> ast::NodeId { mem::replace(&mut self.repeating_scope, scope) } @@ -295,6 +281,18 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.resolve_type(ty) } + /// This is the "main" function when region-checking a function item or a closure + /// within a function item. It begins by updating various fields (e.g., `call_site_scope` + /// and `outlives_environment`) to be appropriate to the function and then adds constraints + /// derived from the function body. + /// + /// Note that it does **not** restore the state of the fields that + /// it updates! This is intentional, since -- for the main + /// function -- we wish to be able to read the final + /// `outlives_environment` and other fields from the caller. For + /// closures, however, we save and restore any "scoped state" + /// before we invoke this function. (See `visit_fn` in the + /// `intravisit::Visitor` impl below.) fn visit_fn_body(&mut self, id: ast::NodeId, // the id of the fn itself body: &'gcx hir::Body, @@ -304,9 +302,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_fn_body(id={})", id); let body_id = body.id(); + self.body_id = body_id.node_id; let call_site = region::Scope::CallSite(body.value.hir_id.local_id); - let old_call_site_scope = self.set_call_site_scope(Some(call_site)); + self.call_site_scope = Some(call_site); let fn_sig = { let fn_hir_id = self.tcx.hir.node_to_hir_id(id); @@ -318,8 +317,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } }; - let old_region_bounds_pairs_len = self.region_bound_pairs.len(); - // Collect the types from which we create inferred bounds. // For the return type, if diverging, substitute `bool` just // because it will have no effect. @@ -328,8 +325,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let fn_sig_tys: Vec<_> = fn_sig.inputs().iter().cloned().chain(Some(fn_sig.output())).collect(); - let old_body_id = self.set_body_id(body_id.node_id); - self.relate_free_regions(&fn_sig_tys[..], body_id.node_id, span); + self.outlives_environment.add_implied_bounds( + self.fcx, + &fn_sig_tys[..], + body_id.node_id, + span); self.link_fn_args(region::Scope::Node(body.value.hir_id.local_id), &body.arguments); self.visit_body(body); self.visit_region_obligations(body_id.node_id); @@ -338,15 +338,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_fn_body body.id {:?} call_site_scope: {:?}", body.id(), call_site_scope); let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope)); + let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id); self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region); - self.region_bound_pairs.truncate(old_region_bounds_pairs_len); - - self.set_body_id(old_body_id); - self.set_call_site_scope(old_call_site_scope); + self.constrain_anon_types(); } fn visit_region_obligations(&mut self, node_id: ast::NodeId) @@ -358,231 +356,205 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // obligations. So make sure we process those. self.select_all_obligations_or_error(); - // Make a copy of the region obligations vec because we'll need - // to be able to borrow the fulfillment-cx below when projecting. - let region_obligations = - self.fulfillment_cx - .borrow() - .region_obligations(node_id) - .to_vec(); - - for r_o in ®ion_obligations { - debug!("visit_region_obligations: r_o={:?} cause={:?}", - r_o, r_o.cause); - let sup_type = self.resolve_type(r_o.sup_type); - let origin = self.code_to_origin(&r_o.cause, sup_type); - self.type_must_outlive(origin, sup_type, r_o.sub_region); - } - - // Processing the region obligations should not cause the list to grow further: - assert_eq!(region_obligations.len(), - self.fulfillment_cx.borrow().region_obligations(node_id).len()); - } - - fn code_to_origin(&self, - cause: &traits::ObligationCause<'tcx>, - sup_type: Ty<'tcx>) - -> SubregionOrigin<'tcx> { - SubregionOrigin::from_obligation_cause(cause, - || infer::RelateParamBound(cause.span, sup_type)) + self.infcx.process_registered_region_obligations( + self.outlives_environment.region_bound_pairs(), + self.implicit_region_bound, + self.param_env, + self.body_id); } - /// This method populates the region map's `free_region_map`. It walks over the transformed - /// argument and return types for each function just before we check the body of that function, - /// looking for types where you have a borrowed pointer to other borrowed data (e.g., `&'a &'b - /// [usize]`. We do not allow references to outlive the things they point at, so we can assume - /// that `'a <= 'b`. This holds for both the argument and return types, basically because, on - /// the caller side, the caller is responsible for checking that the type of every expression - /// (including the actual values for the arguments, as well as the return type of the fn call) - /// is well-formed. + /// Go through each of the existential `impl Trait` types that + /// appear in the function signature. For example, if the current + /// function is as follows: /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` - fn relate_free_regions(&mut self, - fn_sig_tys: &[Ty<'tcx>], - body_id: ast::NodeId, - span: Span) { - debug!("relate_free_regions >>"); - - for &ty in fn_sig_tys { - let ty = self.resolve_type(ty); - debug!("relate_free_regions(t={:?})", ty); - let implied_bounds = self.implied_bounds(body_id, ty, span); - - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. - for implication in implied_bounds { - debug!("implication: {:?}", implication); - match implication { - ImpliedBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), - &ty::ReVar(vid_b)) | - ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), - &ty::ReVar(vid_b)) => { - self.add_given(r_a, vid_b); - } - ImpliedBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); - } - ImpliedBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); - } - ImpliedBound::RegionSubRegion(r_a, r_b) => { - // In principle, we could record (and take - // advantage of) every relationship here, but - // we are also free not to -- it simply means - // strictly less that we can successfully type - // check. Right now we only look for things - // relationships between free regions. (It may - // also be that we should revise our inference - // system to be more general and to make use - // of *every* relationship that arises here, - // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); + /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) + /// + /// we would iterate through the `impl Bar<'a>` and the + /// `impl Bar<'b>` here. Remember that each of them has + /// their own "abstract type" definition created for them. As + /// we iterate, we have a `def_id` that corresponds to this + /// definition, and a set of substitutions `substs` that are + /// being supplied to this abstract typed definition in the + /// signature: + /// + /// abstract type Foo1<'x>: Bar<'x>; + /// abstract type Foo2<'x>: Bar<'x>; + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ^^^^ ^^ substs + /// def_id + /// + /// In addition, for each of the types we will have a type + /// variable `concrete_ty` containing the concrete type that + /// this function uses for `Foo1` and `Foo2`. That is, + /// conceptually, there is a constraint like: + /// + /// for<'a> (Foo1<'a> = C) + /// + /// where `C` is `concrete_ty`. For this equation to be satisfiable, + /// the type `C` can only refer to two regions: `'static` and `'a`. + /// + /// The problem is that this type `C` may contain arbitrary + /// region variables. In fact, it is fairly likely that it + /// does! Consider this possible definition of `foo`: + /// + /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { + /// (&*x, &*y) + /// } + /// + /// Here, the values for the concrete types of the two impl + /// traits will include inference variables: + /// + /// &'0 i32 + /// &'1 i32 + /// + /// Ordinarily, the subtyping rules would ensure that these are + /// sufficiently large. But since `impl Bar<'a>` isn't a specific + /// type per se, we don't get such constraints by default. This + /// is where this function comes into play. It adds extra + /// constraints to ensure that all the regions which appear in the + /// inferred type are regions that could validly appear. + /// + /// This is actually a bit of a tricky constraint in general. We + /// want to say that each variable (e.g., `'0``) can only take on + /// values that were supplied as arguments to the abstract type + /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in + /// scope. We don't have a constraint quite of this kind in the current + /// region checker. + /// + /// What we *do* have is the `<=` relation. So what we do is to + /// find the LUB of all the arguments that appear in the substs: + /// in this case, that would be `LUB('a) = 'a`, and then we apply + /// that as a least bound to the variables (e.g., `'a <= '0`). + /// + /// In some cases this is pretty suboptimal. Consider this example: + /// + /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } + /// + /// Here, the regions `'a` and `'b` appear in the substitutions, + /// so we would generate `LUB('a, 'b)` as a kind of "minimal upper + /// bound", but that turns out be `'static` -- which is clearly + /// too strict! + fn constrain_anon_types(&mut self) { + debug!("constrain_anon_types()"); + + for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { + let concrete_ty = self.resolve_type(anon_defn.concrete_ty); + + debug!("constrain_anon_types: def_id={:?}", def_id); + debug!("constrain_anon_types: anon_defn={:#?}", anon_defn); + debug!("constrain_anon_types: concrete_ty={:?}", concrete_ty); + + let abstract_type_generics = self.tcx.generics_of(def_id); + + let span = self.tcx.def_span(def_id); + + // If there are required region bounds, we can just skip + // ahead. There will already be a registered region + // obligation related `concrete_ty` to those regions. + if anon_defn.required_region_bounds.len() != 0 { + continue; + } + + // There were no `required_region_bounds`, + // so we have to search for a `least_region`. + // Go through all the regions used as arguments to the + // abstract type. These are the parameters to the abstract + // type; so in our example above, `substs` would contain + // `['a]` for the first impl trait and `'b` for the + // second. + let mut least_region = None; + for region_def in &abstract_type_generics.regions { + // Find the index of this region in the list of substitutions. + let index = region_def.index as usize; + + // Get the value supplied for this region from the substs. + let subst_arg = anon_defn.substs[index].as_region().unwrap(); + + // Compute the least upper bound of it with the other regions. + debug!("constrain_anon_types: least_region={:?}", least_region); + debug!("constrain_anon_types: subst_arg={:?}", subst_arg); + match least_region { + None => least_region = Some(subst_arg), + Some(lr) => { + if self.outlives_environment + .free_region_map() + .sub_free_regions(lr, subst_arg) { + // keep the current least region + } else if self.outlives_environment + .free_region_map() + .sub_free_regions(subst_arg, lr) { + // switch to `subst_arg` + least_region = Some(subst_arg); + } else { + // There are two regions (`lr` and + // `subst_arg`) which are not relatable. We can't + // find a best choice. + self.tcx + .sess + .struct_span_err(span, "ambiguous lifetime bound in `impl Trait`") + .span_label(span, + format!("neither `{}` nor `{}` outlives the other", + lr, subst_arg)) + .emit(); + + least_region = Some(self.tcx.mk_region(ty::ReEmpty)); + break; + } } } } - } - debug!("<< relate_free_regions"); - } + let least_region = least_region.unwrap_or(self.tcx.types.re_static); + debug!("constrain_anon_types: least_region={:?}", least_region); + + // Require that the type `concrete_ty` outlives + // `least_region`, modulo any type parameters that appear + // in the type, which we ignore. This is because impl + // trait values are assumed to capture all the in-scope + // type parameters. This little loop here just invokes + // `outlives` repeatedly, draining all the nested + // obligations that result. + let mut types = vec![concrete_ty]; + let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r); + while let Some(ty) = types.pop() { + let mut components = self.tcx.outlives_components(ty); + while let Some(component) = components.pop() { + match component { + Component::Region(r) => { + bound_region(r); + } - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. - fn implied_bounds(&mut self, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span) - -> Vec> { - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Currently (at least) these resulting - // predicates are always guaranteed to be a subset of the original - // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; - - let mut implied_bounds = vec![]; - - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - let obligations = - wf::obligations(self, self.fcx.param_env, body_id, ty, span) - .unwrap_or(vec![]); - - // NB: All of these predicates *ought* to be easily proven - // true. In fact, their correctness is (mostly) implied by - // other parts of the program. However, in #42552, we had - // an annoying scenario where: - // - // - Some `T::Foo` gets normalized, resulting in a - // variable `_1` and a `T: Trait` constraint - // (not sure why it couldn't immediately get - // solved). This result of `_1` got cached. - // - These obligations were dropped on the floor here, - // rather than being registered. - // - Then later we would get a request to normalize - // `T::Foo` which would result in `_1` being used from - // the cache, but hence without the `T: Trait` - // constraint. As a result, `_1` never gets resolved, - // and we get an ICE (in dropck). - // - // Therefore, we register any predicates involving - // inference variables. We restrict ourselves to those - // involving inference variables both for efficiency and - // to avoids duplicate errors that otherwise show up. - self.fcx.register_predicates( - obligations.iter() - .filter(|o| o.predicate.has_infer_types()) - .cloned()); - - // From the full set of obligations, just filter down to the - // region relationships. - implied_bounds.extend( - obligations - .into_iter() - .flat_map(|obligation| { - assert!(!obligation.has_escaping_regions()); - match obligation.predicate { - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::Projection(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ConstEvaluatable(..) => - vec![], - - ty::Predicate::WellFormed(subty) => { - wf_types.push(subty); - vec![] - } + Component::Param(_) => { + // ignore type parameters like `T`, they are captured + // implicitly by the `impl Trait` + } - ty::Predicate::RegionOutlives(ref data) => - match self.tcx.no_late_bound_regions(data) { - None => - vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => - vec![ImpliedBound::RegionSubRegion(r_b, r_a)], - }, - - ty::Predicate::TypeOutlives(ref data) => - match self.tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = self.resolve_type_vars_if_possible(&ty_a); - let components = self.tcx.outlives_components(ty_a); - self.implied_bounds_from_components(r_b, components) - } - }, - }})); - } + Component::UnresolvedInferenceVariable(_) => { + // we should get an error that more type + // annotations are needed in this case + self.tcx.sess.delay_span_bug(span, "unresolved inf var in anon"); + } - implied_bounds - } + Component::Projection(ty::ProjectionTy { substs, item_def_id: _ }) => { + for r in substs.regions() { + bound_region(r); + } + types.extend(substs.types()); + } - /// When we have an implied bound that `T: 'a`, we can further break - /// this down to determine what relationships would have to hold for - /// `T: 'a` to hold. We get to assume that the caller has validated - /// those relationships. - fn implied_bounds_from_components(&self, - sub_region: ty::Region<'tcx>, - sup_components: Vec>) - -> Vec> - { - sup_components - .into_iter() - .flat_map(|component| { - match component { - Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], - Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], - Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], - Component::EscapingProjection(_) => - // If the projection has escaping regions, don't - // try to infer any implied bounds even for its - // free components. This is conservative, because - // the caller will still have to prove that those - // free components outlive `sub_region`. But the - // idea is that the WAY that the caller proves - // that may change in the future and we want to - // give ourselves room to get smarter here. - vec![], - Component::UnresolvedInferenceVariable(..) => - vec![], + Component::EscapingProjection(more_components) => { + components.extend(more_components); + } + } } - }) - .collect() + } + } } fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, - &self.free_region_map); + &self.outlives_environment); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { @@ -638,10 +610,28 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { NestedVisitorMap::None } - fn visit_fn(&mut self, _fk: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl, - b: hir::BodyId, span: Span, id: ast::NodeId) { - let body = self.tcx.hir.body(b); - self.visit_fn_body(id, body, span) + fn visit_fn(&mut self, + fk: intravisit::FnKind<'gcx>, + _: &'gcx hir::FnDecl, + body_id: hir::BodyId, + span: Span, + id: ast::NodeId) { + assert!(match fk { intravisit::FnKind::Closure(..) => true, _ => false }, + "visit_fn invoked for something other than a closure"); + + // Save state of current function before invoking + // `visit_fn_body`. We will restore afterwards. + let old_body_id = self.body_id; + let old_call_site_scope = self.call_site_scope; + let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + + let body = self.tcx.hir.body(body_id); + self.visit_fn_body(id, body, span); + + // Restore state from previous function. + self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.call_site_scope = old_call_site_scope; + self.body_id = old_body_id; } //visit_pat: visit_pat, // (..) see above @@ -815,7 +805,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // the type of the node expr.id here *before applying // adjustments*. // - // FIXME(#6268) nested method calls requires that this rule change + // FIXME(https://github.com/rust-lang/rfcs/issues/811) + // nested method calls requires that this rule change let ty0 = self.resolve_node_type(expr.hir_id); self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region); intravisit::walk_expr(self, expr); @@ -1136,6 +1127,27 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin, ty, minimum_lifetime); } + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive(&self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>) + { + self.infcx.type_must_outlive(self.outlives_environment.region_bound_pairs(), + self.implicit_region_bound, + self.param_env, + origin, + ty, + region); + } + /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the /// resulting pointer is linked to the lifetime of its guarantor (if any). fn link_addr_of(&mut self, expr: &hir::Expr, @@ -1491,345 +1503,4 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin.clone(), ty, expr_region); } } - - /// Ensures that type is well-formed in `region`, which implies (among - /// other things) that all borrowed data reachable via `ty` outlives - /// `region`. - pub fn type_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>) - { - let ty = self.resolve_type(ty); - - debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", - ty, - region, - origin); - - assert!(!ty.has_escaping_regions()); - - let components = self.tcx.outlives_components(ty); - self.components_must_outlive(origin, components, region); - } - - fn components_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - components: Vec>, - region: ty::Region<'tcx>) - { - for component in components { - let origin = origin.clone(); - match component { - Component::Region(region1) => { - self.sub_regions(origin, region, region1); - } - Component::Param(param_ty) => { - self.param_ty_must_outlive(origin, region, param_ty); - } - Component::Projection(projection_ty) => { - self.projection_must_outlive(origin, region, projection_ty); - } - Component::EscapingProjection(subcomponents) => { - self.components_must_outlive(origin, subcomponents, region); - } - Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be - self.tcx.sess.delay_span_bug( - origin.span(), - &format!("unresolved inference variable in outlives: {:?}", v)); - } - } - } - } - - fn param_ty_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - param_ty: ty::ParamTy) { - debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", - region, param_ty, origin); - - let verify_bound = self.param_bound(param_ty); - let generic = GenericKind::Param(param_ty); - self.verify_generic_bound(origin, generic, region, verify_bound); - } - - fn projection_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>) - { - debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, projection_ty, origin); - - // This case is thorny for inference. The fundamental problem is - // that there are many cases where we have choice, and inference - // doesn't like choice (the current region inference in - // particular). :) First off, we have to choose between using the - // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and - // OutlivesProjectionComponent rules, any one of which is - // sufficient. If there are no inference variables involved, it's - // not hard to pick the right rule, but if there are, we're in a - // bit of a catch 22: if we picked which rule we were going to - // use, we could add constraints to the region inference graph - // that make it apply, but if we don't add those constraints, the - // rule might not apply (but another rule might). For now, we err - // on the side of adding too few edges into the graph. - - // Compute the bounds we can derive from the environment or trait - // definition. We know that the projection outlives all the - // regions in this list. - let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); - - debug!("projection_must_outlive: env_bounds={:?}", - env_bounds); - - // If we know that the projection outlives 'static, then we're - // done here. - if env_bounds.contains(&&ty::ReStatic) { - debug!("projection_must_outlive: 'static as declared bound"); - return; - } - - // If declared bounds list is empty, the only applicable rule is - // OutlivesProjectionComponent. If there are inference variables, - // then, we can break down the outlives into more primitive - // components without adding unnecessary edges. - // - // If there are *no* inference variables, however, we COULD do - // this, but we choose not to, because the error messages are less - // good. For example, a requirement like `T::Item: 'r` would be - // translated to a requirement that `T: 'r`; when this is reported - // to the user, it will thus say "T: 'r must hold so that T::Item: - // 'r holds". But that makes it sound like the only way to fix - // the problem is to add `T: 'r`, which isn't true. So, if there are no - // inference variables, we use a verify constraint instead of adding - // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); - if env_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - for component_ty in projection_ty.substs.types() { - self.type_must_outlive(origin.clone(), component_ty, region); - } - - for r in projection_ty.substs.regions() { - self.sub_regions(origin.clone(), region, r); - } - - return; - } - - // If we find that there is a unique declared bound `'b`, and this bound - // appears in the trait reference, then the best action is to require that `'b:'r`, - // so do that. This is best no matter what rule we use: - // - // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to - // the requirement that `'b:'r` - // - OutlivesProjectionComponent: this would require `'b:'r` in addition to - // other conditions - if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { - let unique_bound = env_bounds[0]; - debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound); - if projection_ty.substs.regions().any(|r| env_bounds.contains(&r)) { - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.sub_regions(origin.clone(), region, unique_bound); - return; - } - } - - // Fallback to verifying after the fact that there exists a - // declared bound, or that all the components appearing in the - // projection outlive; in some cases, this may add insufficient - // edges into the inference graph, leading to inference failures - // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); - let generic = GenericKind::Projection(projection_ty); - self.verify_generic_bound(origin, generic.clone(), region, verify_bound); - } - - fn type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { - ty::TyParam(p) => { - self.param_bound(p) - } - ty::TyProjection(data) => { - let declared_bounds = self.projection_declared_bounds(span, data); - self.projection_bound(span, declared_bounds, data) - } - _ => { - self.recursive_type_bound(span, ty) - } - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", - param_ty); - - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); - - // Add in the default bound of fn body that applies to all in - // scope type parameters: - param_bounds.extend(self.implicit_region_bound); - - VerifyBound::AnyRegion(param_bounds) - } - - fn projection_declared_bounds(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds.extend_from_slice( - &self.declared_projection_bounds_from_trait(span, projection_ty)); - - declared_bounds - } - - fn projection_bound(&self, - span: Span, - declared_bounds: Vec>, - projection_ty: ty::ProjectionTy<'tcx>) - -> VerifyBound<'tcx> { - debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, projection_ty); - - // see the extensive comment in projection_must_outlive - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(span, ty); - - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) - } - - fn recursive_type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = vec![]; - - for subty in ty.walk_shallow() { - bounds.push(self.type_bound(span, subty)); - } - - let mut regions = ty.regions(); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); - - if bounds.len() == 1 { - bounds.pop().unwrap() - } else { - VerifyBound::AllBounds(bounds) - } - } - - fn declared_generic_bounds_from_env(&self, generic: GenericKind<'tcx>) - -> Vec> - { - let param_env = &self.param_env; - - // To start, collect bounds from user: - let mut param_bounds = self.tcx.required_region_bounds(generic.to_ty(self.tcx), - param_env.caller_bounds.to_vec()); - - // Next, collect regions we scraped from the well-formedness - // constraints in the fn signature. To do that, we walk the list - // of known relations from the fn ctxt. - // - // This is crucial because otherwise code like this fails: - // - // fn foo<'a, A>(x: &'a A) { x.bar() } - // - // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. - for &(r, p) in &self.region_bound_pairs { - debug!("generic={:?} p={:?}", - generic, - p); - if generic == p { - param_bounds.push(r); - } - } - - param_bounds - } - - fn declared_projection_bounds_from_trait(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - debug!("projection_bounds(projection_ty={:?})", - projection_ty); - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - - // Say we have a projection `>::SomeType`. We are interested - // in looking for a trait definition like: - // - // ``` - // trait SomeTrait<'a> { - // type SomeType : 'a; - // } - // ``` - // - // we can thus deduce that `>::SomeType : 'a`. - let trait_predicates = self.tcx.predicates_of(projection_ty.trait_ref(self.tcx).def_id); - assert_eq!(trait_predicates.parent, None); - let predicates = trait_predicates.predicates.as_slice().to_vec(); - traits::elaborate_predicates(self.tcx, predicates) - .filter_map(|predicate| { - // we're only interesting in `T : 'a` style predicates: - let outlives = match predicate { - ty::Predicate::TypeOutlives(data) => data, - _ => { return None; } - }; - - debug!("projection_bounds: outlives={:?} (1)", - outlives); - - // apply the substitutions (and normalize any projected types) - let outlives = self.instantiate_type_scheme(span, - projection_ty.substs, - &outlives); - - debug!("projection_bounds: outlives={:?} (2)", - outlives); - - let region_result = self.commit_if_ok(|_| { - let (outlives, _) = - self.replace_late_bound_regions_with_fresh_var( - span, - infer::AssocTypeProjection(projection_ty.item_def_id), - &outlives); - - debug!("projection_bounds: outlives={:?} (3)", - outlives); - - // check whether this predicate applies to our current projection - let cause = self.fcx.misc(span); - match self.at(&cause, self.fcx.param_env).eq(outlives.0, ty) { - Ok(ok) => Ok((ok, outlives.1)), - Err(_) => Err(()) - } - }).map(|(ok, result)| { - self.register_infer_ok_obligations(ok); - result - }); - - debug!("projection_bounds: region_result={:?}", - region_result); - - region_result.ok() - }) - .collect() - } } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index d179b390a2918..2e0d0ddfc3936 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -45,16 +45,14 @@ use super::FnCtxt; use middle::expr_use_visitor as euv; use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; +use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; use rustc::infer::UpvarRegion; use syntax::ast; use syntax_pos::Span; use rustc::hir; -use rustc::hir::def_id::DefIndex; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use rustc::util::nodemap::FxHashMap; - -use std::collections::hash_map::Entry; +use rustc::hir::def_id::LocalDefId; +use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn closure_analyze(&self, body: &'gcx hir::Body) { @@ -65,7 +63,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } -struct InferBorrowKindVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +struct InferBorrowKindVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, } @@ -79,14 +77,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> { hir::ExprClosure(cc, _, body_id, _, is_generator) => { let body = self.fcx.tcx.hir.body(body_id); self.visit_body(body); - self.fcx.analyze_closure((expr.id, expr.hir_id), - expr.span, - body, - cc, - is_generator); + self.fcx + .analyze_closure(expr.id, expr.hir_id, expr.span, body, cc, is_generator); } - _ => { } + _ => {} } intravisit::walk_expr(self, expr); @@ -94,90 +89,110 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for InferBorrowKindVisitor<'a, 'gcx, 'tcx> { } impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - fn analyze_closure(&self, - (closure_node_id, closure_hir_id): (ast::NodeId, hir::HirId), - span: Span, - body: &hir::Body, - capture_clause: hir::CaptureClause, - gen: bool) { + fn analyze_closure( + &self, + closure_node_id: ast::NodeId, + closure_hir_id: hir::HirId, + span: Span, + body: &hir::Body, + capture_clause: hir::CaptureClause, + is_generator: bool, + ) { /*! * Analysis starting point. */ - debug!("analyze_closure(id={:?}, body.id={:?})", closure_node_id, body.id()); + debug!( + "analyze_closure(id={:?}, body.id={:?})", + closure_node_id, + body.id() + ); - let infer_kind = if gen { - false - } else { - match self.tables - .borrow_mut() - .closure_kinds_mut() - .entry(closure_hir_id) { - Entry::Occupied(_) => false, - Entry::Vacant(entry) => { - debug!("check_closure: adding closure {:?} as Fn", closure_node_id); - entry.insert((ty::ClosureKind::Fn, None)); - true - } + // Extract the type of the closure. + let (closure_def_id, closure_substs) = match self.node_ty(closure_hir_id).sty { + ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => (def_id, substs), + ref t => { + span_bug!( + span, + "type of closure expr {:?} is not a closure {:?}", + closure_node_id, + t + ); } }; - let closure_def_id = self.tcx.hir.local_def_id(closure_node_id); + let infer_kind = if is_generator { + false + } else { + self.closure_kind(closure_def_id, closure_substs).is_none() + }; self.tcx.with_freevars(closure_node_id, |freevars| { for freevar in freevars { let upvar_id = ty::UpvarId { var_id: self.tcx.hir.node_to_hir_id(freevar.var_id()), - closure_expr_id: closure_def_id.index, + closure_expr_id: LocalDefId::from_def_id(closure_def_id), }; debug!("seed upvar_id {:?}", upvar_id); let capture_kind = match capture_clause { - hir::CaptureByValue => { - ty::UpvarCapture::ByValue - } + hir::CaptureByValue => ty::UpvarCapture::ByValue, hir::CaptureByRef => { let origin = UpvarRegion(upvar_id, span); let freevar_region = self.next_region_var(origin); - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, - region: freevar_region }; + let upvar_borrow = ty::UpvarBorrow { + kind: ty::ImmBorrow, + region: freevar_region, + }; ty::UpvarCapture::ByRef(upvar_borrow) } }; - self.tables.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind); + self.tables + .borrow_mut() + .upvar_capture_map + .insert(upvar_id, capture_kind); } }); - { - let body_owner_def_id = self.tcx.hir.body_owner_def_id(body.id()); - let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id); - let mut delegate = InferBorrowKind { - fcx: self, - adjust_closure_kinds: FxHashMap(), - adjust_upvar_captures: ty::UpvarCaptureMap::default(), - }; - euv::ExprUseVisitor::with_infer(&mut delegate, - &self.infcx, - self.param_env, - region_scope_tree, - &self.tables.borrow()) - .consume_body(body); - - // Write the adjusted values back into the main tables. - if infer_kind { - if let Some(kind) = delegate.adjust_closure_kinds - .remove(&closure_def_id.index) { - self.tables - .borrow_mut() - .closure_kinds_mut() - .insert(closure_hir_id, kind); - } + let body_owner_def_id = self.tcx.hir.body_owner_def_id(body.id()); + let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id); + let mut delegate = InferBorrowKind { + fcx: self, + closure_def_id: closure_def_id, + current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, + current_origin: None, + adjust_upvar_captures: ty::UpvarCaptureMap::default(), + }; + euv::ExprUseVisitor::with_infer( + &mut delegate, + &self.infcx, + self.param_env, + region_scope_tree, + &self.tables.borrow(), + ).consume_body(body); + + if infer_kind { + // Unify the (as yet unbound) type variable in the closure + // substs with the kind we inferred. + let inferred_kind = delegate.current_closure_kind; + let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx); + self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); + + // If we have an origin, store it. + if let Some(origin) = delegate.current_origin { + self.tables + .borrow_mut() + .closure_kind_origins_mut() + .insert(closure_hir_id, origin); } - self.tables.borrow_mut().upvar_capture_map.extend( - delegate.adjust_upvar_captures); } + self.tables + .borrow_mut() + .upvar_capture_map + .extend(delegate.adjust_upvar_captures); + // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure // implements (Fn vs FnMut etc). We now have some updates to do @@ -190,36 +205,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // C, then the type would have infinite size (and the // inference algorithm will reject it). - // Extract the type variables UV0...UVn. - let (def_id, closure_substs) = match self.node_ty(closure_hir_id).sty { - ty::TyClosure(def_id, substs) | - ty::TyGenerator(def_id, substs, _) => (def_id, substs), - ref t => { - span_bug!( - span, - "type of closure expr {:?} is not a closure {:?}", - closure_node_id, t); - } - }; - - // Equate the type variables with the actual types. + // Equate the type variables for the upvars with the actual types. let final_upvar_tys = self.final_upvar_tys(closure_node_id); - debug!("analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}", - closure_node_id, closure_substs, final_upvar_tys); - for (upvar_ty, final_upvar_ty) in - closure_substs.upvar_tys(def_id, self.tcx).zip(final_upvar_tys) + debug!( + "analyze_closure: id={:?} closure_substs={:?} final_upvar_tys={:?}", + closure_node_id, + closure_substs, + final_upvar_tys + ); + for (upvar_ty, final_upvar_ty) in closure_substs + .upvar_tys(closure_def_id, self.tcx) + .zip(final_upvar_tys) { self.demand_eqtype(span, final_upvar_ty, upvar_ty); } // If we are also inferred the closure kind here, // process any deferred resolutions. - if infer_kind { - let deferred_call_resolutions = - self.remove_deferred_call_resolutions(closure_def_id); - for deferred_call_resolution in deferred_call_resolutions { - deferred_call_resolution.resolve(self); - } + let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); + for deferred_call_resolution in deferred_call_resolutions { + deferred_call_resolution.resolve(self); } } @@ -231,54 +236,81 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // This may change if abstract return types of some sort are // implemented. let tcx = self.tcx; - let closure_def_index = tcx.hir.local_def_id(closure_id).index; + let closure_def_index = tcx.hir.local_def_id(closure_id); tcx.with_freevars(closure_id, |freevars| { - freevars.iter().map(|freevar| { - let var_node_id = freevar.var_id(); - let var_hir_id = tcx.hir.node_to_hir_id(var_node_id); - let freevar_ty = self.node_ty(var_hir_id); - let upvar_id = ty::UpvarId { - var_id: var_hir_id, - closure_expr_id: closure_def_index, - }; - let capture = self.tables.borrow().upvar_capture(upvar_id); - - debug!("var_id={:?} freevar_ty={:?} capture={:?}", - var_node_id, freevar_ty, capture); - - match capture { - ty::UpvarCapture::ByValue => freevar_ty, - ty::UpvarCapture::ByRef(borrow) => - tcx.mk_ref(borrow.region, - ty::TypeAndMut { - ty: freevar_ty, - mutbl: borrow.kind.to_mutbl_lossy(), - }), - } - }).collect() + freevars + .iter() + .map(|freevar| { + let var_node_id = freevar.var_id(); + let var_hir_id = tcx.hir.node_to_hir_id(var_node_id); + let freevar_ty = self.node_ty(var_hir_id); + let upvar_id = ty::UpvarId { + var_id: var_hir_id, + closure_expr_id: LocalDefId::from_def_id(closure_def_index), + }; + let capture = self.tables.borrow().upvar_capture(upvar_id); + + debug!( + "var_id={:?} freevar_ty={:?} capture={:?}", + var_node_id, + freevar_ty, + capture + ); + + match capture { + ty::UpvarCapture::ByValue => freevar_ty, + ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref( + borrow.region, + ty::TypeAndMut { + ty: freevar_ty, + mutbl: borrow.kind.to_mutbl_lossy(), + }, + ), + } + }) + .collect() }) } } -struct InferBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +struct InferBorrowKind<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - adjust_closure_kinds: FxHashMap)>, + + // The def-id of the closure whose kind and upvar accesses are being inferred. + closure_def_id: DefId, + + // The kind that we have inferred that the current closure + // requires. Note that we *always* infer a minimal kind, even if + // we don't always *use* that in the final result (i.e., sometimes + // we've taken the closure kind from the expectations instead, and + // for generators we don't even implement the closure traits + // really). + current_closure_kind: ty::ClosureKind, + + // If we modified `current_closure_kind`, this field contains a `Some()` with the + // variable access that caused us to do so. + current_origin: Option<(Span, ast::Name)>, + + // For each upvar that we access, we track the minimal kind of + // access we need (ref, ref mut, move, etc). adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>, } impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { - fn adjust_upvar_borrow_kind_for_consume(&mut self, - cmt: mc::cmt<'tcx>, - mode: euv::ConsumeMode) - { - debug!("adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})", - cmt, mode); + fn adjust_upvar_borrow_kind_for_consume(&mut self, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) { + debug!( + "adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})", + cmt, + mode + ); // we only care about moves match mode { - euv::Copy => { return; } - euv::Move(_) => { } + euv::Copy => { + return; + } + euv::Move(_) => {} } let tcx = self.fcx.tcx; @@ -287,24 +319,39 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { // for that to be legal, the upvar would have to be borrowed // by value instead let guarantor = cmt.guarantor(); - debug!("adjust_upvar_borrow_kind_for_consume: guarantor={:?}", - guarantor); + debug!( + "adjust_upvar_borrow_kind_for_consume: guarantor={:?}", + guarantor + ); + debug!( + "adjust_upvar_borrow_kind_for_consume: guarantor.cat={:?}", + guarantor.cat + ); match guarantor.cat { Categorization::Deref(_, mc::BorrowedPtr(..)) | Categorization::Deref(_, mc::Implicit(..)) => { - match cmt.note { + debug!( + "adjust_upvar_borrow_kind_for_consume: found deref with note {:?}", + cmt.note + ); + match guarantor.note { mc::NoteUpvarRef(upvar_id) => { - debug!("adjust_upvar_borrow_kind_for_consume: \ - setting upvar_id={:?} to by value", - upvar_id); + debug!( + "adjust_upvar_borrow_kind_for_consume: \ + setting upvar_id={:?} to by value", + upvar_id + ); // to move out of an upvar, this must be a FnOnce closure - self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnOnce, - guarantor.span, - var_name(tcx, upvar_id.var_id)); - - self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue); + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnOnce, + guarantor.span, + var_name(tcx, upvar_id.var_id), + ); + + self.adjust_upvar_captures + .insert(upvar_id, ty::UpvarCapture::ByValue); } mc::NoteClosureEnv(upvar_id) => { // we get just a closureenv ref if this is a @@ -313,16 +360,17 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { // must still adjust the kind of the closure // to be a FnOnce closure to permit moves out // of the environment. - self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnOnce, - guarantor.span, - var_name(tcx, upvar_id.var_id)); - } - mc::NoteNone => { + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnOnce, + guarantor.span, + var_name(tcx, upvar_id.var_id), + ); } + mc::NoteNone => {} } } - _ => { } + _ => {} } } @@ -330,8 +378,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { /// to). If cmt contains any by-ref upvars, this implies that /// those upvars must be borrowed using an `&mut` borrow. fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})", - cmt); + debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})", cmt); match cmt.cat.clone() { Categorization::Deref(base, mc::Unique) | @@ -364,8 +411,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { } fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", - cmt); + debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", cmt); match cmt.cat.clone() { Categorization::Deref(base, mc::Unique) | @@ -389,16 +435,11 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { Categorization::StaticItem | Categorization::Rvalue(..) | Categorization::Local(_) | - Categorization::Upvar(..) => { - } + Categorization::Upvar(..) => {} } } - fn try_adjust_upvar_deref(&mut self, - cmt: mc::cmt<'tcx>, - borrow_kind: ty::BorrowKind) - -> bool - { + fn try_adjust_upvar_deref(&mut self, cmt: mc::cmt<'tcx>, borrow_kind: ty::BorrowKind) -> bool { assert!(match borrow_kind { ty::MutBorrow => true, ty::UniqueImmBorrow => true, @@ -418,10 +459,12 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { self.adjust_upvar_borrow_kind(upvar_id, borrow_kind); // also need to be in an FnMut closure since this is not an ImmBorrow - self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnMut, - cmt.span, - var_name(tcx, upvar_id.var_id)); + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + cmt.span, + var_name(tcx, upvar_id.var_id), + ); true } @@ -429,16 +472,16 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { // this kind of deref occurs in a `move` closure, or // for a by-value upvar; in either case, to mutate an // upvar, we need to be an FnMut closure - self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnMut, - cmt.span, - var_name(tcx, upvar_id.var_id)); + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + cmt.span, + var_name(tcx, upvar_id.var_id), + ); true } - mc::NoteNone => { - false - } + mc::NoteNone => false, } } @@ -447,13 +490,17 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { /// moving from left to right as needed (but never right to left). /// Here the argument `mutbl` is the borrow_kind that is required by /// some particular use. - fn adjust_upvar_borrow_kind(&mut self, - upvar_id: ty::UpvarId, - kind: ty::BorrowKind) { - let upvar_capture = self.adjust_upvar_captures.get(&upvar_id).cloned() + fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) { + let upvar_capture = self.adjust_upvar_captures + .get(&upvar_id) + .cloned() .unwrap_or_else(|| self.fcx.tables.borrow().upvar_capture(upvar_id)); - debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", - upvar_id, upvar_capture, kind); + debug!( + "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", + upvar_id, + upvar_capture, + kind + ); match upvar_capture { ty::UpvarCapture::ByValue => { @@ -466,99 +513,107 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> { (ty::ImmBorrow, ty::MutBorrow) | (ty::UniqueImmBorrow, ty::MutBorrow) => { upvar_borrow.kind = kind; - self.adjust_upvar_captures.insert(upvar_id, - ty::UpvarCapture::ByRef(upvar_borrow)); + self.adjust_upvar_captures + .insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow)); } // Take LHS: (ty::ImmBorrow, ty::ImmBorrow) | (ty::UniqueImmBorrow, ty::ImmBorrow) | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | - (ty::MutBorrow, _) => { - } + (ty::MutBorrow, _) => {} } } } } - fn adjust_closure_kind(&mut self, - closure_id: DefIndex, - new_kind: ty::ClosureKind, - upvar_span: Span, - var_name: ast::Name) { - debug!("adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", - closure_id, new_kind, upvar_span, var_name); - - let closure_kind = self.adjust_closure_kinds.get(&closure_id).cloned() - .or_else(|| { - let closure_id = self.fcx.tcx.hir.def_index_to_hir_id(closure_id); - self.fcx.tables.borrow().closure_kinds().get(closure_id).cloned() - }); - - if let Some((existing_kind, _)) = closure_kind { - debug!("adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}", - closure_id, existing_kind, new_kind); - - match (existing_kind, new_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, _) => { - // no change needed - } + fn adjust_closure_kind( + &mut self, + closure_id: LocalDefId, + new_kind: ty::ClosureKind, + upvar_span: Span, + var_name: ast::Name, + ) { + debug!( + "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", + closure_id, + new_kind, + upvar_span, + var_name + ); + + // Is this the closure whose kind is currently being inferred? + if closure_id.to_def_id() != self.closure_def_id { + debug!("adjust_closure_kind: not current closure"); + return; + } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) | - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // new kind is stronger than the old kind - self.adjust_closure_kinds.insert( - closure_id, - (new_kind, Some((upvar_span, var_name))) - ); - } + // closures start out as `Fn`. + let existing_kind = self.current_closure_kind; + + debug!( + "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}", + closure_id, + existing_kind, + new_kind + ); + + match (existing_kind, new_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, _) => { + // no change needed + } + + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) | + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // new kind is stronger than the old kind + self.current_closure_kind = new_kind; + self.current_origin = Some((upvar_span, var_name)); } } } } impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'gcx, 'tcx> { - fn consume(&mut self, - _consume_id: ast::NodeId, - _consume_span: Span, - cmt: mc::cmt<'tcx>, - mode: euv::ConsumeMode) - { + fn consume( + &mut self, + _consume_id: ast::NodeId, + _consume_span: Span, + cmt: mc::cmt<'tcx>, + mode: euv::ConsumeMode, + ) { debug!("consume(cmt={:?},mode={:?})", cmt, mode); self.adjust_upvar_borrow_kind_for_consume(cmt, mode); } - fn matched_pat(&mut self, - _matched_pat: &hir::Pat, - _cmt: mc::cmt<'tcx>, - _mode: euv::MatchMode) - {} - - fn consume_pat(&mut self, - _consume_pat: &hir::Pat, - cmt: mc::cmt<'tcx>, - mode: euv::ConsumeMode) - { + fn matched_pat(&mut self, _matched_pat: &hir::Pat, _cmt: mc::cmt<'tcx>, _mode: euv::MatchMode) { + } + + fn consume_pat(&mut self, _consume_pat: &hir::Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) { debug!("consume_pat(cmt={:?},mode={:?})", cmt, mode); self.adjust_upvar_borrow_kind_for_consume(cmt, mode); } - fn borrow(&mut self, - borrow_id: ast::NodeId, - _borrow_span: Span, - cmt: mc::cmt<'tcx>, - _loan_region: ty::Region<'tcx>, - bk: ty::BorrowKind, - _loan_cause: euv::LoanCause) - { - debug!("borrow(borrow_id={}, cmt={:?}, bk={:?})", - borrow_id, cmt, bk); + fn borrow( + &mut self, + borrow_id: ast::NodeId, + _borrow_span: Span, + cmt: mc::cmt<'tcx>, + _loan_region: ty::Region<'tcx>, + bk: ty::BorrowKind, + _loan_cause: euv::LoanCause, + ) { + debug!( + "borrow(borrow_id={}, cmt={:?}, bk={:?})", + borrow_id, + cmt, + bk + ); match bk { - ty::ImmBorrow => { } + ty::ImmBorrow => {} ty::UniqueImmBorrow => { self.adjust_upvar_borrow_kind_for_unique(cmt); } @@ -568,19 +623,16 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'gcx, 'tcx> { } } - fn decl_without_init(&mut self, - _id: ast::NodeId, - _span: Span) - {} - - fn mutate(&mut self, - _assignment_id: ast::NodeId, - _assignment_span: Span, - assignee_cmt: mc::cmt<'tcx>, - _mode: euv::MutateMode) - { - debug!("mutate(assignee_cmt={:?})", - assignee_cmt); + fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {} + + fn mutate( + &mut self, + _assignment_id: ast::NodeId, + _assignment_span: Span, + assignee_cmt: mc::cmt<'tcx>, + _mode: euv::MutateMode, + ) { + debug!("mutate(assignee_cmt={:?})", assignee_cmt); self.adjust_upvar_borrow_kind_for_mut(assignee_cmt); } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index ddbdd20430589..d4625bb58c338 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -8,19 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use astconv::ExplicitSelf; use check::{Inherited, FnCtxt}; use constrained_type_params::{identify_constrained_type_params, Parameter}; use hir::def_id::DefId; use rustc::traits::{self, ObligationCauseCode}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Lift, Ty, TyCtxt}; +use rustc::ty::util::ExplicitSelf; use rustc::util::nodemap::{FxHashSet, FxHashMap}; use rustc::middle::lang_items; use syntax::ast; +use syntax::feature_gate::{self, GateIssue}; use syntax_pos::Span; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, DiagnosticId}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir; @@ -114,7 +115,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { // FIXME(#27579) what amount of WF checking do we need for neg impls? let trait_ref = tcx.impl_trait_ref(tcx.hir.local_def_id(item.id)).unwrap(); - if !tcx.trait_has_default_impl(trait_ref.def_id) { + if !tcx.trait_is_auto(trait_ref.def_id) { error_192(tcx, item.span); } } @@ -223,10 +224,31 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { { self.for_item(item).with_fcx(|fcx, this| { let variants = lookup_fields(fcx); + let def_id = fcx.tcx.hir.local_def_id(item.id); + let packed = fcx.tcx.adt_def(def_id).repr.packed(); for variant in &variants { - // For DST, all intermediate types must be sized. - let unsized_len = if all_sized || variant.fields.is_empty() { 0 } else { 1 }; + // For DST, or when drop needs to copy things around, all + // intermediate types must be sized. + let needs_drop_copy = || { + packed && { + let ty = variant.fields.last().unwrap().ty; + let ty = fcx.tcx.erase_regions(&ty).lift_to_tcx(this.tcx) + .unwrap_or_else(|| { + span_bug!(item.span, "inference variables in {:?}", ty) + }); + ty.needs_drop(this.tcx, this.tcx.param_env(def_id)) + } + }; + let unsized_len = if + all_sized || + variant.fields.is_empty() || + needs_drop_copy() + { + 0 + } else { + 1 + }; for field in &variant.fields[..variant.fields.len() - unsized_len] { fcx.register_bound( field.ty, @@ -245,7 +267,6 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { } } - let def_id = fcx.tcx.hir.local_def_id(item.id); let predicates = fcx.tcx.predicates_of(def_id).instantiate_identity(fcx.tcx); let predicates = fcx.normalize_associated_types_in(item.span, &predicates); this.check_where_clauses(fcx, item.span, &predicates); @@ -318,7 +339,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { fn check_trait(&mut self, item: &hir::Item) { let trait_def_id = self.tcx.hir.local_def_id(item.id); - if self.tcx.trait_has_default_impl(trait_def_id) { + if self.tcx.trait_is_auto(trait_def_id) { self.check_auto_trait(trait_def_id, item.span); } @@ -430,7 +451,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { implied_bounds: &mut Vec>) { let sig = fcx.normalize_associated_types_in(span, &sig); - let sig = fcx.liberate_late_bound_regions(def_id, &sig); + let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig); for input_ty in sig.inputs() { fcx.register_wf_obligation(&input_ty, span, self.code.clone()); @@ -451,8 +472,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { method: &ty::AssociatedItem, self_ty: Ty<'tcx>) { - // check that the type of the method's receiver matches the - // method's first parameter. + // check that the method has a valid receiver type, given the type `Self` debug!("check_method_receiver({:?}, self_ty={:?})", method, self_ty); @@ -464,30 +484,61 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { let sig = fcx.tcx.fn_sig(method.def_id); let sig = fcx.normalize_associated_types_in(span, &sig); - let sig = fcx.liberate_late_bound_regions(method.def_id, &sig); + let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, &sig); debug!("check_method_receiver: sig={:?}", sig); + let self_ty = fcx.normalize_associated_types_in(span, &self_ty); + let self_ty = fcx.tcx.liberate_late_bound_regions( + method.def_id, + &ty::Binder(self_ty) + ); + let self_arg_ty = sig.inputs()[0]; - let rcvr_ty = match ExplicitSelf::determine(self_ty, self_arg_ty) { - ExplicitSelf::ByValue => self_ty, - ExplicitSelf::ByReference(region, mutbl) => { - fcx.tcx.mk_ref(region, ty::TypeAndMut { - ty: self_ty, - mutbl, - }) + + let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver); + let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty); + let self_arg_ty = fcx.tcx.liberate_late_bound_regions( + method.def_id, + &ty::Binder(self_arg_ty) + ); + + let mut autoderef = fcx.autoderef(span, self_arg_ty); + + loop { + if let Some((potential_self_ty, _)) = autoderef.next() { + debug!("check_method_receiver: potential self type `{:?}` to match `{:?}`", + potential_self_ty, self_ty); + + if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() { + autoderef.finalize(); + if let Some(mut err) = fcx.demand_eqtype_with_origin( + &cause, self_ty, potential_self_ty) { + err.emit(); + } + break + } + } else { + fcx.tcx.sess.diagnostic().mut_span_err( + span, &format!("invalid `self` type: {:?}", self_arg_ty)) + .note(&format!("type must be `{:?}` or a type that dereferences to it`", self_ty)) + .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box`") + .code(DiagnosticId::Error("E0307".into())) + .emit(); + return } - ExplicitSelf::ByBox => fcx.tcx.mk_box(self_ty) - }; - let rcvr_ty = fcx.normalize_associated_types_in(span, &rcvr_ty); - let rcvr_ty = fcx.liberate_late_bound_regions(method.def_id, - &ty::Binder(rcvr_ty)); + } - debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty); + let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok(); + let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty); - let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver); - if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) { - err.emit(); + if let ExplicitSelf::Other = self_kind { + if !fcx.tcx.sess.features.borrow().arbitrary_self_types { + feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span, + GateIssue::Language, "arbitrary `self` types are unstable") + .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box`") + .emit(); + } } } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 3c650718a4bfd..1052f031bbf14 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -18,11 +18,13 @@ use rustc::hir::def_id::{DefId, DefIndex}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::infer::{InferCtxt}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::fold::{TypeFolder,TypeFoldable}; -use rustc::util::nodemap::DefIdSet; +use rustc::ty::fold::{TypeFolder, TypeFoldable}; +use rustc::ty::subst::{Kind, Substs}; +use rustc::util::nodemap::{DefIdSet, FxHashMap}; use syntax::ast; use syntax_pos::Span; use std::mem; +use std::rc::Rc; /////////////////////////////////////////////////////////////////////////// // Entry point @@ -45,16 +47,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wbcx.visit_anon_types(); wbcx.visit_cast_types(); wbcx.visit_free_region_map(); - wbcx.visit_generator_sigs(); - wbcx.visit_generator_interiors(); let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports, - DefIdSet()); + Rc::new(DefIdSet())); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.tables.used_trait_imports = used_trait_imports; wbcx.tables.tainted_by_errors = self.is_tainted_by_errors(); + debug!("writeback: tables for {:?} are {:#?}", item_def_id, wbcx.tables); + self.tcx.alloc_tables(wbcx.tables) } } @@ -197,6 +199,8 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { _ => {} }; + self.visit_pat_adjustments(p.span, p.hir_id); + self.visit_node_id(p.span, p.hir_id); intravisit::walk_pat(self, p); } @@ -240,21 +244,12 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); let common_local_id_root = fcx_tables.local_id_root.unwrap(); - for (&id, closure_ty) in fcx_tables.closure_tys().iter() { + for (&id, &origin) in fcx_tables.closure_kind_origins().iter() { let hir_id = hir::HirId { owner: common_local_id_root.index, local_id: id, }; - let closure_ty = self.resolve(closure_ty, &hir_id); - self.tables.closure_tys_mut().insert(hir_id, closure_ty); - } - - for (&id, &closure_kind) in fcx_tables.closure_kinds().iter() { - let hir_id = hir::HirId { - owner: common_local_id_root.index, - local_id: id, - }; - self.tables.closure_kinds_mut().insert(hir_id, closure_kind); + self.tables.closure_kind_origins_mut().insert(hir_id, origin); } } @@ -282,8 +277,23 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { fn visit_anon_types(&mut self) { let gcx = self.tcx().global_tcx(); - for (&node_id, &concrete_ty) in self.fcx.anon_types.borrow().iter() { - let inside_ty = self.resolve(&concrete_ty, &node_id); + for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { + let node_id = gcx.hir.as_local_node_id(def_id).unwrap(); + let inside_ty = self.resolve(&anon_defn.concrete_ty, &node_id); + + // Use substs to build up a reverse map from regions + // to their identity mappings. + // This is necessary because of `impl Trait` lifetimes + // are computed by replacing existing lifetimes with 'static + // and remapping only those used in the `impl Trait` return type, + // resulting in the parameters shifting. + let id_substs = Substs::identity_for_item(gcx, def_id); + let map: FxHashMap, Kind<'gcx>> = + anon_defn.substs + .iter() + .enumerate() + .map(|(index, subst)| (*subst, id_substs[index])) + .collect(); // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, @@ -292,25 +302,39 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { match *r { // 'static and early-bound regions are valid. ty::ReStatic | - ty::ReEarlyBound(_) | ty::ReEmpty => r, - ty::ReFree(_) | - ty::ReLateBound(..) | - ty::ReScope(_) | - ty::ReSkolemized(..) => { - let span = node_id.to_span(&self.fcx.tcx); - span_err!(self.tcx().sess, span, E0564, - "only named lifetimes are allowed in `impl Trait`, \ - but `{}` was found in the type `{}`", r, inside_ty); - gcx.types.re_static - } - - ty::ReVar(_) | - ty::ReErased => { - let span = node_id.to_span(&self.fcx.tcx); - span_bug!(span, "invalid region in impl Trait: {:?}", r); - } + // All other regions, we map them appropriately to their adjusted + // indices, erroring if we find any lifetimes that were not mapped + // into the new set. + _ => if let Some(r1) = + map.get(&Kind::from(r)).and_then(|k| k.as_region()) { r1 } else + { + // No mapping was found. This means that + // it is either a disallowed lifetime, + // which will be caught by regionck, or it + // is a region in a non-upvar closure + // generic, which is explicitly + // allowed. If that surprises you, read + // on. + // + // The case of closure is a somewhat + // subtle (read: hacky) consideration. The + // problem is that our closure types + // currently include all the lifetime + // parameters declared on the enclosing + // function, even if they are unused by + // the closure itself. We can't readily + // filter them out, so here we replace + // those values with `'empty`. This can't + // really make a difference to the rest of + // the compiler; those regions are ignored + // for the outlives relation, and hence + // don't affect trait selection or auto + // traits, and they are erased during + // trans. + gcx.types.re_empty + }, } }); @@ -366,30 +390,22 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } } - fn visit_generator_interiors(&mut self) { - let common_local_id_root = self.fcx.tables.borrow().local_id_root.unwrap(); - for (&id, interior) in self.fcx.tables.borrow().generator_interiors().iter() { - let hir_id = hir::HirId { - owner: common_local_id_root.index, - local_id: id, - }; - let interior = self.resolve(interior, &hir_id); - self.tables.generator_interiors_mut().insert(hir_id, interior); - } - } + fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) { + let adjustment = self.fcx + .tables + .borrow_mut() + .pat_adjustments_mut() + .remove(hir_id); + match adjustment { + None => { + debug!("No pat_adjustments for node {:?}", hir_id); + } - fn visit_generator_sigs(&mut self) { - let common_local_id_root = self.fcx.tables.borrow().local_id_root.unwrap(); - for (&id, gen_sig) in self.fcx.tables.borrow().generator_sigs().iter() { - let hir_id = hir::HirId { - owner: common_local_id_root.index, - local_id: id, - }; - let gen_sig = gen_sig.map(|s| ty::GenSig { - yield_ty: self.resolve(&s.yield_ty, &hir_id), - return_ty: self.resolve(&s.return_ty, &hir_id), - }); - self.tables.generator_sigs_mut().insert(hir_id, gen_sig); + Some(adjustment) => { + let resolved_adjustment = self.resolve(&adjustment, &span); + debug!("pat_adjustments for node {:?}: {:?}", hir_id, resolved_adjustment); + self.tables.pat_adjustments_mut().insert(hir_id, resolved_adjustment); + } } } diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index 0c35b5e6834de..f2f1e2938cb12 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -66,16 +66,34 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut used_trait_imports = DefIdSet(); for &body_id in tcx.hir.krate().bodies.keys() { let item_def_id = tcx.hir.body_owner_def_id(body_id); - let tables = tcx.typeck_tables_of(item_def_id); - let imports = &tables.used_trait_imports; + let imports = tcx.used_trait_imports(item_def_id); debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); - used_trait_imports.extend(imports); + used_trait_imports.extend(imports.iter()); } let mut visitor = CheckVisitor { tcx, used_trait_imports }; tcx.hir.krate().visit_all_item_likes(&mut visitor); for &(def_id, span) in tcx.maybe_unused_extern_crates(LOCAL_CRATE).iter() { + // The `def_id` here actually was calculated during resolution (at least + // at the time of this writing) and is being shipped to us via a side + // channel of the tcx. There may have been extra expansion phases, + // however, which ended up removing the `def_id` *after* expansion such + // as the `ReplaceBodyWithLoop` pass (which is a bit of a hack, but hey) + // + // As a result we need to verify that `def_id` is indeed still valid for + // our AST and actually present in the HIR map. If it's not there then + // there's safely nothing to warn about, and otherwise we carry on with + // our execution. + // + // Note that if we carry through to the `extern_mod_stmt_cnum` query + // below it'll cause a panic because `def_id` is actually bogus at this + // point in time otherwise. + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + if tcx.hir.find(id).is_none() { + continue + } + } let cnum = tcx.extern_mod_stmt_cnum(def_id).unwrap(); if tcx.is_compiler_builtins(cnum) { continue diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index fedfa51d61d11..d63980eaa506b 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -11,7 +11,7 @@ //! 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::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::middle::lang_items::UnsizeTraitLangItem; @@ -391,9 +391,12 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, resolve all regions. let region_scope_tree = region::ScopeTree::default(); - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates(¶m_env.caller_bounds); - infcx.resolve_regions_and_report_errors(impl_did, ®ion_scope_tree, &free_regions); + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did, + ®ion_scope_tree, + &outlives_env, + ); CoerceUnsizedInfo { custom_kind: kind diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 15e15abfb3606..569b6a2febb45 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -117,6 +117,9 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyAdt(def, _) => { self.check_def_id(item, def.did); } + ty::TyForeign(did) => { + self.check_def_id(item, did); + } ty::TyDynamic(ref data, ..) if data.principal().is_some() => { self.check_def_id(item, data.principal().unwrap().def_id()); } @@ -134,6 +137,13 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { "str", item.span); } + ty::TySlice(slice_item) if slice_item == self.tcx.types.u8 => { + self.check_primitive_impl(def_id, + lang_items.slice_u8_impl(), + "slice_u8", + "[u8]", + item.span); + } ty::TySlice(_) => { self.check_primitive_impl(def_id, lang_items.slice_impl(), diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs index 76dcfe36e4fcd..1355f711a4b14 100644 --- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs +++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use namespace::Namespace; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::hir; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::traits; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::TyCtxt; pub fn crate_inherent_impls_overlap_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) { @@ -28,19 +29,10 @@ struct InherentOverlapChecker<'a, 'tcx: 'a> { impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> { fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId, overlap: traits::OverlapResult) { - #[derive(Copy, Clone, PartialEq)] - enum Namespace { - Type, - Value, - } let name_and_namespace = |def_id| { let item = self.tcx.associated_item(def_id); - (item.name, match item.kind { - ty::AssociatedKind::Type => Namespace::Type, - ty::AssociatedKind::Const | - ty::AssociatedKind::Method => Namespace::Value, - }) + (item.name, Namespace::from(item.kind)) }; let impl_items1 = self.tcx.associated_item_def_ids(impl1); diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 6109fc57b0dfc..90a0952af0425 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -132,7 +132,7 @@ pub fn check_coherence<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { unsafety::check(tcx); orphan::check(tcx); - overlap::check_default_impls(tcx); + overlap::check_auto_impls(tcx); // these queries are executed for side-effects (error reporting): tcx.crate_inherent_impls(LOCAL_CRATE); diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index 097720adad447..9f18397362189 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -68,10 +68,10 @@ impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for OrphanChecker<'cx, 'tcx> { } // In addition to the above rules, we restrict impls of defaulted traits - // so that they can only be implemented on structs/enums. To see why this - // restriction exists, consider the following example (#22978). Imagine - // that crate A defines a defaulted trait `Foo` and a fn that operates - // on pairs of types: + // so that they can only be implemented on nominal types, such as structs, + // enums or foreign types. To see why this restriction exists, consider the + // following example (#22978). Imagine that crate A defines a defaulted trait + // `Foo` and a fn that operates on pairs of types: // // ``` // // Crate A @@ -100,20 +100,21 @@ impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for OrphanChecker<'cx, 'tcx> { // This final impl is legal according to the orpan // rules, but it invalidates the reasoning from // `two_foos` above. - debug!("trait_ref={:?} trait_def_id={:?} trait_has_default_impl={}", + debug!("trait_ref={:?} trait_def_id={:?} trait_is_auto={}", trait_ref, trait_def_id, - self.tcx.trait_has_default_impl(trait_def_id)); - if self.tcx.trait_has_default_impl(trait_def_id) && + self.tcx.trait_is_auto(trait_def_id)); + if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { let self_ty = trait_ref.self_ty(); let opt_self_def_id = match self_ty.sty { ty::TyAdt(self_def, _) => Some(self_def.did), + ty::TyForeign(did) => Some(did), _ => None, }; let msg = match opt_self_def_id { - // We only want to permit structs/enums, but not *all* structs/enums. + // We only want to permit nominal types, but not *all* nominal types. // They must be local to the current crate, so that people // can't do `unsafe impl Send for Rc` or // `impl !Send for Box`. @@ -141,7 +142,7 @@ impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for OrphanChecker<'cx, 'tcx> { } } } - hir::ItemDefaultImpl(_, ref item_trait_ref) => { + hir::ItemAutoImpl(_, ref item_trait_ref) => { // "Trait" impl debug!("coherence2::orphan check: default trait impl {}", self.tcx.hir.node_to_string(item.id)); diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 59ebae16d08ca..5cc6eaa5602fb 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -18,7 +18,7 @@ use syntax::ast; use rustc::hir; use rustc::hir::itemlikevisit::ItemLikeVisitor; -pub fn check_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { +pub fn check_auto_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut overlap = OverlapChecker { tcx }; // this secondary walk specifically checks for some other cases, @@ -74,19 +74,19 @@ struct OverlapChecker<'cx, 'tcx: 'cx> { impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for OverlapChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemDefaultImpl(..) => { - // look for another default impl; note that due to the + hir::ItemAutoImpl(..) => { + // look for another auto impl; note that due to the // general orphan/coherence rules, it must always be // in this crate. let impl_def_id = self.tcx.hir.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); - let prev_id = self.tcx.hir.trait_default_impl(trait_ref.def_id).unwrap(); + let prev_id = self.tcx.hir.trait_auto_impl(trait_ref.def_id).unwrap(); if prev_id != item.id { let mut err = struct_span_err!(self.tcx.sess, self.tcx.span_of_impl(impl_def_id).unwrap(), E0521, - "redundant default implementations of trait \ + "redundant auto implementations of trait \ `{}`:", trait_ref); err.span_note(self.tcx diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs index 4672975d056b8..280fb04e04001 100644 --- a/src/librustc_typeck/coherence/unsafety.rs +++ b/src/librustc_typeck/coherence/unsafety.rs @@ -84,7 +84,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> { impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for UnsafetyChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemDefaultImpl(unsafety, _) => { + hir::ItemAutoImpl(unsafety, _) => { self.check_unsafety_coherence(item, None, unsafety, hir::ImplPolarity::Positive); } hir::ItemImpl(unsafety, polarity, _, ref generics, ..) => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a36594cb6e557..b754c981b2101 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -43,6 +43,7 @@ use rustc_const_math::ConstInt; use std::collections::BTreeMap; use syntax::{abi, ast}; +use syntax::ptr::P; use syntax::codemap::Spanned; use syntax::symbol::{Symbol, keywords}; use syntax_pos::{Span, DUMMY_SP}; @@ -73,7 +74,7 @@ pub fn provide(providers: &mut Providers) { impl_trait_ref, impl_polarity, is_foreign_item, - is_default_impl, + is_auto_impl, ..*providers }; } @@ -130,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - if let hir::TyImplTrait(..) = ty.node { + if let hir::TyImplTraitExistential(..) = ty.node { let def_id = self.tcx.hir.local_def_id(ty.id); self.tcx.generics_of(def_id); self.tcx.predicates_of(def_id); @@ -201,7 +202,7 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { - if let Some(trait_ref) = self.tcx().no_late_bound_regions(&poly_trait_ref) { + if let Some(trait_ref) = poly_trait_ref.no_late_bound_regions() { self.tcx().mk_projection(item_def_id, trait_ref.substs) } else { // no late-bound regions, we can just ignore the binder @@ -261,19 +262,9 @@ fn type_param_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let item_node_id = tcx.hir.as_local_node_id(item_def_id).unwrap(); let ast_generics = match tcx.hir.get(item_node_id) { - NodeTraitItem(item) => { - match item.node { - TraitItemKind::Method(ref sig, _) => &sig.generics, - _ => return result - } - } + NodeTraitItem(item) => &item.generics, - NodeImplItem(item) => { - match item.node { - ImplItemKind::Method(ref sig, _) => &sig.generics, - _ => return result - } - } + NodeImplItem(item) => &item.generics, NodeItem(item) => { match item.node { @@ -283,7 +274,7 @@ fn type_param_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ItemEnum(_, ref generics) | ItemStruct(_, ref generics) | ItemUnion(_, ref generics) => generics, - ItemTrait(_, ref generics, ..) => { + ItemTrait(_, _, ref generics, ..) => { // Implied `Self: Trait` and supertrait bounds. if param_id == item_node_id { result.predicates.push(ty::TraitRef { @@ -435,7 +426,7 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) { tcx.predicates_of(def_id); convert_enum_variant_types(tcx, def_id, &enum_definition.variants); }, - hir::ItemDefaultImpl(..) => { + hir::ItemAutoImpl(..) => { tcx.impl_trait_ref(def_id); } hir::ItemImpl(..) => { @@ -680,7 +671,7 @@ fn super_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; let (generics, bounds) = match item.node { - hir::ItemTrait(_, ref generics, ref supertraits, _) => (generics, supertraits), + hir::ItemTrait(.., ref generics, ref supertraits, _) => (generics, supertraits), _ => span_bug!(item.span, "super_predicates invoked on non-trait"), }; @@ -723,7 +714,7 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let item = tcx.hir.expect_item(node_id); let unsafety = match item.node { - hir::ItemTrait(unsafety, ..) => unsafety, + hir::ItemTrait(_, unsafety, ..) => unsafety, _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), }; @@ -740,11 +731,14 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } let def_path_hash = tcx.def_path_hash(def_id); - let has_default_impl = tcx.hir.trait_is_auto(def_id); + let is_auto = match item.node { + hir::ItemTrait(hir::IsAuto::Yes, ..) => true, + _ => tcx.hir.trait_is_auto(def_id), + }; let def = ty::TraitDef::new(def_id, unsafety, paren_sugar, - has_default_impl, + is_auto, def_path_hash); tcx.alloc_trait_def(def) } @@ -790,7 +784,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let hir_id = self.tcx.hir.node_to_hir_id(lt.id); match self.tcx.named_region(hir_id) { Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} - Some(rl::Region::LateBound(debruijn, _)) | + Some(rl::Region::LateBound(debruijn, _, _)) | Some(rl::Region::LateBoundAnon(debruijn, _)) if debruijn.depth < self.binder_depth => {} _ => self.has_late_bound_regions = Some(lt.span), @@ -818,12 +812,12 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match node { hir_map::NodeTraitItem(item) => match item.node { hir::TraitItemKind::Method(ref sig, _) => - has_late_bound_regions(tcx, &sig.generics, &sig.decl), + has_late_bound_regions(tcx, &item.generics, &sig.decl), _ => None, }, hir_map::NodeImplItem(item) => match item.node { hir::ImplItemKind::Method(ref sig, _) => - has_late_bound_regions(tcx, &sig.generics, &sig.decl), + has_late_bound_regions(tcx, &item.generics, &sig.decl), _ => None, }, hir_map::NodeForeignItem(item) => match item.node { @@ -861,7 +855,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => { Some(tcx.closure_base_def_id(def_id)) } - NodeTy(&hir::Ty { node: hir::TyImplTrait(..), .. }) => { + NodeTy(&hir::Ty { node: hir::TyImplTraitExistential(..), .. }) => { let mut parent_id = node_id; loop { match tcx.hir.get(parent_id) { @@ -880,35 +874,35 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut allow_defaults = false; let no_generics = hir::Generics::empty(); - let ast_generics = match node { + let (ast_generics, opt_inputs) = match node { NodeTraitItem(item) => { match item.node { - TraitItemKind::Method(ref sig, _) => &sig.generics, - _ => &no_generics + TraitItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)), + _ => (&item.generics, None) } } NodeImplItem(item) => { match item.node { - ImplItemKind::Method(ref sig, _) => &sig.generics, - _ => &no_generics + ImplItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)), + _ => (&item.generics, None) } } NodeItem(item) => { match item.node { - ItemFn(.., ref generics, _) | - ItemImpl(_, _, _, ref generics, ..) => generics, + ItemFn(ref decl, .., ref generics, _) => (generics, Some(&decl.inputs)), + ItemImpl(_, _, _, ref generics, ..) => (generics, None), ItemTy(_, ref generics) | ItemEnum(_, ref generics) | ItemStruct(_, ref generics) | ItemUnion(_, ref generics) => { allow_defaults = true; - generics + (generics, None) } - ItemTrait(_, ref generics, ..) => { + ItemTrait(_, _, ref generics, ..) => { // Add in the self type parameter. // // Something of a hack: use the node id for the trait, also as @@ -922,24 +916,30 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, has_default: false, object_lifetime_default: rl::Set1::Empty, pure_wrt_drop: false, + synthetic: None, }); allow_defaults = true; - generics + (generics, None) } - _ => &no_generics + _ => (&no_generics, None) } } NodeForeignItem(item) => { match item.node { - ForeignItemStatic(..) => &no_generics, - ForeignItemFn(_, _, ref generics) => generics + ForeignItemStatic(..) => (&no_generics, None), + ForeignItemFn(ref decl, _, ref generics) => (generics, Some(&decl.inputs)), + ForeignItemType => (&no_generics, None) } } - _ => &no_generics + NodeTy(&hir::Ty { node: hir::TyImplTraitExistential(ref exist_ty, _), .. }) => { + (&exist_ty.generics, None) + } + + _ => (&no_generics, None) }; let has_self = opt_self.is_some(); @@ -993,22 +993,63 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, object_lifetime_default: object_lifetime_defaults.as_ref().map_or(rl::Set1::Empty, |o| o[i]), pure_wrt_drop: p.pure_wrt_drop, + synthetic: p.synthetic, } }); - let mut types: Vec<_> = opt_self.into_iter().chain(types).collect(); + + let fn_ins = opt_inputs.map(|tys| &tys[..]); + let univ_impl_trait_info = extract_universal_impl_trait_info(tcx, fn_ins); + let other_type_start = type_start + ast_generics.ty_params.len() as u32; + let mut types: Vec<_> = opt_self.into_iter() + .chain(types) + .chain(univ_impl_trait_info.iter().enumerate().map(|(i, info)| { + ty::TypeParameterDef { + index: other_type_start + i as u32, + name: Symbol::intern(&tcx.hir.node_to_pretty_string(info.id)), + def_id: info.def_id, + has_default: false, + object_lifetime_default: rl::Set1::Empty, + pure_wrt_drop: false, + synthetic: Some(SyntheticTyParamKind::ImplTrait), + } + })) + .collect(); // provide junk type parameter defs - the only place that // cares about anything but the length is instantiation, // and we don't do that for closures. if let NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) = node { + // add a dummy parameter for the closure kind + types.push(ty::TypeParameterDef { + index: type_start, + name: Symbol::intern(""), + def_id, + has_default: false, + object_lifetime_default: rl::Set1::Empty, + pure_wrt_drop: false, + synthetic: None, + }); + + // add a dummy parameter for the closure signature + types.push(ty::TypeParameterDef { + index: type_start + 1, + name: Symbol::intern(""), + def_id, + has_default: false, + object_lifetime_default: rl::Set1::Empty, + pure_wrt_drop: false, + synthetic: None, + }); + tcx.with_freevars(node_id, |fv| { - types.extend(fv.iter().enumerate().map(|(i, _)| ty::TypeParameterDef { - index: type_start + i as u32, + types.extend(fv.iter().zip(2..).map(|(_, i)| ty::TypeParameterDef { + index: type_start + i, name: Symbol::intern(""), def_id, has_default: false, object_lifetime_default: rl::Set1::Empty, pure_wrt_drop: false, + synthetic: None, })); }); } @@ -1090,7 +1131,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let substs = Substs::identity_for_item(tcx, def_id); tcx.mk_adt(def, substs) } - ItemDefaultImpl(..) | + ItemAutoImpl(..) | ItemTrait(..) | ItemMod(..) | ItemForeignMod(..) | @@ -1111,7 +1152,8 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let substs = Substs::identity_for_item(tcx, def_id); tcx.mk_fn_def(def_id, substs) } - ForeignItemStatic(ref t, _) => icx.to_ty(t) + ForeignItemStatic(ref t, _) => icx.to_ty(t), + ForeignItemType => tcx.mk_foreign(def_id), } } @@ -1136,14 +1178,19 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, return tcx.typeck_tables_of(def_id).node_id_to_type(hir_id); } - tcx.mk_closure(def_id, Substs::for_item( - tcx, def_id, - |def, _| { - let region = def.to_early_bound_region_data(); - tcx.mk_region(ty::ReEarlyBound(region)) - }, - |def, _| tcx.mk_param_from_def(def) - )) + let substs = ty::ClosureSubsts { + substs: Substs::for_item( + tcx, + def_id, + |def, _| { + let region = def.to_early_bound_region_data(); + tcx.mk_region(ty::ReEarlyBound(region)) + }, + |def, _| tcx.mk_param_from_def(def) + ) + }; + + tcx.mk_closure(def_id, substs) } NodeExpr(_) => match tcx.hir.get(tcx.hir.get_parent_node(node_id)) { @@ -1167,7 +1214,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, icx.to_ty(ty) } - NodeTy(&hir::Ty { node: TyImplTrait(..), .. }) => { + NodeTy(&hir::Ty { node: TyImplTraitExistential(..), .. }) => { let owner = tcx.hir.get_parent_did(node_id); let hir_id = tcx.hir.node_to_hir_id(node_id); tcx.typeck_tables_of(owner).node_id_to_type(hir_id) @@ -1222,7 +1269,14 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => { - tcx.typeck_tables_of(def_id).closure_tys()[hir_id] + let tables = tcx.typeck_tables_of(def_id); + match tables.node_id_to_type(hir_id).sty { + ty::TyClosure(closure_def_id, closure_substs) => { + assert_eq!(def_id, closure_def_id); + return closure_substs.closure_sig(closure_def_id, tcx); + } + ref t => bug!("closure with non-closure type: {:?}", t), + } } x => { @@ -1238,7 +1292,7 @@ fn impl_trait_ref<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); match tcx.hir.expect_item(node_id).node { - hir::ItemDefaultImpl(_, ref ast_trait_ref) => { + hir::ItemAutoImpl(_, ref ast_trait_ref) => { Some(AstConv::instantiate_mono_trait_ref(&icx, ast_trait_ref, tcx.mk_self_type())) @@ -1329,9 +1383,21 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx>( fn predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::GenericPredicates<'tcx> { + let explicit = explicit_predicates_of(tcx, def_id); + ty::GenericPredicates { + parent: explicit.parent, + predicates: [&explicit.predicates[..], &tcx.inferred_outlives_of(def_id)[..]].concat() + } +} + +fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> ty::GenericPredicates<'tcx> { use rustc::hir::map::*; use rustc::hir::*; + debug!("explicit_predicates_of(def_id={:?})", def_id); + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); let node = tcx.hir.get(node_id); @@ -1339,66 +1405,79 @@ fn predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let icx = ItemCtxt::new(tcx, def_id); let no_generics = hir::Generics::empty(); - let ast_generics = match node { + let (ast_generics, opt_inputs) = match node { NodeTraitItem(item) => { match item.node { - TraitItemKind::Method(ref sig, _) => &sig.generics, - _ => &no_generics + TraitItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)), + _ => (&item.generics, None) } } NodeImplItem(item) => { match item.node { - ImplItemKind::Method(ref sig, _) => &sig.generics, - _ => &no_generics + ImplItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)), + _ => (&item.generics, None) } } NodeItem(item) => { match item.node { - ItemFn(.., ref generics, _) | + ItemFn(ref decl, .., ref generics, _) => (generics, Some(&decl.inputs)), + ItemImpl(_, _, _, ref generics, ..) | ItemTy(_, ref generics) | ItemEnum(_, ref generics) | ItemStruct(_, ref generics) | ItemUnion(_, ref generics) => { - generics + (generics, None) } - ItemTrait(_, ref generics, .., ref items) => { + ItemTrait(_, _, ref generics, .., ref items) => { is_trait = Some((ty::TraitRef { def_id, substs: Substs::identity_for_item(tcx, def_id) }, items)); - generics + (generics, None) } - _ => &no_generics + _ => (&no_generics, None) } } NodeForeignItem(item) => { match item.node { - ForeignItemStatic(..) => &no_generics, - ForeignItemFn(_, _, ref generics) => generics + ForeignItemStatic(..) => (&no_generics, None), + ForeignItemFn(ref decl, _, ref generics) => (generics, Some(&decl.inputs)), + ForeignItemType => (&no_generics, None), } } - NodeTy(&Ty { node: TyImplTrait(ref bounds), span, .. }) => { + NodeTy(&Ty { node: TyImplTraitExistential(ref exist_ty, _), span, .. }) => { let substs = Substs::identity_for_item(tcx, def_id); let anon_ty = tcx.mk_anon(def_id, substs); + debug!("explicit_predicates_of: anon_ty={:?}", anon_ty); + // Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`. - let bounds = compute_bounds(&icx, anon_ty, bounds, + let bounds = compute_bounds(&icx, + anon_ty, + &exist_ty.bounds, SizedByDefault::Yes, span); + + debug!("explicit_predicates_of: bounds={:?}", bounds); + + let predicates = bounds.predicates(tcx, anon_ty); + + debug!("explicit_predicates_of: predicates={:?}", predicates); + return ty::GenericPredicates { parent: None, - predicates: bounds.predicates(tcx, anon_ty) + predicates: predicates }; } - _ => &no_generics + _ => (&no_generics, None) }; let generics = tcx.generics_of(def_id); @@ -1529,6 +1608,19 @@ fn predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, })) } + // Add predicates from impl Trait arguments + let fn_ins = opt_inputs.map(|tys| &tys[..]); + let univ_impl_trait_info = extract_universal_impl_trait_info(tcx, fn_ins); + for info in univ_impl_trait_info.iter() { + let name = keywords::Invalid.name(); + let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); + index += 1; + let bounds = compute_bounds(&icx, param_ty, info.bounds, + SizedByDefault::Yes, + info.span); + predicates.extend(bounds.predicates(tcx, param_ty)); + } + // Subtle: before we store the predicates into the tcx, we // sort them so that predicates like `T: Foo` come // before uses of `U`. This avoids false ambiguity errors @@ -1679,13 +1771,64 @@ fn is_foreign_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn is_default_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +fn is_auto_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { match tcx.hir.get_if_local(def_id) { - Some(hir_map::NodeItem(&hir::Item { node: hir::ItemDefaultImpl(..), .. })) + Some(hir_map::NodeItem(&hir::Item { node: hir::ItemAutoImpl(..), .. })) => true, Some(_) => false, - _ => bug!("is_default_impl applied to non-local def-id {:?}", def_id) + _ => bug!("is_auto_impl applied to non-local def-id {:?}", def_id) } } + +struct ImplTraitUniversalInfo<'hir> { + id: ast::NodeId, + def_id: DefId, + span: Span, + bounds: &'hir [hir::TyParamBound], +} + +/// Take some possible list of arguments and return the DefIds of the ImplTraitUniversal +/// arguments +fn extract_universal_impl_trait_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + opt_inputs: Option<&'tcx [P]>) + -> Vec> +{ + // A visitor for simply collecting Universally quantified impl Trait arguments + struct ImplTraitUniversalVisitor<'tcx> { + items: Vec<&'tcx hir::Ty> + } + + impl<'tcx> Visitor<'tcx> for ImplTraitUniversalVisitor<'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &'tcx hir::Ty) { + if let hir::TyImplTraitUniversal(..) = ty.node { + self.items.push(ty); + } + intravisit::walk_ty(self, ty); + } + } + + let mut visitor = ImplTraitUniversalVisitor { items: Vec::new() }; + + if let Some(inputs) = opt_inputs { + for t in inputs.iter() { + visitor.visit_ty(t); + } + } + + visitor.items.into_iter().map(|ty| if let hir::TyImplTraitUniversal(_, ref bounds) = ty.node { + ImplTraitUniversalInfo { + id: ty.id, + def_id: tcx.hir.local_def_id(ty.id), + span: ty.span, + bounds: bounds + } + } else { + span_bug!(ty.span, "this type should be a universally quantified impl trait. this is a bug") + }).collect() +} diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 6bbe2233ff1fa..328b7f9fdefca 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1854,7 +1854,7 @@ unsafe impl !Clone for Foo { } This will compile: -``` +```ignore (ignore auto_trait future compatibility warning) #![feature(optin_builtin_traits)] struct Foo; @@ -2455,9 +2455,9 @@ fn main() { } ``` -Send and Sync are an exception to this rule: it's possible to have bounds of -one non-builtin trait, plus either or both of Send and Sync. For example, the -following compiles correctly: +Auto traits such as Send and Sync are an exception to this rule: +It's possible to have bounds of one non-builtin trait, plus any number of +auto traits. For example, the following compiles correctly: ``` fn main() { @@ -3818,46 +3818,6 @@ let s = Simba { mother: 1, father: 0 }; // ok! ``` "##, -E0562: r##" -Abstract return types (written `impl Trait` for some trait `Trait`) are only -allowed as function return types. - -Erroneous code example: - -```compile_fail,E0562 -#![feature(conservative_impl_trait)] - -fn main() { - let count_to_ten: impl Iterator = 0..10; - // error: `impl Trait` not allowed outside of function and inherent method - // return types - for i in count_to_ten { - println!("{}", i); - } -} -``` - -Make sure `impl Trait` only appears in return-type position. - -``` -#![feature(conservative_impl_trait)] - -fn count_to_n(n: usize) -> impl Iterator { - 0..n -} - -fn main() { - for i in count_to_n(10) { // ok! - println!("{}", i); - } -} -``` - -See [RFC 1522] for more details. - -[RFC 1522]: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md -"##, - E0569: r##" If an impl has a generic parameter with the `#[may_dangle]` attribute, then that impl must be declared as an `unsafe impl. @@ -3986,6 +3946,10 @@ details. "##, E0599: r##" +This error occurs when a method is used on a type which doesn't implement it: + +Erroneous code example: + ```compile_fail,E0599 struct Mouth; @@ -4602,6 +4566,81 @@ foo.method(); // Ok! ``` "##, +E0638: r##" +This error indicates that the struct or enum must be matched non-exhaustively +as it has been marked as `non_exhaustive`. + +When applied within a crate, downstream users of the crate will need to use the +`_` pattern when matching enums and use the `..` pattern when matching structs. + +For example, in the below example, since the enum is marked as +`non_exhaustive`, it is required that downstream crates match non-exhaustively +on it. + +```rust,ignore (pseudo-Rust) +use std::error::Error as StdError; + +#[non_exhaustive] pub enum Error { + Message(String), + Other, +} + +impl StdError for Error { + fn description(&self) -> &str { + // This will not error, despite being marked as non_exhaustive, as this + // enum is defined within the current crate, it can be matched + // exhaustively. + match *self { + Message(ref s) => s, + Other => "other or unknown error", + } + } +} +``` + +An example of matching non-exhaustively on the above enum is provided below: + +```rust,ignore (pseudo-Rust) +use mycrate::Error; + +// This will not error as the non_exhaustive Error enum has been matched with a +// wildcard. +match error { + Message(ref s) => ..., + Other => ..., + _ => ..., +} +``` + +Similarly, for structs, match with `..` to avoid this error. +"##, + +E0639: r##" +This error indicates that the struct or enum cannot be instantiated from +outside of the defining crate as it has been marked as `non_exhaustive` and as +such more fields/variants may be added in future that could cause adverse side +effects for this code. + +It is recommended that you look for a `new` function or equivalent in the +crate's documentation. +"##, + +E0643: r##" +This error indicates that there is a mismatch between generic parameters and +impl Trait parameters in a trait declaration versus its impl. + +```compile_fail,E0643 +#![feature(universal_impl_trait)] +trait Foo { + fn foo(&self, _: &impl Iterator); +} +impl Foo for () { + fn foo(&self, _: &U) { } // error method `foo` has incompatible + // signature for trait +} +``` +"##, + } register_diagnostics! { @@ -4661,11 +4700,12 @@ register_diagnostics! { // E0247, // E0248, // value used as a type, now reported earlier during resolution as E0412 // E0249, + E0307, // invalid method `self` type // E0319, // trait impls for defaulted traits allowed just for structs/enums // E0372, // coherence not object safe E0377, // the trait `CoerceUnsized` may only be implemented for a coercion // between structures with the same definition - E0521, // redundant default implementations of trait + E0521, // redundant auto implementations of trait E0533, // `{}` does not name a unit variant, unit struct or a constant // E0563, // cannot determine a type for this `impl Trait`: {} // removed in 6383de15 E0564, // only named lifetimes are allowed in `impl Trait`, @@ -4676,5 +4716,9 @@ register_diagnostics! { E0588, // packed struct cannot transitively contain a `[repr(align)]` struct E0592, // duplicate definitions with name `{}` // E0613, // Removed (merged with E0609) + E0640, // infer outlives E0627, // yield statement outside of generator literal + E0632, // cannot provide explicit type parameters when `impl Trait` is used in + // argument position. + E0641, // cannot cast to/from a pointer with an unknown kind } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 7a6ee73b9b9e9..bf8f9d8b24a0d 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -50,6 +50,8 @@ independently: - variance: variance inference +- outlives: outlives inference + - check: walks over function bodies and type checks them, inferring types for local variables, type parameters, etc as necessary. @@ -73,7 +75,10 @@ This API is completely unstable and subject to change. #![feature(advanced_slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(crate_visibility_modifier)] #![feature(conservative_impl_trait)] +#![feature(from_ref)] +#![feature(match_default_bindings)] #![feature(never_type)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] @@ -86,7 +91,6 @@ extern crate syntax_pos; extern crate arena; #[macro_use] extern crate rustc; extern crate rustc_platform_intrinsics as intrinsics; -extern crate rustc_back; extern crate rustc_const_math; extern crate rustc_data_structures; extern crate rustc_errors as errors; @@ -122,7 +126,9 @@ mod collect; mod constrained_type_params; mod impl_wf_check; mod coherence; +mod outlives; mod variance; +mod namespace; pub struct TypeAndSubsts<'tcx> { substs: &'tcx Substs<'tcx>, @@ -285,6 +291,7 @@ pub fn provide(providers: &mut Providers) { coherence::provide(providers); check::provide(providers); variance::provide(providers); + outlives::provide(providers); } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) @@ -300,6 +307,11 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) })?; + tcx.sess.track_errors(|| { + time(time_passes, "outlives testing", || + outlives::test::test_inferred_outlives(tcx)); + })?; + tcx.sess.track_errors(|| { time(time_passes, "impl wf inference", || impl_wf_check::impl_wf_check(tcx)); diff --git a/src/librustc_typeck/namespace.rs b/src/librustc_typeck/namespace.rs new file mode 100644 index 0000000000000..6f0e46b3afee1 --- /dev/null +++ b/src/librustc_typeck/namespace.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::ty; + +// Whether an item exists in the type or value namespace. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Namespace { + Type, + Value, +} + +impl From for Namespace { + fn from(a_kind: ty::AssociatedKind) -> Self { + match a_kind { + ty::AssociatedKind::Type => Namespace::Type, + ty::AssociatedKind::Const | + ty::AssociatedKind::Method => Namespace::Value, + } + } +} + +impl<'a> From <&'a hir::ImplItemKind> for Namespace { + fn from(impl_kind: &'a hir::ImplItemKind) -> Self { + match *impl_kind { + hir::ImplItemKind::Type(..) => Namespace::Type, + hir::ImplItemKind::Const(..) | + hir::ImplItemKind::Method(..) => Namespace::Value, + } + } +} diff --git a/src/librustc_typeck/outlives/mod.rs b/src/librustc_typeck/outlives/mod.rs new file mode 100644 index 0000000000000..1127028cbc8c7 --- /dev/null +++ b/src/librustc_typeck/outlives/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir::def_id::DefId; +use rustc::ty::{self, TyCtxt}; +use rustc::ty::maps::Providers; + +/// Code to write unit test for outlives. +pub mod test; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + inferred_outlives_of, + ..*providers + }; +} + +//todo +fn inferred_outlives_of<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, _def_id: DefId) + -> Vec> { + Vec::new() +} diff --git a/src/librustc_typeck/outlives/test.rs b/src/librustc_typeck/outlives/test.rs new file mode 100644 index 0000000000000..196e660549463 --- /dev/null +++ b/src/librustc_typeck/outlives/test.rs @@ -0,0 +1,41 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::ty::TyCtxt; + +pub fn test_inferred_outlives<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + tcx.hir.krate().visit_all_item_likes(&mut OutlivesTest { tcx }); +} + +struct OutlivesTest<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx> +} + +impl<'a, 'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + let item_def_id = self.tcx.hir.local_def_id(item.id); + + // For unit testing: check for a special "rustc_outlives" + // attribute and report an error with various results if found. + if self.tcx.has_attr(item_def_id, "rustc_outlives") { + let inferred_outlives_of = self.tcx.inferred_outlives_of(item_def_id); + span_err!(self.tcx.sess, + item.span, + E0640, + "{:?}", + inferred_outlives_of); + } + } + + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { } + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { } +} diff --git a/src/librustc_typeck/variance/README.md b/src/librustc_typeck/variance/README.md index 592916178897c..64d3389b34af7 100644 --- a/src/librustc_typeck/variance/README.md +++ b/src/librustc_typeck/variance/README.md @@ -104,22 +104,16 @@ into two queries: - `crate_variances` computes the variance for all items in the current crate. - `variances_of` accesses the variance for an individual reading; it works by requesting `crate_variances` and extracting the relevant data. - + If you limit yourself to reading `variances_of`, your code will only depend then on the inference inferred for that particular item. -Eventually, the goal is to rely on the red-green dependency management -algorithm. At the moment, however, we rely instead on a hack, where -`variances_of` ignores the dependencies of accessing -`crate_variances` and instead computes the *correct* dependencies -itself. To this end, when we build up the constraints in the system, -we also built up a transitive `dependencies` relation as part of the -crate map. A `(X, Y)` pair is added to the map each time we have a -constraint that the variance of some inferred for the item `X` depends -on the variance of some element of `Y`. This is to some extent a -mirroring of the inference graph in the dependency graph. This means -we can just completely ignore the fixed-point iteration, since it is -just shuffling values along this graph. +Ultimately, this setup relies on the red-green algorithm. +In particular, every variance query ultimately depends on -- effectively -- +all type definitions in the entire crate (through `crate_variances`), +but since most changes will not result in a change +to the actual results from variance inference, +the `variances_of` query will wind up being considered green after it is re-evaluated. ### Addendum: Variance on traits diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 29da763f334c7..ef6552c8e33f4 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -14,7 +14,7 @@ //! We walk the set of items and, for each member, generate new constraints. use hir::def_id::DefId; -use rustc::dep_graph::{DepGraphSafe, DepKind}; +use rustc::dep_graph::{DepGraphSafe, DepKind, DepNodeColor}; use rustc::ich::StableHashingContext; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; @@ -22,7 +22,6 @@ use syntax::ast; use rustc::hir; use rustc::hir::itemlikevisit::ItemLikeVisitor; -use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_data_structures::stable_hasher::StableHashingContextProvider; use super::terms::*; @@ -38,11 +37,6 @@ pub struct ConstraintContext<'a, 'tcx: 'a> { bivariant: VarianceTermPtr<'a>, pub constraints: Vec>, - - /// This relation tracks the dependencies between the variance of - /// various items. In particular, if `a < b`, then the variance of - /// `a` depends on the sources of `b`. - pub dependencies: TransitiveRelation, } /// Declares that the variable `decl_id` appears in a location with @@ -63,7 +57,6 @@ pub struct Constraint<'a> { /// then while we are visiting `Bar`, the `CurrentItem` would have /// the def-id and the start of `Foo`'s inferreds. pub struct CurrentItem { - def_id: DefId, inferred_start: InferredIndex, } @@ -81,7 +74,6 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>) invariant, bivariant, constraints: Vec::new(), - dependencies: TransitiveRelation::new(), }; tcx.hir.krate().visit_all_item_likes(&mut constraint_cx); @@ -162,10 +154,22 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // See README.md for a detailed discussion // on dep-graph management. let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints); - tcx.dep_graph.with_task(dep_node, - self, - def_id, - visit_item_task); + + if let Some(DepNodeColor::Green(_)) = tcx.dep_graph.node_color(&dep_node) { + // If the corresponding node has already been marked as green, the + // appropriate portion of the DepGraph has already been loaded from + // the previous graph, so we don't do any dep-tracking. Since we + // don't cache any values though, we still have to re-run the + // computation. + tcx.dep_graph.with_ignore(|| { + self.build_constraints_for_item(def_id); + }); + } else { + tcx.dep_graph.with_task(dep_node, + self, + def_id, + visit_item_task); + } fn visit_item_task<'a, 'tcx>(ccx: &mut ConstraintContext<'a, 'tcx>, def_id: DefId) @@ -189,7 +193,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let id = tcx.hir.as_local_node_id(def_id).unwrap(); let inferred_start = self.terms_cx.inferred_starts[&id]; - let current_item = &CurrentItem { def_id, inferred_start }; + let current_item = &CurrentItem { inferred_start }; match tcx.type_of(def_id).sty { ty::TyAdt(def, _) => { // Not entirely obvious: constraints on structs/enums do not @@ -301,7 +305,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { match ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | - ty::TyStr | ty::TyNever => { + ty::TyStr | ty::TyNever | ty::TyForeign(..) => { // leaf type -- noop } @@ -398,12 +402,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { return; } - // Add a corresponding relation into the dependencies to - // indicate that the variance for `current` relies on `def_id`. - if self.tcx().dep_graph.is_fully_enabled() { - self.dependencies.add(current.def_id, def_id); - } - let (local, remote) = if let Some(id) = self.tcx().hir.as_local_node_id(def_id) { (Some(self.terms_cx.inferred_starts[&id]), None) } else { diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs index 7a9f35545e2f3..418d2b9467096 100644 --- a/src/librustc_typeck/variance/mod.rs +++ b/src/librustc_typeck/variance/mod.rs @@ -94,20 +94,9 @@ fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId) // Everything else must be inferred. - // Lacking red/green, we read the variances for all items here - // but ignore the dependencies, then re-synthesize the ones we need. - let crate_map = tcx.dep_graph.with_ignore(|| tcx.crate_variances(LOCAL_CRATE)); + let crate_map = tcx.crate_variances(LOCAL_CRATE); let dep_node = item_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints); tcx.dep_graph.read(dep_node); - for &dep_def_id in crate_map.dependencies.less_than(&item_def_id) { - if dep_def_id.is_local() { - let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints); - tcx.dep_graph.read(dep_node); - } else { - let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVariances); - tcx.dep_graph.read(dep_node); - } - } crate_map.variances.get(&item_def_id) .unwrap_or(&crate_map.empty_variance) diff --git a/src/librustc_typeck/variance/solve.rs b/src/librustc_typeck/variance/solve.rs index 495eb95419a90..434e8ce148f3b 100644 --- a/src/librustc_typeck/variance/solve.rs +++ b/src/librustc_typeck/variance/solve.rs @@ -34,7 +34,7 @@ struct SolveContext<'a, 'tcx: 'a> { } pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap { - let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx; + let ConstraintContext { terms_cx, constraints, .. } = constraints_cx; let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()]; for &(id, ref variances) in &terms_cx.lang_items { @@ -53,7 +53,7 @@ pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariance let variances = solutions_cx.create_map(); let empty_variance = Rc::new(Vec::new()); - ty::CrateVariancesMap { dependencies, variances, empty_variance } + ty::CrateVariancesMap { variances, empty_variance } } impl<'a, 'tcx> SolveContext<'a, 'tcx> { diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index b295b414a035b..fd8a6e0b59326 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -11,11 +11,11 @@ path = "lib.rs" doctest = false [dependencies] -env_logger = { version = "0.4", default-features = false } log = "0.3" -pulldown-cmark = { version = "0.0.14", default-features = false } -html-diff = "0.0.4" +pulldown-cmark = { version = "0.1.0", default-features = false } +html-diff = "0.0.5" +tempdir = "0.3" [build-dependencies] build_helper = { path = "../build_helper" } -cc = "1.0" +cc = "1.0.1" diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 97c9ca1e2d27c..276825bd31a75 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -27,6 +27,6 @@ fn main() { .warnings(false) .include(src_dir) .warnings(false) - .compile("libhoedown.a"); + .compile("hoedown"); } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index da8c3a5cf206b..5eb3e38d5b371 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -15,7 +15,6 @@ use std::mem; use std::fmt::{self, Write}; use std::ops; -use std::ascii::AsciiExt; use syntax::symbol::Symbol; use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind}; @@ -337,7 +336,6 @@ impl<'a> fmt::Display for Html<'a> { "l4re" => "L4Re", "linux" => "Linux", "macos" => "macOS", - "nacl" => "NaCl", "netbsd" => "NetBSD", "openbsd" => "OpenBSD", "redox" => "Redox", @@ -886,4 +884,4 @@ mod test { only." ); } -} \ No newline at end of file +} diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 3a4dcc3217388..85c1796ecef39 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -77,6 +77,11 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name) ret.extend(build_impls(cx, did)); clean::EnumItem(build_enum(cx, did)) } + Def::TyForeign(did) => { + record_extern_fqn(cx, did, clean::TypeKind::Foreign); + ret.extend(build_impls(cx, did)); + clean::ForeignTypeItem + } // Never inline enum variants but leave them shown as reexports. Def::Variant(..) => return None, // Assume that enum variants and struct types are reexported next to @@ -140,11 +145,13 @@ pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait { let generics = (cx.tcx.generics_of(did), &predicates).clean(cx); let generics = filter_non_trait_generics(did, generics); let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); + let is_spotlight = load_attrs(cx, did).has_doc_flag("spotlight"); clean::Trait { unsafety: cx.tcx.trait_def(did).unsafety, generics, items: trait_items, bounds: supertrait_bounds, + is_spotlight, } } @@ -292,10 +299,10 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { } } - // If this is a defaulted impl, then bail out early here - if tcx.is_default_impl(did) { + // If this is an auto impl, then bail out early here + if tcx.is_auto_impl(did) { return ret.push(clean::Item { - inner: clean::DefaultImplItem(clean::DefaultImpl { + inner: clean::AutoImplItem(clean::AutoImpl { // FIXME: this should be decoded unsafety: hir::Unsafety::Normal, trait_: match associated_trait.as_ref().unwrap().clean(cx) { @@ -325,74 +332,10 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { let predicates = tcx.predicates_of(did); let trait_items = tcx.associated_items(did).filter_map(|item| { - match item.kind { - ty::AssociatedKind::Const => { - let default = if item.defaultness.has_value() { - Some(print_inlined_const(cx, item.def_id)) - } else { - None - }; - Some(clean::Item { - name: Some(item.name.clean(cx)), - inner: clean::AssociatedConstItem( - tcx.type_of(item.def_id).clean(cx), - default, - ), - source: tcx.def_span(item.def_id).clean(cx), - attrs: clean::Attributes::default(), - visibility: None, - stability: tcx.lookup_stability(item.def_id).clean(cx), - deprecation: tcx.lookup_deprecation(item.def_id).clean(cx), - def_id: item.def_id - }) - } - ty::AssociatedKind::Method => { - if item.vis != ty::Visibility::Public && associated_trait.is_none() { - return None - } - let mut cleaned = item.clean(cx); - cleaned.inner = match cleaned.inner.clone() { - clean::TyMethodItem(clean::TyMethod { - unsafety, decl, generics, abi - }) => { - let constness = if tcx.is_const_fn(item.def_id) { - hir::Constness::Const - } else { - hir::Constness::NotConst - }; - - clean::MethodItem(clean::Method { - unsafety, - constness, - decl, - generics, - abi, - }) - } - ref r => panic!("not a tymethod: {:?}", r), - }; - Some(cleaned) - } - ty::AssociatedKind::Type => { - let typedef = clean::Typedef { - type_: tcx.type_of(item.def_id).clean(cx), - generics: clean::Generics { - lifetimes: vec![], - type_params: vec![], - where_predicates: vec![] - } - }; - Some(clean::Item { - name: Some(item.name.clean(cx)), - inner: clean::TypedefItem(typedef, true), - source: tcx.def_span(item.def_id).clean(cx), - attrs: clean::Attributes::default(), - visibility: None, - stability: tcx.lookup_stability(item.def_id).clean(cx), - deprecation: tcx.lookup_deprecation(item.def_id).clean(cx), - def_id: item.def_id - }) - } + if associated_trait.is_some() || item.vis == ty::Visibility::Public { + Some(item.clean(cx)) + } else { + None } }).collect::>(); let polarity = tcx.impl_polarity(did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c9afa3646b2da..be7bd3d5510ef 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -44,6 +44,7 @@ use rustc::hir; use rustc_const_math::ConstInt; use std::{mem, slice, vec}; +use std::iter::FromIterator; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -112,6 +113,7 @@ impl, U> Clean> for P<[T]> { #[derive(Clone, Debug)] pub struct Crate { pub name: String, + pub version: Option, pub src: PathBuf, pub module: Option, pub externs: Vec<(CrateNum, ExternalCrate)>, @@ -150,7 +152,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { match module.inner { ModuleItem(ref module) => { for it in &module.items { - if it.is_extern_crate() && it.attrs.has_doc_masked() { + if it.is_extern_crate() && it.attrs.has_doc_flag("masked") { masked_crates.insert(it.def_id.krate); } } @@ -183,6 +185,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { Crate { name, + version: None, src, module: Some(module), externs, @@ -236,6 +239,7 @@ impl Clean for CrateNum { if prim.is_some() { break; } + // FIXME: should warn on unknown primitives? } } } @@ -297,6 +301,11 @@ impl Item { pub fn doc_value<'a>(&'a self) -> Option<&'a str> { self.attrs.doc_value() } + /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined + /// with newlines. + pub fn collapsed_doc_value(&self) -> Option { + self.attrs.collapsed_doc_value() + } pub fn is_crate(&self) -> bool { match self.inner { StrippedItem(box ModuleItem(Module { is_crate: true, ..})) | @@ -416,11 +425,13 @@ pub enum ItemEnum { ForeignFunctionItem(Function), /// `static`s from an extern block ForeignStaticItem(Static), + /// `type`s from an extern block + ForeignTypeItem, MacroItem(Macro), PrimitiveItem(PrimitiveType), AssociatedConstItem(Type, Option), AssociatedTypeItem(Vec, Option), - DefaultImplItem(DefaultImpl), + AutoImplItem(AutoImpl), /// An item that has been stripped by a rustdoc pass StrippedItem(Box), } @@ -559,9 +570,69 @@ impl> NestedAttributesExt for I { } } +/// A portion of documentation, extracted from a `#[doc]` attribute. +/// +/// Each variant contains the line number within the complete doc-comment where the fragment +/// starts, as well as the Span where the corresponding doc comment or attribute is located. +/// +/// Included files are kept separate from inline doc comments so that proper line-number +/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are +/// kept separate because of issue #42760. +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)] +pub enum DocFragment { + // FIXME #44229 (misdreavus): sugared and raw doc comments can be brought back together once + // hoedown is completely removed from rustdoc. + /// A doc fragment created from a `///` or `//!` doc comment. + SugaredDoc(usize, syntax_pos::Span, String), + /// A doc fragment created from a "raw" `#[doc=""]` attribute. + RawDoc(usize, syntax_pos::Span, String), + /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the + /// given filename and the file contents. + Include(usize, syntax_pos::Span, String, String), +} + +impl DocFragment { + pub fn as_str(&self) -> &str { + match *self { + DocFragment::SugaredDoc(_, _, ref s) => &s[..], + DocFragment::RawDoc(_, _, ref s) => &s[..], + DocFragment::Include(_, _, _, ref s) => &s[..], + } + } + + pub fn span(&self) -> syntax_pos::Span { + match *self { + DocFragment::SugaredDoc(_, span, _) | + DocFragment::RawDoc(_, span, _) | + DocFragment::Include(_, span, _, _) => span, + } + } +} + +impl<'a> FromIterator<&'a DocFragment> for String { + fn from_iter(iter: T) -> Self + where + T: IntoIterator + { + iter.into_iter().fold(String::new(), |mut acc, frag| { + if !acc.is_empty() { + acc.push('\n'); + } + match *frag { + DocFragment::SugaredDoc(_, _, ref docs) + | DocFragment::RawDoc(_, _, ref docs) + | DocFragment::Include(_, _, _, ref docs) => + acc.push_str(docs), + } + + acc + }) + } +} + #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Default)] pub struct Attributes { - pub doc_strings: Vec, + pub doc_strings: Vec, pub other_attrs: Vec, pub cfg: Option>, pub span: Option, @@ -591,12 +662,53 @@ impl Attributes { None } - pub fn has_doc_masked(&self) -> bool { + /// Reads a `MetaItem` from within an attribute, looks for whether it is a + /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from + /// its expansion. + fn extract_include(mi: &ast::MetaItem) + -> Option<(String, String)> + { + mi.meta_item_list().and_then(|list| { + for meta in list { + if meta.check_name("include") { + // the actual compiled `#[doc(include="filename")]` gets expanded to + // `#[doc(include(file="filename", contents="file contents")]` so we need to + // look for that instead + return meta.meta_item_list().and_then(|list| { + let mut filename: Option = None; + let mut contents: Option = None; + + for it in list { + if it.check_name("file") { + if let Some(name) = it.value_str() { + filename = Some(name.to_string()); + } + } else if it.check_name("contents") { + if let Some(docs) = it.value_str() { + contents = Some(docs.to_string()); + } + } + } + + if let (Some(filename), Some(contents)) = (filename, contents) { + Some((filename, contents)) + } else { + None + } + }); + } + } + + None + }) + } + + pub fn has_doc_flag(&self, flag: &str) -> bool { for attr in &self.other_attrs { if !attr.check_name("doc") { continue; } if let Some(items) = attr.meta_item_list() { - if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name("masked")) { + if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name(flag)) { return true; } } @@ -605,10 +717,12 @@ impl Attributes { false } - pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes { + pub fn from_ast(diagnostic: &::errors::Handler, + attrs: &[ast::Attribute]) -> Attributes { let mut doc_strings = vec![]; let mut sp = None; let mut cfg = Cfg::True; + let mut doc_line = 0; let other_attrs = attrs.iter().filter_map(|attr| { attr.with_desugared_doc(|attr| { @@ -616,7 +730,16 @@ impl Attributes { if let Some(mi) = attr.meta() { if let Some(value) = mi.value_str() { // Extracted #[doc = "..."] - doc_strings.push(value.to_string()); + let value = value.to_string(); + let line = doc_line; + doc_line += value.lines().count(); + + if attr.is_sugared_doc { + doc_strings.push(DocFragment::SugaredDoc(line, attr.span, value)); + } else { + doc_strings.push(DocFragment::RawDoc(line, attr.span, value)); + } + if sp.is_none() { sp = Some(attr.span); } @@ -628,6 +751,14 @@ impl Attributes { Err(e) => diagnostic.span_err(e.span, e.msg), } return None; + } else if let Some((filename, contents)) = Attributes::extract_include(&mi) + { + let line = doc_line; + doc_line += contents.lines().count(); + doc_strings.push(DocFragment::Include(line, + attr.span, + filename, + contents)); } } } @@ -645,7 +776,17 @@ impl Attributes { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. pub fn doc_value<'a>(&'a self) -> Option<&'a str> { - self.doc_strings.first().map(|s| &s[..]) + self.doc_strings.first().map(|s| s.as_str()) + } + + /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined + /// with newlines. + pub fn collapsed_doc_value(&self) -> Option { + if !self.doc_strings.is_empty() { + Some(self.doc_strings.iter().collect()) + } else { + None + } } } @@ -867,8 +1008,8 @@ impl Clean for hir::Lifetime { let hir_id = cx.tcx.hir.node_to_hir_id(self.id); let def = cx.tcx.named_region(hir_id); match def { - Some(rl::Region::EarlyBound(_, node_id)) | - Some(rl::Region::LateBound(_, node_id)) | + Some(rl::Region::EarlyBound(_, node_id, _)) | + Some(rl::Region::LateBound(_, node_id, _)) | Some(rl::Region::Free(_, node_id)) => { if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { return lt; @@ -1138,13 +1279,13 @@ pub struct Method { pub abi: Abi, } -impl<'a> Clean for (&'a hir::MethodSig, hir::BodyId) { +impl<'a> Clean for (&'a hir::MethodSig, &'a hir::Generics, hir::BodyId) { fn clean(&self, cx: &DocContext) -> Method { Method { - generics: self.0.generics.clean(cx), + generics: self.1.clean(cx), unsafety: self.0.unsafety, constness: self.0.constness, - decl: (&*self.0.decl, self.1).clean(cx), + decl: (&*self.0.decl, self.2).clean(cx), abi: self.0.abi } } @@ -1326,19 +1467,31 @@ impl Clean for hir::FunctionRetTy { } } +impl GetDefId for FunctionRetTy { + fn def_id(&self) -> Option { + match *self { + Return(ref ty) => ty.def_id(), + DefaultReturn => None, + } + } +} + #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Trait { pub unsafety: hir::Unsafety, pub items: Vec, pub generics: Generics, pub bounds: Vec, + pub is_spotlight: bool, } impl Clean for doctree::Trait { fn clean(&self, cx: &DocContext) -> Item { + let attrs = self.attrs.clean(cx); + let is_spotlight = attrs.has_doc_flag("spotlight"); Item { name: Some(self.name.clean(cx)), - attrs: self.attrs.clean(cx), + attrs: attrs, source: self.whence.clean(cx), def_id: cx.tcx.hir.local_def_id(self.id), visibility: self.vis.clean(cx), @@ -1349,6 +1502,7 @@ impl Clean for doctree::Trait { items: self.items.clean(cx), generics: self.generics.clean(cx), bounds: self.bounds.clean(cx), + is_spotlight: is_spotlight, }), } } @@ -1377,13 +1531,13 @@ impl Clean for hir::TraitItem { default.map(|e| print_const_expr(cx, e))) } hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => { - MethodItem((sig, body).clean(cx)) + MethodItem((sig, &self.generics, body).clean(cx)) } hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(ref names)) => { TyMethodItem(TyMethod { unsafety: sig.unsafety.clone(), decl: (&*sig.decl, &names[..]).clean(cx), - generics: sig.generics.clean(cx), + generics: self.generics.clean(cx), abi: sig.abi }) } @@ -1412,7 +1566,7 @@ impl Clean for hir::ImplItem { Some(print_const_expr(cx, expr))) } hir::ImplItemKind::Method(ref sig, body) => { - MethodItem((sig, body).clean(cx)) + MethodItem((sig, &self.generics, body).clean(cx)) } hir::ImplItemKind::Type(ref ty) => TypedefItem(Typedef { type_: ty.clean(cx), @@ -1441,7 +1595,12 @@ impl<'tcx> Clean for ty::AssociatedItem { let inner = match self.kind { ty::AssociatedKind::Const => { let ty = cx.tcx.type_of(self.def_id); - AssociatedConstItem(ty.clean(cx), None) + let default = if self.defaultness.has_value() { + Some(inline::print_inlined_const(cx, self.def_id)) + } else { + None + }; + AssociatedConstItem(ty.clean(cx), default) } ty::AssociatedKind::Method => { let generics = (cx.tcx.generics_of(self.def_id), @@ -1472,18 +1631,21 @@ impl<'tcx> Clean for ty::AssociatedItem { } let provided = match self.container { - ty::ImplContainer(_) => false, + ty::ImplContainer(_) => true, ty::TraitContainer(_) => self.defaultness.has_value() }; if provided { + let constness = if cx.tcx.is_const_fn(self.def_id) { + hir::Constness::Const + } else { + hir::Constness::NotConst + }; MethodItem(Method { unsafety: sig.unsafety(), generics, decl, abi: sig.abi(), - - // trait methods cannot (currently, at least) be const - constness: hir::Constness::NotConst, + constness, }) } else { TyMethodItem(TyMethod { @@ -1497,14 +1659,14 @@ impl<'tcx> Clean for ty::AssociatedItem { ty::AssociatedKind::Type => { let my_name = self.name.clean(cx); - let mut bounds = if let ty::TraitContainer(did) = self.container { + if let ty::TraitContainer(did) = self.container { // When loading a cross-crate associated type, the bounds for this type // are actually located on the trait/impl itself, so we need to load // all of the generics from there and then look for bounds that are // applied to this associated type in question. let predicates = cx.tcx.predicates_of(did); let generics = (cx.tcx.generics_of(did), &predicates).clean(cx); - generics.where_predicates.iter().filter_map(|pred| { + let mut bounds = generics.where_predicates.iter().filter_map(|pred| { let (name, self_type, trait_, bounds) = match *pred { WherePredicate::BoundPredicate { ty: QPath { ref name, ref self_type, ref trait_ }, @@ -1522,34 +1684,45 @@ impl<'tcx> Clean for ty::AssociatedItem { _ => return None, } Some(bounds) - }).flat_map(|i| i.iter().cloned()).collect::>() - } else { - vec![] - }; + }).flat_map(|i| i.iter().cloned()).collect::>(); + // Our Sized/?Sized bound didn't get handled when creating the generics + // because we didn't actually get our whole set of bounds until just now + // (some of them may have come from the trait). If we do have a sized + // bound, we remove it, and if we don't then we add the `?Sized` bound + // at the end. + match bounds.iter().position(|b| b.is_sized_bound(cx)) { + Some(i) => { bounds.remove(i); } + None => bounds.push(TyParamBound::maybe_sized(cx)), + } - // Our Sized/?Sized bound didn't get handled when creating the generics - // because we didn't actually get our whole set of bounds until just now - // (some of them may have come from the trait). If we do have a sized - // bound, we remove it, and if we don't then we add the `?Sized` bound - // at the end. - match bounds.iter().position(|b| b.is_sized_bound(cx)) { - Some(i) => { bounds.remove(i); } - None => bounds.push(TyParamBound::maybe_sized(cx)), - } + let ty = if self.defaultness.has_value() { + Some(cx.tcx.type_of(self.def_id)) + } else { + None + }; - let ty = if self.defaultness.has_value() { - Some(cx.tcx.type_of(self.def_id)) + AssociatedTypeItem(bounds, ty.clean(cx)) } else { - None - }; - - AssociatedTypeItem(bounds, ty.clean(cx)) + TypedefItem(Typedef { + type_: cx.tcx.type_of(self.def_id).clean(cx), + generics: Generics { + lifetimes: Vec::new(), + type_params: Vec::new(), + where_predicates: Vec::new(), + }, + }, true) + } } }; + let visibility = match self.container { + ty::ImplContainer(_) => self.vis.clean(cx), + ty::TraitContainer(_) => None, + }; + Item { name: Some(self.name.clean(cx)), - visibility: Some(Inherited), + visibility, stability: get_stability(cx, self.def_id), deprecation: get_deprecation(cx, self.def_id), def_id: self.def_id, @@ -1625,6 +1798,7 @@ pub enum PrimitiveType { Slice, Array, Tuple, + Unit, RawPointer, Reference, Fn, @@ -1642,6 +1816,7 @@ pub enum TypeKind { Trait, Variant, Typedef, + Foreign, } pub trait GetDefId { @@ -1660,7 +1835,11 @@ impl Type { Primitive(p) | BorrowedRef { type_: box Primitive(p), ..} => Some(p), Slice(..) | BorrowedRef { type_: box Slice(..), .. } => Some(PrimitiveType::Slice), Array(..) | BorrowedRef { type_: box Array(..), .. } => Some(PrimitiveType::Array), - Tuple(..) => Some(PrimitiveType::Tuple), + Tuple(ref tys) => if tys.is_empty() { + Some(PrimitiveType::Unit) + } else { + Some(PrimitiveType::Tuple) + }, RawPointer(..) => Some(PrimitiveType::RawPointer), BorrowedRef { type_: box Generic(..), .. } => Some(PrimitiveType::Reference), BareFunction(..) => Some(PrimitiveType::Fn), @@ -1681,6 +1860,21 @@ impl Type { _ => false } } + + pub fn generics(&self) -> Option<&[Type]> { + match *self { + ResolvedPath { ref path, .. } => { + path.segments.last().and_then(|seg| { + if let PathParameters::AngleBracketed { ref types, .. } = seg.params { + Some(&**types) + } else { + None + } + }) + } + _ => None, + } + } } impl GetDefId for Type { @@ -1691,7 +1885,11 @@ impl GetDefId for Type { BorrowedRef { type_: box Generic(..), .. } => Primitive(PrimitiveType::Reference).def_id(), BorrowedRef { ref type_, .. } => type_.def_id(), - Tuple(..) => Primitive(PrimitiveType::Tuple).def_id(), + Tuple(ref tys) => if tys.is_empty() { + Primitive(PrimitiveType::Unit).def_id() + } else { + Primitive(PrimitiveType::Tuple).def_id() + }, BareFunction(..) => Primitive(PrimitiveType::Fn).def_id(), Slice(..) => Primitive(PrimitiveType::Slice).def_id(), Array(..) => Primitive(PrimitiveType::Array).def_id(), @@ -1725,6 +1923,7 @@ impl PrimitiveType { "array" => Some(PrimitiveType::Array), "slice" => Some(PrimitiveType::Slice), "tuple" => Some(PrimitiveType::Tuple), + "unit" => Some(PrimitiveType::Unit), "pointer" => Some(PrimitiveType::RawPointer), "reference" => Some(PrimitiveType::Reference), "fn" => Some(PrimitiveType::Fn), @@ -1755,6 +1954,7 @@ impl PrimitiveType { Array => "array", Slice => "slice", Tuple => "tuple", + Unit => "unit", RawPointer => "pointer", Reference => "reference", Fn => "fn", @@ -1928,7 +2128,8 @@ impl Clean for hir::Ty { } } TyBareFn(ref barefn) => BareFunction(box barefn.clean(cx)), - TyImplTrait(ref bounds) => ImplTrait(bounds.clean(cx)), + TyImplTraitExistential(ref exist_ty, ref _lts) => ImplTrait(exist_ty.bounds.clean(cx)), + TyImplTraitUniversal(_, ref bounds) => ImplTrait(bounds.clean(cx)), TyInfer | TyErr => Infer, TyTypeof(..) => panic!("Unimplemented type {:?}", self.node), } @@ -1998,6 +2199,17 @@ impl<'tcx> Clean for Ty<'tcx> { is_generic: false, } } + ty::TyForeign(did) => { + inline::record_extern_fqn(cx, did, TypeKind::Foreign); + let path = external_path(cx, &cx.tcx.item_name(did), + None, false, vec![], Substs::empty()); + ResolvedPath { + path: path, + typarams: None, + did: did, + is_generic: false, + } + } ty::TyDynamic(ref obj, ref reg) => { if let Some(principal) = obj.principal() { let did = principal.def_id(); @@ -2491,7 +2703,7 @@ impl Clean for hir::BareFnTy { type_params: Vec::new(), where_predicates: Vec::new() }, - decl: (&*self.decl, &[][..]).clean(cx), + decl: (&*self.decl, &self.arg_names[..]).clean(cx), abi: self.abi, } } @@ -2676,6 +2888,7 @@ fn build_deref_target_impls(cx: &DocContext, Slice => tcx.lang_items().slice_impl(), Array => tcx.lang_items().slice_impl(), Tuple => None, + Unit => None, RawPointer => tcx.lang_items().const_ptr_impl(), Reference => None, Fn => None, @@ -2689,12 +2902,12 @@ fn build_deref_target_impls(cx: &DocContext, } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub struct DefaultImpl { +pub struct AutoImpl { pub unsafety: hir::Unsafety, pub trait_: Type, } -impl Clean for doctree::DefaultImpl { +impl Clean for doctree::AutoImpl { fn clean(&self, cx: &DocContext) -> Item { Item { name: None, @@ -2704,7 +2917,7 @@ impl Clean for doctree::DefaultImpl { visibility: Some(Public), stability: None, deprecation: None, - inner: DefaultImplItem(DefaultImpl { + inner: AutoImplItem(AutoImpl { unsafety: self.unsafety, trait_: self.trait_.clean(cx), }), @@ -2810,6 +3023,9 @@ impl Clean for hir::ForeignItem { expr: "".to_string(), }) } + hir::ForeignItemType => { + ForeignTypeItem + } }; Item { name: Some(self.name.clean(cx)), @@ -2921,6 +3137,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId { Def::Struct(i) => (i, TypeKind::Struct), Def::Union(i) => (i, TypeKind::Union), Def::Mod(i) => (i, TypeKind::Module), + Def::TyForeign(i) => (i, TypeKind::Foreign), Def::Static(i, _) => (i, TypeKind::Static), Def::Variant(i) => (cx.tcx.parent_def_id(i).unwrap(), TypeKind::Enum), Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 2ecb7b546fce2..456a00947ae0a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,7 +10,6 @@ use rustc_lint; use rustc_driver::{driver, target_features, abort_on_err}; -use rustc_driver::pretty::ReplaceBodyWithLoop; use rustc::session::{self, config}; use rustc::hir::def_id::DefId; use rustc::hir::def::Def; @@ -26,7 +25,6 @@ use rustc_metadata::cstore::CStore; use syntax::codemap; use syntax::feature_gate::UnstableFeatures; -use syntax::fold::Folder; use errors; use errors::emitter::ColorConfig; @@ -154,10 +152,9 @@ pub fn run_core(search_paths: SearchPaths, target_features::add_configuration(&mut cfg, &sess); sess.parse_sess.config = cfg; - let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), - &sess, - &input)); - let krate = ReplaceBodyWithLoop::new().fold_crate(krate); + let control = &driver::CompileController::basic(); + + let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input)); let name = link::find_crate_name(Some(&sess), &krate.attrs, &input); @@ -182,7 +179,8 @@ pub fn run_core(search_paths: SearchPaths, &[], &sess); - abort_on_err(driver::phase_3_run_analysis_passes(&sess, + abort_on_err(driver::phase_3_run_analysis_passes(control, + &sess, &*cstore, hir_map, analysis, diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 71594825cdb01..c21bfd8842f72 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -44,7 +44,7 @@ pub struct Module { pub stab: Option, pub depr: Option, pub impls: Vec, - pub def_traits: Vec, + pub def_traits: Vec, pub foreigns: Vec, pub macros: Vec, pub is_crate: bool, @@ -227,7 +227,7 @@ pub struct Impl { pub id: ast::NodeId, } -pub struct DefaultImpl { +pub struct AutoImpl { pub unsafety: hir::Unsafety, pub trait_: hir::TraitRef, pub id: ast::NodeId, diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 111ae4ede277a..2f7bd5e39a149 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -10,7 +10,6 @@ use std::fs::File; use std::io::prelude::*; -use std::io; use std::path::Path; use std::str; use html::markdown::{Markdown, RenderType}; @@ -70,17 +69,13 @@ pub fn load_string>(file_path: P) -> Result Ok(s.to_string()), Err(_) => { - let _ = writeln!(&mut io::stderr(), - "error reading `{}`: not UTF-8", - file_path.display()); + eprintln!("error reading `{}`: not UTF-8", file_path.display()); Err(LoadStringError::BadUtf8) } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6303fd662bf2b..2f5150d799f45 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -601,7 +601,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt: clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), clean::BareFunction(ref decl) => { if f.alternate() { - write!(f, "{}{}fn{:#}{:#}", + write!(f, "{}{:#}fn{:#}{:#}", UnsafetySpace(decl.unsafety), AbiSpace(decl.abi), decl.generics, @@ -614,7 +614,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt: } clean::Tuple(ref typs) => { match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Tuple, "()"), + &[] => primitive_link(f, PrimitiveType::Unit, "()"), &[ref one] => { primitive_link(f, PrimitiveType::Tuple, "(")?; //carry f.alternate() into this display w/o branching manually diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 98863b229b511..6b0f209c0c454 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -42,7 +42,7 @@ pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str> let mut out = Vec::new(); if let Some((tooltip, class)) = tooltip { - write!(out, "
{}
", class, tooltip).unwrap(); } diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index f584c4e2f4d9c..c214c15ed4b2b 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -41,6 +41,7 @@ pub enum ItemType { Constant = 17, AssociatedConst = 18, Union = 19, + ForeignType = 20, } @@ -81,7 +82,8 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::PrimitiveItem(..) => ItemType::Primitive, clean::AssociatedConstItem(..) => ItemType::AssociatedConst, clean::AssociatedTypeItem(..) => ItemType::AssociatedType, - clean::DefaultImplItem(..) => ItemType::Impl, + clean::AutoImplItem(..) => ItemType::Impl, + clean::ForeignTypeItem => ItemType::ForeignType, clean::StrippedItem(..) => unreachable!(), } } @@ -100,6 +102,7 @@ impl From for ItemType { clean::TypeKind::Const => ItemType::Constant, clean::TypeKind::Variant => ItemType::Variant, clean::TypeKind::Typedef => ItemType::Typedef, + clean::TypeKind::Foreign => ItemType::ForeignType, } } } @@ -127,6 +130,7 @@ impl ItemType { ItemType::AssociatedType => "associatedtype", ItemType::Constant => "constant", ItemType::AssociatedConst => "associatedconstant", + ItemType::ForeignType => "foreigntype", } } @@ -139,7 +143,8 @@ impl ItemType { ItemType::Typedef | ItemType::Trait | ItemType::Primitive | - ItemType::AssociatedType => NameSpace::Type, + ItemType::AssociatedType | + ItemType::ForeignType => NameSpace::Type, ItemType::ExternCrate | ItemType::Import | diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index d08a7bde71c73..8c14d1bbe8f8a 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -97,13 +97,15 @@ r##"
Show this help dialog
S
Focus the search field
-
+
Move up in search results
-
+
Move down in search results
+
+
Switch tab
Go to active search result
-
+
+
+ / -
Collapse/expand all sections
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 80d1f0b01cc26..f7a67b1b9c79c 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -30,7 +30,6 @@ use libc; use std::slice; -use std::ascii::AsciiExt; use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::default::Default; @@ -228,9 +227,9 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { )) }); let tooltip = if ignore { - Some(("Be careful when using this code, it's not being tested!", "ignore")) + Some(("This example is not tested", "ignore")) } else if compile_fail { - Some(("This code doesn't compile so be extra careful!", "compile_fail")) + Some(("This example deliberately fails to compile", "compile_fail")) } else { None }; @@ -371,7 +370,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { match self.inner.next() { Some(Event::FootnoteReference(ref reference)) => { let entry = self.get_entry(&reference); - let reference = format!("
{0}\ + let reference = format!("{0}\

(&mut self, mut predicate: P) -> Option where Self: Sized, P: FnMut(Self::Item) -> bool, { - // `enumerate` might overflow. - for (i, x) in self.enumerate() { - if predicate(x) { - return Some(i); - } - } - None + // The addition might panic on overflow + self.try_fold(0, move |i, x| { + if predicate(x) { LoopState::Break(i) } + else { LoopState::Continue(i + 1) } + }).break_value() } /// Searches for an element in an iterator from the right, returning its @@ -1681,17 +1747,14 @@ pub trait Iterator { P: FnMut(Self::Item) -> bool, Self: Sized + ExactSizeIterator + DoubleEndedIterator { - let mut i = self.len(); - - while let Some(v) = self.next_back() { - // No need for an overflow check here, because `ExactSizeIterator` - // implies that the number of elements fits into a `usize`. - i -= 1; - if predicate(v) { - return Some(i); - } - } - None + // No need for an overflow check here, because `ExactSizeIterator` + // implies that the number of elements fits into a `usize`. + let n = self.len(); + self.try_rfold(n, move |i, x| { + let i = i - 1; + if predicate(x) { LoopState::Break(i) } + else { LoopState::Continue(i) } + }).break_value() } /// Returns the maximum element of an iterator. @@ -1922,10 +1985,10 @@ pub trait Iterator { let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); - for (t, u) in self { + self.for_each(|(t, u)| { ts.extend(Some(t)); us.extend(Some(u)); - } + }); (ts, us) } @@ -2059,14 +2122,23 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return Ordering::Equal, - (None, _ ) => return Ordering::Less, - (_ , None) => return Ordering::Greater, - (Some(x), Some(y)) => match x.cmp(&y) { - Ordering::Equal => (), - non_eq => return non_eq, + let x = match self.next() { + None => if other.next().is_none() { + return Ordering::Equal + } else { + return Ordering::Less }, + Some(val) => val, + }; + + let y = match other.next() { + None => return Ordering::Greater, + Some(val) => val, + }; + + match x.cmp(&y) { + Ordering::Equal => (), + non_eq => return non_eq, } } } @@ -2082,14 +2154,23 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return Some(Ordering::Equal), - (None, _ ) => return Some(Ordering::Less), - (_ , None) => return Some(Ordering::Greater), - (Some(x), Some(y)) => match x.partial_cmp(&y) { - Some(Ordering::Equal) => (), - non_eq => return non_eq, + let x = match self.next() { + None => if other.next().is_none() { + return Some(Ordering::Equal) + } else { + return Some(Ordering::Less) }, + Some(val) => val, + }; + + let y = match other.next() { + None => return Some(Ordering::Greater), + Some(val) => val, + }; + + match x.partial_cmp(&y) { + Some(Ordering::Equal) => (), + non_eq => return non_eq, } } } @@ -2105,11 +2186,17 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return true, - (None, _) | (_, None) => return false, - (Some(x), Some(y)) => if x != y { return false }, - } + let x = match self.next() { + None => return other.next().is_none(), + Some(val) => val, + }; + + let y = match other.next() { + None => return false, + Some(val) => val, + }; + + if x != y { return false } } } @@ -2124,11 +2211,17 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return false, - (None, _) | (_, None) => return true, - (Some(x), Some(y)) => if x.ne(&y) { return true }, - } + let x = match self.next() { + None => return other.next().is_some(), + Some(val) => val, + }; + + let y = match other.next() { + None => return true, + Some(val) => val, + }; + + if x != y { return true } } } @@ -2143,18 +2236,21 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return false, - (None, _ ) => return true, - (_ , None) => return false, - (Some(x), Some(y)) => { - match x.partial_cmp(&y) { - Some(Ordering::Less) => return true, - Some(Ordering::Equal) => {} - Some(Ordering::Greater) => return false, - None => return false, - } - }, + let x = match self.next() { + None => return other.next().is_some(), + Some(val) => val, + }; + + let y = match other.next() { + None => return false, + Some(val) => val, + }; + + match x.partial_cmp(&y) { + Some(Ordering::Less) => return true, + Some(Ordering::Equal) => (), + Some(Ordering::Greater) => return false, + None => return false, } } } @@ -2170,18 +2266,21 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return true, - (None, _ ) => return true, - (_ , None) => return false, - (Some(x), Some(y)) => { - match x.partial_cmp(&y) { - Some(Ordering::Less) => return true, - Some(Ordering::Equal) => {} - Some(Ordering::Greater) => return false, - None => return false, - } - }, + let x = match self.next() { + None => { other.next(); return true; }, + Some(val) => val, + }; + + let y = match other.next() { + None => return false, + Some(val) => val, + }; + + match x.partial_cmp(&y) { + Some(Ordering::Less) => return true, + Some(Ordering::Equal) => (), + Some(Ordering::Greater) => return false, + None => return false, } } } @@ -2197,18 +2296,21 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return false, - (None, _ ) => return false, - (_ , None) => return true, - (Some(x), Some(y)) => { - match x.partial_cmp(&y) { - Some(Ordering::Less) => return false, - Some(Ordering::Equal) => {} - Some(Ordering::Greater) => return true, - None => return false, - } - } + let x = match self.next() { + None => { other.next(); return false; }, + Some(val) => val, + }; + + let y = match other.next() { + None => return true, + Some(val) => val, + }; + + match x.partial_cmp(&y) { + Some(Ordering::Less) => return false, + Some(Ordering::Equal) => (), + Some(Ordering::Greater) => return true, + None => return false, } } } @@ -2224,18 +2326,21 @@ pub trait Iterator { let mut other = other.into_iter(); loop { - match (self.next(), other.next()) { - (None, None) => return true, - (None, _ ) => return false, - (_ , None) => return true, - (Some(x), Some(y)) => { - match x.partial_cmp(&y) { - Some(Ordering::Less) => return false, - Some(Ordering::Equal) => {} - Some(Ordering::Greater) => return true, - None => return false, - } - }, + let x = match self.next() { + None => return other.next().is_none(), + Some(val) => val, + }; + + let y = match other.next() { + None => return true, + Some(val) => val, + }; + + match x.partial_cmp(&y) { + Some(Ordering::Less) => return false, + Some(Ordering::Equal) => (), + Some(Ordering::Greater) => return true, + None => return false, } } } @@ -2258,17 +2363,17 @@ fn select_fold1(mut it: I, // start with the first element as our selection. This avoids // having to use `Option`s inside the loop, translating to a // sizeable performance gain (6x in one case). - it.next().map(|mut sel| { - let mut sel_p = f_proj(&sel); + it.next().map(|first| { + let first_p = f_proj(&first); - for x in it { + it.fold((first_p, first), |(sel_p, sel), x| { let x_p = f_proj(&x); if f_cmp(&sel_p, &sel, &x_p, &x) { - sel = x; - sel_p = x_p; + (x_p, x) + } else { + (sel_p, sel) } - } - (sel_p, sel) + }) }) } diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 2d3ff6a348d39..e173f43b5e6ea 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -305,6 +305,7 @@ use cmp; use fmt; use iter_private::TrustedRandomAccess; +use ops::Try; use usize; #[stable(feature = "rust1", since = "1.0.0")] @@ -336,6 +337,71 @@ mod range; mod sources; mod traits; +/// Transparent newtype used to implement foo methods in terms of try_foo. +/// Important until #43278 is fixed; might be better as `Result` later. +struct AlwaysOk(pub T); + +impl Try for AlwaysOk { + type Ok = T; + type Error = !; + #[inline] + fn into_result(self) -> Result { Ok(self.0) } + #[inline] + fn from_error(v: Self::Error) -> Self { v } + #[inline] + fn from_ok(v: Self::Ok) -> Self { AlwaysOk(v) } +} + +/// Used to make try_fold closures more like normal loops +#[derive(PartialEq)] +enum LoopState { + Continue(C), + Break(B), +} + +impl Try for LoopState { + type Ok = C; + type Error = B; + #[inline] + fn into_result(self) -> Result { + match self { + LoopState::Continue(y) => Ok(y), + LoopState::Break(x) => Err(x), + } + } + #[inline] + fn from_error(v: Self::Error) -> Self { LoopState::Break(v) } + #[inline] + fn from_ok(v: Self::Ok) -> Self { LoopState::Continue(v) } +} + +impl LoopState { + #[inline] + fn break_value(self) -> Option { + match self { + LoopState::Continue(..) => None, + LoopState::Break(x) => Some(x), + } + } +} + +impl LoopState { + #[inline] + fn from_try(r: R) -> Self { + match Try::into_result(r) { + Ok(v) => LoopState::Continue(v), + Err(v) => LoopState::Break(Try::from_error(v)), + } + } + #[inline] + fn into_try(self) -> R { + match self { + LoopState::Continue(v) => Try::from_ok(v), + LoopState::Break(v) => v, + } + } +} + /// A double-ended iterator with the direction inverted. /// /// This `struct` is created by the [`rev`] method on [`Iterator`]. See its @@ -359,6 +425,12 @@ impl Iterator for Rev where I: DoubleEndedIterator { #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + fn try_fold(&mut self, init: B, f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.iter.try_rfold(init, f) + } + fn fold(self, init: Acc, f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -385,6 +457,12 @@ impl DoubleEndedIterator for Rev where I: DoubleEndedIterator { #[inline] fn next_back(&mut self) -> Option<::Item> { self.iter.next() } + fn try_rfold(&mut self, init: B, f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.iter.try_fold(init, f) + } + fn rfold(self, init: Acc, f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -447,6 +525,12 @@ impl<'a, I, T: 'a> Iterator for Cloned self.it.size_hint() } + fn try_fold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.it.try_fold(init, move |acc, elt| f(acc, elt.clone())) + } + fn fold(self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -462,6 +546,12 @@ impl<'a, I, T: 'a> DoubleEndedIterator for Cloned self.it.next_back().cloned() } + fn try_rfold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + self.it.try_rfold(init, move |acc, elt| f(acc, elt.clone())) + } + fn rfold(self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -558,7 +648,7 @@ impl Iterator for Cycle where I: Clone + Iterator { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for Cycle where I: Clone + Iterator {} -/// An adapter for stepping iterators by a custom amount. +/// An iterator for stepping iterators by a custom amount. /// /// This `struct` is created by the [`step_by`] method on [`Iterator`]. See /// its documentation for more. @@ -683,6 +773,25 @@ impl Iterator for Chain where } } + fn try_fold(&mut self, init: Acc, mut f: F) -> R where + Self: Sized, F: FnMut(Acc, Self::Item) -> R, R: Try + { + let mut accum = init; + match self.state { + ChainState::Both | ChainState::Front => { + accum = self.a.try_fold(accum, &mut f)?; + if let ChainState::Both = self.state { + self.state = ChainState::Back; + } + } + _ => { } + } + if let ChainState::Back = self.state { + accum = self.b.try_fold(accum, &mut f)?; + } + Try::from_ok(accum) + } + fn fold(self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -792,6 +901,25 @@ impl DoubleEndedIterator for Chain where } } + fn try_rfold(&mut self, init: Acc, mut f: F) -> R where + Self: Sized, F: FnMut(Acc, Self::Item) -> R, R: Try + { + let mut accum = init; + match self.state { + ChainState::Both | ChainState::Back => { + accum = self.b.try_rfold(accum, &mut f)?; + if let ChainState::Both = self.state { + self.state = ChainState::Front; + } + } + _ => { } + } + if let ChainState::Front = self.state { + accum = self.a.try_rfold(accum, &mut f)?; + } + Try::from_ok(accum) + } + fn rfold(self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -1128,6 +1256,13 @@ impl Iterator for Map where F: FnMut(I::Item) -> B { self.iter.size_hint() } + fn try_fold(&mut self, init: Acc, mut g: G) -> R where + Self: Sized, G: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_fold(init, move |acc, elt| g(acc, f(elt))) + } + fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { @@ -1145,6 +1280,13 @@ impl DoubleEndedIterator for Map where self.iter.next_back().map(&mut self.f) } + fn try_rfold(&mut self, init: Acc, mut g: G) -> R where + Self: Sized, G: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_rfold(init, move |acc, elt| g(acc, f(elt))) + } + fn rfold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { @@ -1250,6 +1392,30 @@ impl Iterator for Filter where P: FnMut(&I::Item) -> bool } count } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let predicate = &mut self.predicate; + self.iter.try_fold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + Try::from_ok(acc) + }) + } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.fold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1265,6 +1431,30 @@ impl DoubleEndedIterator for Filter } None } + + #[inline] + fn try_rfold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let predicate = &mut self.predicate; + self.iter.try_rfold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + Try::from_ok(acc) + }) + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.rfold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1316,6 +1506,28 @@ impl Iterator for FilterMap let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_fold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => Try::from_ok(acc), + }) + } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1331,6 +1543,28 @@ impl DoubleEndedIterator for FilterMap } None } + + #[inline] + fn try_rfold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_rfold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => Try::from_ok(acc), + }) + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1395,6 +1629,32 @@ impl Iterator for Enumerate where I: Iterator { fn count(self) -> usize { self.iter.count() } + + #[inline] + #[rustc_inherit_overflow_checks] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let count = &mut self.count; + self.iter.try_fold(init, move |acc, item| { + let acc = fold(acc, (*count, item)); + *count += 1; + acc + }) + } + + #[inline] + #[rustc_inherit_overflow_checks] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut count = self.count; + self.iter.fold(init, move |acc, item| { + let acc = fold(acc, (count, item)); + count += 1; + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1410,6 +1670,32 @@ impl DoubleEndedIterator for Enumerate where (self.count + len, a) }) } + + #[inline] + fn try_rfold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + let mut count = self.count + self.iter.len(); + self.iter.try_rfold(init, move |acc, item| { + count -= 1; + fold(acc, (count, item)) + }) + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + let mut count = self.count + self.iter.len(); + self.iter.rfold(init, move |acc, item| { + count -= 1; + fold(acc, (count, item)) + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1521,6 +1807,30 @@ impl Iterator for Peekable { let hi = hi.and_then(|x| x.checked_add(peek_len)); (lo, hi) } + + #[inline] + fn try_fold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + let acc = match self.peeked.take() { + Some(None) => return Try::from_ok(init), + Some(Some(v)) => f(init, v)?, + None => init, + }; + self.iter.try_fold(acc, f) + } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let acc = match self.peeked { + Some(None) => return init, + Some(Some(v)) => fold(init, v), + None => init, + }; + self.iter.fold(acc, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1615,13 +1925,16 @@ impl Iterator for SkipWhile #[inline] fn next(&mut self) -> Option { - for x in self.iter.by_ref() { - if self.flag || !(self.predicate)(&x) { - self.flag = true; - return Some(x); + let flag = &mut self.flag; + let pred = &mut self.predicate; + self.iter.find(move |x| { + if *flag || !pred(x) { + *flag = true; + true + } else { + false } - } - None + }) } #[inline] @@ -1629,6 +1942,32 @@ impl Iterator for SkipWhile let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn try_fold(&mut self, mut init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v)?, + None => return Try::from_ok(init), + } + } + self.iter.try_fold(init, fold) + } + + #[inline] + fn fold(mut self, mut init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v), + None => return init, + } + } + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1688,6 +2027,26 @@ impl Iterator for TakeWhile let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if self.flag { + Try::from_ok(init) + } else { + let flag = &mut self.flag; + let p = &mut self.predicate; + self.iter.try_fold(init, move |acc, x|{ + if p(&x) { + LoopState::from_try(fold(acc, x)) + } else { + *flag = true; + LoopState::Break(Try::from_ok(acc)) + } + }).into_try() + } + } } #[unstable(feature = "fused", issue = "35602")] @@ -1769,6 +2128,34 @@ impl Iterator for Skip where I: Iterator { (lower, upper) } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let n = self.n; + self.n = 0; + if n > 0 { + // nth(n) skips n+1 + if self.iter.nth(n - 1).is_none() { + return Try::from_ok(init); + } + } + self.iter.try_fold(init, fold) + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n > 0 { + // nth(n) skips n+1 + if self.iter.nth(self.n - 1).is_none() { + return init; + } + } + self.iter.fold(init, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1783,6 +2170,22 @@ impl DoubleEndedIterator for Skip where I: DoubleEndedIterator + ExactSize None } } + + fn try_rfold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let mut n = self.len(); + if n == 0 { + Try::from_ok(init) + } else { + self.iter.try_rfold(init, move |acc, x| { + n -= 1; + let r = fold(acc, x); + if n == 0 { LoopState::Break(r) } + else { LoopState::from_try(r) } + }).into_try() + } + } } #[unstable(feature = "fused", issue = "35602")] @@ -1844,6 +2247,23 @@ impl Iterator for Take where I: Iterator{ (lower, upper) } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if self.n == 0 { + Try::from_ok(init) + } else { + let n = &mut self.n; + self.iter.try_fold(init, move |acc, x| { + *n -= 1; + let r = fold(acc, x); + if *n == 0 { LoopState::Break(r) } + else { LoopState::from_try(r) } + }).into_try() + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1895,6 +2315,20 @@ impl Iterator for Scan where let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the scan function } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let state = &mut self.state; + let f = &mut self.f; + self.iter.try_fold(init, move |acc, x| { + match f(state, x) { + None => LoopState::Break(Try::from_ok(acc)), + Some(x) => LoopState::from_try(fold(acc, x)), + } + }).into_try() + } } /// An iterator that maps each element to an iterator, and yields the elements @@ -1960,6 +2394,35 @@ impl Iterator for FlatMap } } + #[inline] + fn try_fold(&mut self, mut init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if let Some(ref mut front) = self.frontiter { + init = front.try_fold(init, &mut fold)?; + } + self.frontiter = None; + + { + let f = &mut self.f; + let frontiter = &mut self.frontiter; + init = self.iter.try_fold(init, |acc, x| { + let mut mid = f(x).into_iter(); + let r = mid.try_fold(acc, &mut fold); + *frontiter = Some(mid); + r + })?; + } + self.frontiter = None; + + if let Some(ref mut back) = self.backiter { + init = back.try_fold(init, &mut fold)?; + } + self.backiter = None; + + Try::from_ok(init) + } + #[inline] fn fold(self, init: Acc, mut fold: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, @@ -1991,6 +2454,45 @@ impl DoubleEndedIterator for FlatMap wher } } } + + #[inline] + fn try_rfold(&mut self, mut init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if let Some(ref mut back) = self.backiter { + init = back.try_rfold(init, &mut fold)?; + } + self.backiter = None; + + { + let f = &mut self.f; + let backiter = &mut self.backiter; + init = self.iter.try_rfold(init, |acc, x| { + let mut mid = f(x).into_iter(); + let r = mid.try_rfold(acc, &mut fold); + *backiter = Some(mid); + r + })?; + } + self.backiter = None; + + if let Some(ref mut front) = self.frontiter { + init = front.try_rfold(init, &mut fold)?; + } + self.frontiter = None; + + Try::from_ok(init) + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.frontiter.into_iter() + .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.backiter) + .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) + } } #[unstable(feature = "fused", issue = "35602")] @@ -2068,6 +2570,30 @@ impl Iterator for Fuse where I: Iterator { self.iter.size_hint() } } + + #[inline] + default fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if self.done { + Try::from_ok(init) + } else { + let acc = self.iter.try_fold(init, fold)?; + self.done = true; + Try::from_ok(acc) + } + } + + #[inline] + default fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.fold(init, fold) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2082,6 +2608,30 @@ impl DoubleEndedIterator for Fuse where I: DoubleEndedIterator { next } } + + #[inline] + default fn try_rfold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + if self.done { + Try::from_ok(init) + } else { + let acc = self.iter.try_rfold(init, fold)?; + self.done = true; + Try::from_ok(acc) + } + } + + #[inline] + default fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.rfold(init, fold) + } + } } unsafe impl TrustedRandomAccess for Fuse @@ -2122,6 +2672,20 @@ impl Iterator for Fuse where I: FusedIterator { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.iter.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", reason = "recently added", issue = "35602")] @@ -2132,6 +2696,20 @@ impl DoubleEndedIterator for Fuse fn next_back(&mut self) -> Option<::Item> { self.iter.next_back() } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.iter.try_rfold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, fold) + } } @@ -2196,6 +2774,22 @@ impl Iterator for Inspect where F: FnMut(&I::Item) { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_fold(init, move |acc, item| { f(&item); fold(acc, item) }) + } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2207,6 +2801,22 @@ impl DoubleEndedIterator for Inspect let next = self.iter.next_back(); self.do_inspect(next) } + + #[inline] + fn try_rfold(&mut self, init: Acc, mut fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + let f = &mut self.f; + self.iter.try_rfold(init, move |acc, item| { f(&item); fold(acc, item) }) + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 73d518b570a11..e9aee4a4676de 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -89,6 +89,7 @@ macro_rules! step_impl_unsigned { } #[inline] + #[allow(unreachable_patterns)] fn add_usize(&self, n: usize) -> Option { match <$t>::try_from(n) { Ok(n_as_t) => self.checked_add(n_as_t), @@ -120,6 +121,7 @@ macro_rules! step_impl_signed { } #[inline] + #[allow(unreachable_patterns)] fn add_usize(&self, n: usize) -> Option { match <$unsigned>::try_from(n) { Ok(n_as_unsigned) => { diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 28236d193c324..11e668d228c48 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -7,9 +7,11 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use ops::{Mul, Add}; +use ops::{Mul, Add, Try}; use num::Wrapping; +use super::{AlwaysOk, LoopState}; + /// Conversion from an `Iterator`. /// /// By implementing `FromIterator` for a type, you define how it will be @@ -415,6 +417,52 @@ pub trait DoubleEndedIterator: Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; + /// This is the reverse version of [`try_fold()`]: it takes elements + /// starting from the back of the iterator. + /// + /// [`try_fold()`]: trait.Iterator.html#method.try_fold + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_try_fold)] + /// let a = ["1", "2", "3"]; + /// let sum = a.iter() + /// .map(|&s| s.parse::()) + /// .try_rfold(0, |acc, x| x.and_then(|y| Ok(acc + y))); + /// assert_eq!(sum, Ok(6)); + /// ``` + /// + /// Short-circuiting: + /// + /// ``` + /// #![feature(iterator_try_fold)] + /// let a = ["1", "rust", "3"]; + /// let mut it = a.iter(); + /// let sum = it + /// .by_ref() + /// .map(|&s| s.parse::()) + /// .try_rfold(0, |acc, x| x.and_then(|y| Ok(acc + y))); + /// assert!(sum.is_err()); + /// + /// // Because it short-circuited, the remaining elements are still + /// // available through the iterator. + /// assert_eq!(it.next_back(), Some(&"1")); + /// ``` + #[inline] + #[unstable(feature = "iterator_try_fold", issue = "45594")] + fn try_rfold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try + { + let mut accum = init; + while let Some(x) = self.next_back() { + accum = f(accum, x)?; + } + Try::from_ok(accum) + } + /// An iterator method that reduces the iterator's elements to a single, /// final value, starting from the back. /// @@ -470,13 +518,10 @@ pub trait DoubleEndedIterator: Iterator { /// ``` #[inline] #[unstable(feature = "iter_rfold", issue = "44705")] - fn rfold(mut self, mut accum: B, mut f: F) -> B where + fn rfold(mut self, accum: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { - while let Some(x) = self.next_back() { - accum = f(accum, x); - } - accum + self.try_rfold(accum, move |acc, x| AlwaysOk(f(acc, x))).0 } /// Searches for an element of an iterator from the right that satisfies a predicate. @@ -531,10 +576,10 @@ pub trait DoubleEndedIterator: Iterator { Self: Sized, P: FnMut(&Self::Item) -> bool { - while let Some(x) = self.next_back() { - if predicate(&x) { return Some(x) } - } - None + self.try_rfold((), move |(), x| { + if predicate(&x) { LoopState::Break(x) } + else { LoopState::Continue(()) } + }).break_value() } } diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 69612bd2a32a4..d5190b65863cb 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -85,29 +85,12 @@ #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] -#![cfg_attr(not(stage0), feature(rustc_const_unstable))] #![feature(specialization)] #![feature(staged_api)] #![feature(unboxed_closures)] #![feature(untagged_unions)] #![feature(unwind_attributes)] - -#![cfg_attr(not(stage0), feature(const_min_value))] -#![cfg_attr(not(stage0), feature(const_max_value))] -#![cfg_attr(not(stage0), feature(const_atomic_bool_new))] -#![cfg_attr(not(stage0), feature(const_atomic_isize_new))] -#![cfg_attr(not(stage0), feature(const_atomic_usize_new))] -#![cfg_attr(not(stage0), feature(const_atomic_i8_new))] -#![cfg_attr(not(stage0), feature(const_atomic_u8_new))] -#![cfg_attr(not(stage0), feature(const_atomic_i16_new))] -#![cfg_attr(not(stage0), feature(const_atomic_u16_new))] -#![cfg_attr(not(stage0), feature(const_atomic_i32_new))] -#![cfg_attr(not(stage0), feature(const_atomic_u32_new))] -#![cfg_attr(not(stage0), feature(const_atomic_i64_new))] -#![cfg_attr(not(stage0), feature(const_atomic_u64_new))] -#![cfg_attr(not(stage0), feature(const_unsafe_cell_new))] -#![cfg_attr(not(stage0), feature(const_cell_new))] -#![cfg_attr(not(stage0), feature(const_nonzero_new))] +#![feature(doc_spotlight)] #[prelude_import] #[allow(unused)] @@ -190,3 +173,4 @@ pub mod fmt; mod char_private; mod iter_private; mod tuple; +mod unit; diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index d64c984ea7d28..6e3dbcbec9dc9 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -120,6 +120,9 @@ macro_rules! assert_eq { } } }); + ($left:expr, $right:expr,) => ({ + assert_eq!($left, $right) + }); ($left:expr, $right:expr, $($arg:tt)+) => ({ match (&($left), &($right)) { (left_val, right_val) => { @@ -168,6 +171,9 @@ macro_rules! assert_ne { } } }); + ($left:expr, $right:expr,) => { + assert_ne!($left, $right) + }; ($left:expr, $right:expr, $($arg:tt)+) => ({ match (&($left), &($right)) { (left_val, right_val) => { @@ -355,7 +361,7 @@ macro_rules! try { }) } -/// Write formatted data into a buffer +/// Write formatted data into a buffer. /// /// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be /// formatted according to the specified format string and the result will be passed to the writer. @@ -606,9 +612,10 @@ mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] #[cfg(dox)] - macro_rules! format_args { ($fmt:expr, $($args:tt)*) => ({ - /* compiler built-in */ - }) } + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }); + } /// Inspect an environment variable at compile time. /// @@ -618,7 +625,10 @@ mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] #[cfg(dox)] - macro_rules! env { ($name:expr) => ({ /* compiler built-in */ }) } + macro_rules! env { + ($name:expr) => ({ /* compiler built-in */ }); + ($name:expr,) => ({ /* compiler built-in */ }); + } /// Optionally inspect an environment variable at compile time. /// @@ -639,7 +649,8 @@ mod builtin { #[macro_export] #[cfg(dox)] macro_rules! concat_idents { - ($($e:ident),*) => ({ /* compiler built-in */ }) + ($($e:ident),*) => ({ /* compiler built-in */ }); + ($($e:ident,)*) => ({ /* compiler built-in */ }); } /// Concatenates literals into a static string slice. @@ -650,7 +661,10 @@ mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] #[cfg(dox)] - macro_rules! concat { ($($e:expr),*) => ({ /* compiler built-in */ }) } + macro_rules! concat { + ($($e:expr),*) => ({ /* compiler built-in */ }); + ($($e:expr,)*) => ({ /* compiler built-in */ }); + } /// A macro which expands to the line number on which it was invoked. /// @@ -682,7 +696,7 @@ mod builtin { #[cfg(dox)] macro_rules! file { () => ({ /* compiler built-in */ }) } - /// A macro which stringifies its argument. + /// A macro which stringifies its arguments. /// /// For more information, see the documentation for [`std::stringify!`]. /// @@ -690,7 +704,7 @@ mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] #[cfg(dox)] - macro_rules! stringify { ($t:tt) => ({ /* compiler built-in */ }) } + macro_rules! stringify { ($($t:tt)*) => ({ /* compiler built-in */ }) } /// Includes a utf8-encoded file as a string. /// diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index e8fd729b638be..17e77654cf5ef 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -39,13 +39,14 @@ use hash::Hasher; /// [arc]: ../../std/sync/struct.Arc.html /// [ub]: ../../reference/behavior-considered-undefined.html #[stable(feature = "rust1", since = "1.0.0")] -#[lang = "send"] #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] pub unsafe trait Send { // empty. } #[stable(feature = "rust1", since = "1.0.0")] +#[allow(unknown_lints)] +#[allow(auto_impl)] unsafe impl Send for .. { } #[stable(feature = "rust1", since = "1.0.0")] @@ -122,7 +123,7 @@ pub trait Sized { /// [RFC982]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md /// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "unsize", issue = "27732")] -#[lang="unsize"] +#[lang = "unsize"] pub trait Unsize { // Empty. } @@ -312,7 +313,7 @@ pub trait Copy : Clone { /// /// For cases when one does need thread-safe interior mutability, /// Rust provides [atomic data types], as well as explicit locking via -/// [`sync::Mutex`][mutex] and [`sync::RWLock`][rwlock]. These types +/// [`sync::Mutex`][mutex] and [`sync::RwLock`][rwlock]. These types /// ensure that any mutation cannot cause data races, hence the types /// are `Sync`. Likewise, [`sync::Arc`][arc] provides a thread-safe /// analogue of [`Rc`][rc]. @@ -349,6 +350,8 @@ pub unsafe trait Sync { } #[stable(feature = "rust1", since = "1.0.0")] +#[allow(unknown_lints)] +#[allow(auto_impl)] unsafe impl Sync for .. { } #[stable(feature = "rust1", since = "1.0.0")] @@ -562,6 +565,8 @@ mod impls { #[lang = "freeze"] unsafe trait Freeze {} +#[allow(unknown_lints)] +#[allow(auto_impl)] unsafe impl Freeze for .. {} impl !Freeze for UnsafeCell {} diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 669b93120cf45..5b1a9399c39bf 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -209,6 +209,35 @@ pub fn forget(t: T) { /// The mutability of a pointer does not change its size. As such, `&T` and `&mut T` /// have the same size. Likewise for `*const T` and `*mut T`. /// +/// # Size of `#[repr(C)]` items +/// +/// The `C` representation for items has a defined layout. With this layout, +/// the size of items is also stable as long as all fields have a stable size. +/// +/// ## Size of Structs +/// +/// For `structs`, the size is determined by the following algorithm. +/// +/// For each field in the struct ordered by declaration order: +/// +/// 1. Add the size of the field. +/// 2. Round up the current size to the nearest multiple of the next field's [alignment]. +/// +/// Finally, round the size of the struct to the nearest multiple of its [alignment]. +/// +/// Unlike `C`, zero sized structs are not rounded up to one byte in size. +/// +/// ## Size of Enums +/// +/// Enums that carry no data other than the descriminant have the same size as C enums +/// on the platform they are compiled for. +/// +/// ## Size of Unions +/// +/// The size of a union is the size of its largest field. +/// +/// Unlike `C`, zero sized unions are not rounded up to one byte in size. +/// /// # Examples /// /// ``` @@ -231,9 +260,57 @@ pub fn forget(t: T) { /// assert_eq!(mem::size_of::<&i32>(), mem::size_of::>()); /// assert_eq!(mem::size_of::>(), mem::size_of::>>()); /// ``` +/// +/// Using `#[repr(C)]`. +/// +/// ``` +/// use std::mem; +/// +/// #[repr(C)] +/// struct FieldStruct { +/// first: u8, +/// second: u16, +/// third: u8 +/// } +/// +/// // The size of the first field is 1, so add 1 to the size. Size is 1. +/// // The alignment of the second field is 2, so add 1 to the size for padding. Size is 2. +/// // The size of the second field is 2, so add 2 to the size. Size is 4. +/// // The alignment of the third field is 1, so add 0 to the size for padding. Size is 4. +/// // The size of the third field is 1, so add 1 to the size. Size is 5. +/// // Finally, the alignment of the struct is 2, so add 1 to the size for padding. Size is 6. +/// assert_eq!(6, mem::size_of::()); +/// +/// #[repr(C)] +/// struct TupleStruct(u8, u16, u8); +/// +/// // Tuple structs follow the same rules. +/// assert_eq!(6, mem::size_of::()); +/// +/// // Note that reordering the fields can lower the size. We can remove both padding bytes +/// // by putting `third` before `second`. +/// #[repr(C)] +/// struct FieldStructOptimized { +/// first: u8, +/// third: u8, +/// second: u16 +/// } +/// +/// assert_eq!(4, mem::size_of::()); +/// +/// // Union size is the size of the largest field. +/// #[repr(C)] +/// union ExampleUnion { +/// smaller: u8, +/// larger: u16 +/// } +/// +/// assert_eq!(2, mem::size_of::()); +/// ``` +/// +/// [alignment]: ./fn.align_of.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_size_of"))] pub const fn size_of() -> usize { unsafe { intrinsics::size_of::() } } @@ -325,7 +402,6 @@ pub fn min_align_of_val(val: &T) -> usize { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_align_of"))] pub const fn align_of() -> usize { unsafe { intrinsics::min_align_of::() } } @@ -351,9 +427,11 @@ pub fn align_of_val(val: &T) -> usize { /// Returns whether dropping values of type `T` matters. /// -/// This is purely an optimization hint, and may be implemented conservatively. -/// For instance, always returning `true` would be a valid implementation of -/// this function. +/// This is purely an optimization hint, and may be implemented conservatively: +/// it may return `true` for types that don't actually need to be dropped. +/// As such always returning `true` would be a valid implementation of +/// this function. However if this function actually returns `false`, then you +/// can be certain dropping `T` has no side effect. /// /// Low level implementations of things like collections, which need to manually /// drop their data, should use this function to avoid unnecessarily @@ -402,7 +480,7 @@ pub fn align_of_val(val: &T) -> usize { /// } /// ``` #[inline] -#[stable(feature = "needs_drop", since = "1.22.0")] +#[stable(feature = "needs_drop", since = "1.21.0")] pub fn needs_drop() -> bool { unsafe { intrinsics::needs_drop::() } } @@ -758,7 +836,7 @@ pub unsafe fn transmute_copy(src: &T) -> U { /// /// See the `discriminant` function in this module for more information. #[stable(feature = "discriminant_value", since = "1.21.0")] -pub struct Discriminant(u64, PhantomData<*const T>); +pub struct Discriminant(u64, PhantomData T>); // N.B. These trait implementations cannot be derived because we don't want any bounds on T. diff --git a/src/libcore/nonzero.rs b/src/libcore/nonzero.rs index f075d825f5d53..2c966eb3b5794 100644 --- a/src/libcore/nonzero.rs +++ b/src/libcore/nonzero.rs @@ -28,8 +28,7 @@ macro_rules! impl_zeroable_for_pointer_types { unsafe impl Zeroable for $Ptr { #[inline] fn is_zero(&self) -> bool { - // Cast because `is_null` is only available on thin pointers - (*self as *mut u8).is_null() + (*self).is_null() } } )+ @@ -71,7 +70,6 @@ impl NonZero { #[unstable(feature = "nonzero", reason = "needs an RFC to flesh out the design", issue = "27730")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_nonzero_new"))] #[inline] pub const unsafe fn new_unchecked(inner: T) -> Self { NonZero(inner) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index bf31deae7a625..7c7562eac5152 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -12,9 +12,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -use convert::TryFrom; +use convert::{Infallible, TryFrom}; use fmt; use intrinsics; +use ops; use str::FromStr; /// Provides intentionally-wrapped arithmetic on `T`. @@ -109,7 +110,6 @@ macro_rules! int_impl { /// assert_eq!(i8::min_value(), -128); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_min_value"))] #[inline] pub const fn min_value() -> Self { !0 ^ ((!0 as $UnsignedT) >> 1) as Self @@ -123,7 +123,6 @@ macro_rules! int_impl { /// assert_eq!(i8::max_value(), 127); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_max_value"))] #[inline] pub const fn max_value() -> Self { !Self::min_value() @@ -131,7 +130,14 @@ macro_rules! int_impl { /// Converts a string slice in a given base to an integer. /// + /// The string is expected to be an optional `+` or `-` sign + /// followed by digits. /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` /// /// # Panics /// @@ -374,7 +380,7 @@ macro_rules! int_impl { if cfg!(target_endian = "little") { self } else { self.swap_bytes() } } - /// Checked integer addition. Computes `self + other`, returning `None` + /// Checked integer addition. Computes `self + rhs`, returning `None` /// if overflow occurred. /// /// # Examples @@ -387,12 +393,12 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_add(self, other: Self) -> Option { - let (a, b) = self.overflowing_add(other); + pub fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); if b {None} else {Some(a)} } - /// Checked integer subtraction. Computes `self - other`, returning + /// Checked integer subtraction. Computes `self - rhs`, returning /// `None` if underflow occurred. /// /// # Examples @@ -405,12 +411,12 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_sub(self, other: Self) -> Option { - let (a, b) = self.overflowing_sub(other); + pub fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); if b {None} else {Some(a)} } - /// Checked integer multiplication. Computes `self * other`, returning + /// Checked integer multiplication. Computes `self * rhs`, returning /// `None` if underflow or overflow occurred. /// /// # Examples @@ -423,13 +429,13 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_mul(self, other: Self) -> Option { - let (a, b) = self.overflowing_mul(other); + pub fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); if b {None} else {Some(a)} } - /// Checked integer division. Computes `self / other`, returning `None` - /// if `other == 0` or the operation results in underflow or overflow. + /// Checked integer division. Computes `self / rhs`, returning `None` + /// if `rhs == 0` or the operation results in underflow or overflow. /// /// # Examples /// @@ -442,16 +448,16 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_div(self, other: Self) -> Option { - if other == 0 || (self == Self::min_value() && other == -1) { + pub fn checked_div(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::min_value() && rhs == -1) { None } else { - Some(unsafe { intrinsics::unchecked_div(self, other) }) + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) } } - /// Checked integer remainder. Computes `self % other`, returning `None` - /// if `other == 0` or the operation results in underflow or overflow. + /// Checked integer remainder. Computes `self % rhs`, returning `None` + /// if `rhs == 0` or the operation results in underflow or overflow. /// /// # Examples /// @@ -466,11 +472,11 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[inline] - pub fn checked_rem(self, other: Self) -> Option { - if other == 0 || (self == Self::min_value() && other == -1) { + pub fn checked_rem(self, rhs: Self) -> Option { + if rhs == 0 || (self == Self::min_value() && rhs == -1) { None } else { - Some(unsafe { intrinsics::unchecked_rem(self, other) }) + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) } } @@ -553,7 +559,7 @@ macro_rules! int_impl { } } - /// Saturating integer addition. Computes `self + other`, saturating at + /// Saturating integer addition. Computes `self + rhs`, saturating at /// the numeric bounds instead of overflowing. /// /// # Examples @@ -566,15 +572,15 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn saturating_add(self, other: Self) -> Self { - match self.checked_add(other) { + pub fn saturating_add(self, rhs: Self) -> Self { + match self.checked_add(rhs) { Some(x) => x, - None if other >= 0 => Self::max_value(), + None if rhs >= 0 => Self::max_value(), None => Self::min_value(), } } - /// Saturating integer subtraction. Computes `self - other`, saturating + /// Saturating integer subtraction. Computes `self - rhs`, saturating /// at the numeric bounds instead of overflowing. /// /// # Examples @@ -587,15 +593,15 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn saturating_sub(self, other: Self) -> Self { - match self.checked_sub(other) { + pub fn saturating_sub(self, rhs: Self) -> Self { + match self.checked_sub(rhs) { Some(x) => x, - None if other >= 0 => Self::min_value(), + None if rhs >= 0 => Self::min_value(), None => Self::max_value(), } } - /// Saturating integer multiplication. Computes `self * other`, + /// Saturating integer multiplication. Computes `self * rhs`, /// saturating at the numeric bounds instead of overflowing. /// /// # Examples @@ -611,9 +617,9 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[inline] - pub fn saturating_mul(self, other: Self) -> Self { - self.checked_mul(other).unwrap_or_else(|| { - if (self < 0 && other < 0) || (self > 0 && other > 0) { + pub fn saturating_mul(self, rhs: Self) -> Self { + self.checked_mul(rhs).unwrap_or_else(|| { + if (self < 0 && rhs < 0) || (self > 0 && rhs > 0) { Self::max_value() } else { Self::min_value() @@ -621,7 +627,7 @@ macro_rules! int_impl { }) } - /// Wrapping (modular) addition. Computes `self + other`, + /// Wrapping (modular) addition. Computes `self + rhs`, /// wrapping around at the boundary of the type. /// /// # Examples @@ -640,7 +646,7 @@ macro_rules! int_impl { } } - /// Wrapping (modular) subtraction. Computes `self - other`, + /// Wrapping (modular) subtraction. Computes `self - rhs`, /// wrapping around at the boundary of the type. /// /// # Examples @@ -660,7 +666,7 @@ macro_rules! int_impl { } /// Wrapping (modular) multiplication. Computes `self * - /// other`, wrapping around at the boundary of the type. + /// rhs`, wrapping around at the boundary of the type. /// /// # Examples /// @@ -678,7 +684,7 @@ macro_rules! int_impl { } } - /// Wrapping (modular) division. Computes `self / other`, + /// Wrapping (modular) division. Computes `self / rhs`, /// wrapping around at the boundary of the type. /// /// The only case where such wrapping can occur is when one @@ -706,7 +712,7 @@ macro_rules! int_impl { self.overflowing_div(rhs).0 } - /// Wrapping (modular) remainder. Computes `self % other`, + /// Wrapping (modular) remainder. Computes `self % rhs`, /// wrapping around at the boundary of the type. /// /// Such wrap-around never actually occurs mathematically; @@ -1282,7 +1288,6 @@ macro_rules! uint_impl { /// assert_eq!(u8::min_value(), 0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_min_value"))] #[inline] pub const fn min_value() -> Self { 0 } @@ -1294,13 +1299,23 @@ macro_rules! uint_impl { /// assert_eq!(u8::max_value(), 255); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_max_value"))] #[inline] pub const fn max_value() -> Self { !0 } /// Converts a string slice in a given base to an integer. /// + /// The string is expected to be an optional `+` sign + /// followed by digits. /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. /// /// # Examples /// @@ -1558,7 +1573,7 @@ macro_rules! uint_impl { if cfg!(target_endian = "little") { self } else { self.swap_bytes() } } - /// Checked integer addition. Computes `self + other`, returning `None` + /// Checked integer addition. Computes `self + rhs`, returning `None` /// if overflow occurred. /// /// # Examples @@ -1571,12 +1586,12 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_add(self, other: Self) -> Option { - let (a, b) = self.overflowing_add(other); + pub fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); if b {None} else {Some(a)} } - /// Checked integer subtraction. Computes `self - other`, returning + /// Checked integer subtraction. Computes `self - rhs`, returning /// `None` if underflow occurred. /// /// # Examples @@ -1589,12 +1604,12 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_sub(self, other: Self) -> Option { - let (a, b) = self.overflowing_sub(other); + pub fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); if b {None} else {Some(a)} } - /// Checked integer multiplication. Computes `self * other`, returning + /// Checked integer multiplication. Computes `self * rhs`, returning /// `None` if underflow or overflow occurred. /// /// # Examples @@ -1607,13 +1622,13 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_mul(self, other: Self) -> Option { - let (a, b) = self.overflowing_mul(other); + pub fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); if b {None} else {Some(a)} } - /// Checked integer division. Computes `self / other`, returning `None` - /// if `other == 0` or the operation results in underflow or overflow. + /// Checked integer division. Computes `self / rhs`, returning `None` + /// if `rhs == 0` or the operation results in underflow or overflow. /// /// # Examples /// @@ -1625,15 +1640,15 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn checked_div(self, other: Self) -> Option { - match other { + pub fn checked_div(self, rhs: Self) -> Option { + match rhs { 0 => None, - other => Some(unsafe { intrinsics::unchecked_div(self, other) }), + rhs => Some(unsafe { intrinsics::unchecked_div(self, rhs) }), } } - /// Checked integer remainder. Computes `self % other`, returning `None` - /// if `other == 0` or the operation results in underflow or overflow. + /// Checked integer remainder. Computes `self % rhs`, returning `None` + /// if `rhs == 0` or the operation results in underflow or overflow. /// /// # Examples /// @@ -1645,11 +1660,11 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[inline] - pub fn checked_rem(self, other: Self) -> Option { - if other == 0 { + pub fn checked_rem(self, rhs: Self) -> Option { + if rhs == 0 { None } else { - Some(unsafe { intrinsics::unchecked_rem(self, other) }) + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) } } @@ -1709,7 +1724,7 @@ macro_rules! uint_impl { if b {None} else {Some(a)} } - /// Saturating integer addition. Computes `self + other`, saturating at + /// Saturating integer addition. Computes `self + rhs`, saturating at /// the numeric bounds instead of overflowing. /// /// # Examples @@ -1722,14 +1737,14 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn saturating_add(self, other: Self) -> Self { - match self.checked_add(other) { + pub fn saturating_add(self, rhs: Self) -> Self { + match self.checked_add(rhs) { Some(x) => x, None => Self::max_value(), } } - /// Saturating integer subtraction. Computes `self - other`, saturating + /// Saturating integer subtraction. Computes `self - rhs`, saturating /// at the numeric bounds instead of overflowing. /// /// # Examples @@ -1742,14 +1757,14 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn saturating_sub(self, other: Self) -> Self { - match self.checked_sub(other) { + pub fn saturating_sub(self, rhs: Self) -> Self { + match self.checked_sub(rhs) { Some(x) => x, None => Self::min_value(), } } - /// Saturating integer multiplication. Computes `self * other`, + /// Saturating integer multiplication. Computes `self * rhs`, /// saturating at the numeric bounds instead of overflowing. /// /// # Examples @@ -1764,11 +1779,11 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[inline] - pub fn saturating_mul(self, other: Self) -> Self { - self.checked_mul(other).unwrap_or(Self::max_value()) + pub fn saturating_mul(self, rhs: Self) -> Self { + self.checked_mul(rhs).unwrap_or(Self::max_value()) } - /// Wrapping (modular) addition. Computes `self + other`, + /// Wrapping (modular) addition. Computes `self + rhs`, /// wrapping around at the boundary of the type. /// /// # Examples @@ -1787,7 +1802,7 @@ macro_rules! uint_impl { } } - /// Wrapping (modular) subtraction. Computes `self - other`, + /// Wrapping (modular) subtraction. Computes `self - rhs`, /// wrapping around at the boundary of the type. /// /// # Examples @@ -1807,7 +1822,7 @@ macro_rules! uint_impl { } /// Wrapping (modular) multiplication. Computes `self * - /// other`, wrapping around at the boundary of the type. + /// rhs`, wrapping around at the boundary of the type. /// /// # Examples /// @@ -1825,7 +1840,7 @@ macro_rules! uint_impl { } } - /// Wrapping (modular) division. Computes `self / other`. + /// Wrapping (modular) division. Computes `self / rhs`. /// Wrapped division on unsigned types is just normal division. /// There's no way wrapping could ever happen. /// This function exists, so that all operations @@ -1844,7 +1859,7 @@ macro_rules! uint_impl { self / rhs } - /// Wrapping (modular) remainder. Computes `self % other`. + /// Wrapping (modular) remainder. Computes `self % rhs`. /// Wrapped remainder calculation on unsigned types is /// just the regular remainder calculation. /// There's no way wrapping could ever happen. @@ -2223,7 +2238,8 @@ macro_rules! uint_impl { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two() + 1 + // Call the trait to get overflow checks + ops::Add::add(self.one_less_than_next_power_of_two(), 1) } /// Returns the smallest power of two greater than or equal to `n`. If @@ -2257,6 +2273,547 @@ impl u8 { intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } + + + /// Checks if the value is within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = 97u8; + /// let non_ascii = 150u8; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + *self & 128 == 0 + } + + /// Makes a copy of the value in its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let lowercase_a = 97u8; + /// + /// assert_eq!(65, lowercase_a.to_ascii_uppercase()); + /// ``` + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> u8 { + ASCII_UPPERCASE_MAP[*self as usize] + } + + /// Makes a copy of the value in its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = 65u8; + /// + /// assert_eq!(97, uppercase_a.to_ascii_lowercase()); + /// ``` + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> u8 { + ASCII_LOWERCASE_MAP[*self as usize] + } + + /// Checks that two values are an ASCII case-insensitive match. + /// + /// This is equivalent to `to_ascii_lowercase(a) == to_ascii_lowercase(b)`. + /// + /// # Examples + /// + /// ``` + /// let lowercase_a = 97u8; + /// let uppercase_a = 65u8; + /// + /// assert!(lowercase_a.eq_ignore_ascii_case(&uppercase_a)); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &u8) -> bool { + self.to_ascii_lowercase() == other.to_ascii_lowercase() + } + + /// Converts this value to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut byte = b'a'; + /// + /// byte.make_ascii_uppercase(); + /// + /// assert_eq!(b'A', byte); + /// ``` + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + *self = self.to_ascii_uppercase(); + } + + /// Converts this value to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut byte = b'A'; + /// + /// byte.make_ascii_lowercase(); + /// + /// assert_eq!(b'a', byte); + /// ``` + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + *self = self.to_ascii_lowercase(); + } + + /// Checks if the value is an ASCII alphabetic character: + /// + /// - U+0041 'A' ... U+005A 'Z', or + /// - U+0061 'a' ... U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_alphabetic()); + /// assert!(uppercase_g.is_ascii_alphabetic()); + /// assert!(a.is_ascii_alphabetic()); + /// assert!(g.is_ascii_alphabetic()); + /// assert!(!zero.is_ascii_alphabetic()); + /// assert!(!percent.is_ascii_alphabetic()); + /// assert!(!space.is_ascii_alphabetic()); + /// assert!(!lf.is_ascii_alphabetic()); + /// assert!(!esc.is_ascii_alphabetic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_alphabetic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + L | Lx | U | Ux => true, + _ => false + } + } + + /// Checks if the value is an ASCII uppercase character: + /// U+0041 'A' ... U+005A 'Z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_uppercase()); + /// assert!(uppercase_g.is_ascii_uppercase()); + /// assert!(!a.is_ascii_uppercase()); + /// assert!(!g.is_ascii_uppercase()); + /// assert!(!zero.is_ascii_uppercase()); + /// assert!(!percent.is_ascii_uppercase()); + /// assert!(!space.is_ascii_uppercase()); + /// assert!(!lf.is_ascii_uppercase()); + /// assert!(!esc.is_ascii_uppercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_uppercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + U | Ux => true, + _ => false + } + } + + /// Checks if the value is an ASCII lowercase character: + /// U+0061 'a' ... U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_lowercase()); + /// assert!(!uppercase_g.is_ascii_lowercase()); + /// assert!(a.is_ascii_lowercase()); + /// assert!(g.is_ascii_lowercase()); + /// assert!(!zero.is_ascii_lowercase()); + /// assert!(!percent.is_ascii_lowercase()); + /// assert!(!space.is_ascii_lowercase()); + /// assert!(!lf.is_ascii_lowercase()); + /// assert!(!esc.is_ascii_lowercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_lowercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + L | Lx => true, + _ => false + } + } + + /// Checks if the value is an ASCII alphanumeric character: + /// + /// - U+0041 'A' ... U+005A 'Z', or + /// - U+0061 'a' ... U+007A 'z', or + /// - U+0030 '0' ... U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_alphanumeric()); + /// assert!(uppercase_g.is_ascii_alphanumeric()); + /// assert!(a.is_ascii_alphanumeric()); + /// assert!(g.is_ascii_alphanumeric()); + /// assert!(zero.is_ascii_alphanumeric()); + /// assert!(!percent.is_ascii_alphanumeric()); + /// assert!(!space.is_ascii_alphanumeric()); + /// assert!(!lf.is_ascii_alphanumeric()); + /// assert!(!esc.is_ascii_alphanumeric()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_alphanumeric(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D | L | Lx | U | Ux => true, + _ => false + } + } + + /// Checks if the value is an ASCII decimal digit: + /// U+0030 '0' ... U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_digit()); + /// assert!(!uppercase_g.is_ascii_digit()); + /// assert!(!a.is_ascii_digit()); + /// assert!(!g.is_ascii_digit()); + /// assert!(zero.is_ascii_digit()); + /// assert!(!percent.is_ascii_digit()); + /// assert!(!space.is_ascii_digit()); + /// assert!(!lf.is_ascii_digit()); + /// assert!(!esc.is_ascii_digit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_digit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D => true, + _ => false + } + } + + /// Checks if the value is an ASCII hexadecimal digit: + /// + /// - U+0030 '0' ... U+0039 '9', or + /// - U+0041 'A' ... U+0046 'F', or + /// - U+0061 'a' ... U+0066 'f'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_hexdigit()); + /// assert!(!uppercase_g.is_ascii_hexdigit()); + /// assert!(a.is_ascii_hexdigit()); + /// assert!(!g.is_ascii_hexdigit()); + /// assert!(zero.is_ascii_hexdigit()); + /// assert!(!percent.is_ascii_hexdigit()); + /// assert!(!space.is_ascii_hexdigit()); + /// assert!(!lf.is_ascii_hexdigit()); + /// assert!(!esc.is_ascii_hexdigit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_hexdigit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D | Lx | Ux => true, + _ => false + } + } + + /// Checks if the value is an ASCII punctuation character: + /// + /// - U+0021 ... U+002F `! " # $ % & ' ( ) * + , - . /`, or + /// - U+003A ... U+0040 `: ; < = > ? @`, or + /// - U+005B ... U+0060 ``[ \ ] ^ _ ` ``, or + /// - U+007B ... U+007E `{ | } ~` + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_punctuation()); + /// assert!(!uppercase_g.is_ascii_punctuation()); + /// assert!(!a.is_ascii_punctuation()); + /// assert!(!g.is_ascii_punctuation()); + /// assert!(!zero.is_ascii_punctuation()); + /// assert!(percent.is_ascii_punctuation()); + /// assert!(!space.is_ascii_punctuation()); + /// assert!(!lf.is_ascii_punctuation()); + /// assert!(!esc.is_ascii_punctuation()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_punctuation(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + P => true, + _ => false + } + } + + /// Checks if the value is an ASCII graphic character: + /// U+0021 '@' ... U+007E '~'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_graphic()); + /// assert!(uppercase_g.is_ascii_graphic()); + /// assert!(a.is_ascii_graphic()); + /// assert!(g.is_ascii_graphic()); + /// assert!(zero.is_ascii_graphic()); + /// assert!(percent.is_ascii_graphic()); + /// assert!(!space.is_ascii_graphic()); + /// assert!(!lf.is_ascii_graphic()); + /// assert!(!esc.is_ascii_graphic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_graphic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Ux | U | Lx | L | D | P => true, + _ => false + } + } + + /// Checks if the value is an ASCII whitespace character: + /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, + /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// U+000B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_whitespace()); + /// assert!(!uppercase_g.is_ascii_whitespace()); + /// assert!(!a.is_ascii_whitespace()); + /// assert!(!g.is_ascii_whitespace()); + /// assert!(!zero.is_ascii_whitespace()); + /// assert!(!percent.is_ascii_whitespace()); + /// assert!(space.is_ascii_whitespace()); + /// assert!(lf.is_ascii_whitespace()); + /// assert!(!esc.is_ascii_whitespace()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_whitespace(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Cw | W => true, + _ => false + } + } + + /// Checks if the value is an ASCII control character: + /// U+0000 NUL ... U+001F UNIT SEPARATOR, or U+007F DELETE. + /// Note that most ASCII whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_control()); + /// assert!(!uppercase_g.is_ascii_control()); + /// assert!(!a.is_ascii_control()); + /// assert!(!g.is_ascii_control()); + /// assert!(!zero.is_ascii_control()); + /// assert!(!percent.is_ascii_control()); + /// assert!(!space.is_ascii_control()); + /// assert!(lf.is_ascii_control()); + /// assert!(esc.is_ascii_control()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_control(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + C | Cw => true, + _ => false + } + } } #[lang = "u16"] @@ -2507,16 +3064,24 @@ impl fmt::Display for TryFromIntError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl From for TryFromIntError { + fn from(infallible: Infallible) -> TryFromIntError { + match infallible { + } + } +} + // no possible bounds violation macro_rules! try_from_unbounded { ($source:ty, $($target:ty),*) => {$( #[unstable(feature = "try_from", issue = "33417")] impl TryFrom<$source> for $target { - type Error = TryFromIntError; + type Error = Infallible; #[inline] - fn try_from(u: $source) -> Result<$target, TryFromIntError> { - Ok(u as $target) + fn try_from(value: $source) -> Result { + Ok(value as $target) } } )*} @@ -2588,31 +3153,17 @@ macro_rules! rev { } /// intra-sign conversions -try_from_unbounded!(u8, u8, u16, u32, u64, u128); -try_from_unbounded!(u16, u16, u32, u64, u128); -try_from_unbounded!(u32, u32, u64, u128); -try_from_unbounded!(u64, u64, u128); -try_from_unbounded!(u128, u128); try_from_upper_bounded!(u16, u8); try_from_upper_bounded!(u32, u16, u8); try_from_upper_bounded!(u64, u32, u16, u8); try_from_upper_bounded!(u128, u64, u32, u16, u8); -try_from_unbounded!(i8, i8, i16, i32, i64, i128); -try_from_unbounded!(i16, i16, i32, i64, i128); -try_from_unbounded!(i32, i32, i64, i128); -try_from_unbounded!(i64, i64, i128); -try_from_unbounded!(i128, i128); try_from_both_bounded!(i16, i8); try_from_both_bounded!(i32, i16, i8); try_from_both_bounded!(i64, i32, i16, i8); try_from_both_bounded!(i128, i64, i32, i16, i8); // unsigned-to-signed -try_from_unbounded!(u8, i16, i32, i64, i128); -try_from_unbounded!(u16, i32, i64, i128); -try_from_unbounded!(u32, i64, i128); -try_from_unbounded!(u64, i128); try_from_upper_bounded!(u8, i8); try_from_upper_bounded!(u16, i8, i16); try_from_upper_bounded!(u32, i8, i16, i32); @@ -2631,15 +3182,13 @@ try_from_both_bounded!(i64, u32, u16, u8); try_from_both_bounded!(i128, u64, u32, u16, u8); // usize/isize -try_from_unbounded!(usize, usize); try_from_upper_bounded!(usize, isize); try_from_lower_bounded!(isize, usize); -try_from_unbounded!(isize, isize); #[cfg(target_pointer_width = "16")] mod ptr_try_from_impls { use super::TryFromIntError; - use convert::TryFrom; + use convert::{Infallible, TryFrom}; try_from_upper_bounded!(usize, u8); try_from_unbounded!(usize, u16, u32, u64, u128); @@ -2651,21 +3200,21 @@ mod ptr_try_from_impls { try_from_both_bounded!(isize, i8); try_from_unbounded!(isize, i16, i32, i64, i128); - rev!(try_from_unbounded, usize, u8, u16); + rev!(try_from_unbounded, usize, u16); rev!(try_from_upper_bounded, usize, u32, u64, u128); rev!(try_from_lower_bounded, usize, i8, i16); rev!(try_from_both_bounded, usize, i32, i64, i128); rev!(try_from_unbounded, isize, u8); rev!(try_from_upper_bounded, isize, u16, u32, u64, u128); - rev!(try_from_unbounded, isize, i8, i16); + rev!(try_from_unbounded, isize, i16); rev!(try_from_both_bounded, isize, i32, i64, i128); } #[cfg(target_pointer_width = "32")] mod ptr_try_from_impls { use super::TryFromIntError; - use convert::TryFrom; + use convert::{Infallible, TryFrom}; try_from_upper_bounded!(usize, u8, u16); try_from_unbounded!(usize, u32, u64, u128); @@ -2677,21 +3226,21 @@ mod ptr_try_from_impls { try_from_both_bounded!(isize, i8, i16); try_from_unbounded!(isize, i32, i64, i128); - rev!(try_from_unbounded, usize, u8, u16, u32); + rev!(try_from_unbounded, usize, u16, u32); rev!(try_from_upper_bounded, usize, u64, u128); rev!(try_from_lower_bounded, usize, i8, i16, i32); rev!(try_from_both_bounded, usize, i64, i128); rev!(try_from_unbounded, isize, u8, u16); rev!(try_from_upper_bounded, isize, u32, u64, u128); - rev!(try_from_unbounded, isize, i8, i16, i32); + rev!(try_from_unbounded, isize, i16, i32); rev!(try_from_both_bounded, isize, i64, i128); } #[cfg(target_pointer_width = "64")] mod ptr_try_from_impls { use super::TryFromIntError; - use convert::TryFrom; + use convert::{Infallible, TryFrom}; try_from_upper_bounded!(usize, u8, u16, u32); try_from_unbounded!(usize, u64, u128); @@ -2703,14 +3252,14 @@ mod ptr_try_from_impls { try_from_both_bounded!(isize, i8, i16, i32); try_from_unbounded!(isize, i64, i128); - rev!(try_from_unbounded, usize, u8, u16, u32, u64); + rev!(try_from_unbounded, usize, u16, u32, u64); rev!(try_from_upper_bounded, usize, u128); rev!(try_from_lower_bounded, usize, i8, i16, i32, i64); rev!(try_from_both_bounded, usize, i128); rev!(try_from_unbounded, isize, u8, u16, u32); rev!(try_from_upper_bounded, isize, u64, u128); - rev!(try_from_unbounded, isize, i8, i16, i32, i64); + rev!(try_from_unbounded, isize, i16, i32, i64); rev!(try_from_both_bounded, isize, i128); } @@ -2934,3 +3483,106 @@ impl_from! { u32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0" // Float -> Float impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] } + +static ASCII_LOWERCASE_MAP: [u8; 256] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'', + b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', + b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', + b'@', + + b'a', b'b', b'c', b'd', b'e', b'f', b'g', + b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', + b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', + b'x', b'y', b'z', + + b'[', b'\\', b']', b'^', b'_', + b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', + b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', + b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', + b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +static ASCII_UPPERCASE_MAP: [u8; 256] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'', + b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', + b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', + b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', + b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', + b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', + b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_', + b'`', + + b'A', b'B', b'C', b'D', b'E', b'F', b'G', + b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', + b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', + b'X', b'Y', b'Z', + + b'{', b'|', b'}', b'~', 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +]; + +enum AsciiCharacterClass { + C, // control + Cw, // control whitespace + W, // whitespace + D, // digit + L, // lowercase + Lx, // lowercase hex digit + U, // uppercase + Ux, // uppercase hex digit + P, // punctuation +} +use self::AsciiCharacterClass::*; + +static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 128] = [ +// _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _a _b _c _d _e _f + C, C, C, C, C, C, C, C, C, Cw,Cw,C, Cw,Cw,C, C, // 0_ + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // 1_ + W, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, // 2_ + D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, // 3_ + P, Ux,Ux,Ux,Ux,Ux,Ux,U, U, U, U, U, U, U, U, U, // 4_ + U, U, U, U, U, U, U, U, U, U, U, P, P, P, P, P, // 5_ + P, Lx,Lx,Lx,Lx,Lx,Lx,L, L, L, L, L, L, L, L, L, // 6_ + L, L, L, L, L, L, L, L, L, L, L, P, P, P, P, C, // 7_ +]; diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index acdf685e850ab..ae1b0b3ce11b2 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -36,6 +36,7 @@ macro_rules! sh_impl_signed { *self = *self << other; } } + forward_ref_op_assign! { impl ShlAssign, shl_assign for Wrapping<$t>, $f } #[stable(feature = "rust1", since = "1.0.0")] impl Shr<$f> for Wrapping<$t> { @@ -58,6 +59,7 @@ macro_rules! sh_impl_signed { *self = *self >> other; } } + forward_ref_op_assign! { impl ShrAssign, shr_assign for Wrapping<$t>, $f } ) } @@ -80,6 +82,7 @@ macro_rules! sh_impl_unsigned { *self = *self << other; } } + forward_ref_op_assign! { impl ShlAssign, shl_assign for Wrapping<$t>, $f } #[stable(feature = "rust1", since = "1.0.0")] impl Shr<$f> for Wrapping<$t> { @@ -98,6 +101,7 @@ macro_rules! sh_impl_unsigned { *self = *self >> other; } } + forward_ref_op_assign! { impl ShrAssign, shr_assign for Wrapping<$t>, $f } ) } @@ -142,6 +146,7 @@ macro_rules! wrapping_impl { *self = *self + other; } } + forward_ref_op_assign! { impl AddAssign, add_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "rust1", since = "1.0.0")] impl Sub for Wrapping<$t> { @@ -162,6 +167,7 @@ macro_rules! wrapping_impl { *self = *self - other; } } + forward_ref_op_assign! { impl SubAssign, sub_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "rust1", since = "1.0.0")] impl Mul for Wrapping<$t> { @@ -182,6 +188,7 @@ macro_rules! wrapping_impl { *self = *self * other; } } + forward_ref_op_assign! { impl MulAssign, mul_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "wrapping_div", since = "1.3.0")] impl Div for Wrapping<$t> { @@ -202,6 +209,7 @@ macro_rules! wrapping_impl { *self = *self / other; } } + forward_ref_op_assign! { impl DivAssign, div_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "wrapping_impls", since = "1.7.0")] impl Rem for Wrapping<$t> { @@ -222,6 +230,7 @@ macro_rules! wrapping_impl { *self = *self % other; } } + forward_ref_op_assign! { impl RemAssign, rem_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "rust1", since = "1.0.0")] impl Not for Wrapping<$t> { @@ -254,6 +263,7 @@ macro_rules! wrapping_impl { *self = *self ^ other; } } + forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "rust1", since = "1.0.0")] impl BitOr for Wrapping<$t> { @@ -274,6 +284,7 @@ macro_rules! wrapping_impl { *self = *self | other; } } + forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "rust1", since = "1.0.0")] impl BitAnd for Wrapping<$t> { @@ -294,6 +305,7 @@ macro_rules! wrapping_impl { *self = *self & other; } } + forward_ref_op_assign! { impl BitAndAssign, bitand_assign for Wrapping<$t>, Wrapping<$t> } #[stable(feature = "wrapping_neg", since = "1.10.0")] impl Neg for Wrapping<$t> { diff --git a/src/libcore/ops/arith.rs b/src/libcore/ops/arith.rs index 62007caedd3fc..8b3d662a6db77 100644 --- a/src/libcore/ops/arith.rs +++ b/src/libcore/ops/arith.rs @@ -662,6 +662,8 @@ macro_rules! add_assign_impl { #[rustc_inherit_overflow_checks] fn add_assign(&mut self, other: $t) { *self += other } } + + forward_ref_op_assign! { impl AddAssign, add_assign for $t, $t } )+) } @@ -713,6 +715,8 @@ macro_rules! sub_assign_impl { #[rustc_inherit_overflow_checks] fn sub_assign(&mut self, other: $t) { *self -= other } } + + forward_ref_op_assign! { impl SubAssign, sub_assign for $t, $t } )+) } @@ -755,6 +759,8 @@ macro_rules! mul_assign_impl { #[rustc_inherit_overflow_checks] fn mul_assign(&mut self, other: $t) { *self *= other } } + + forward_ref_op_assign! { impl MulAssign, mul_assign for $t, $t } )+) } @@ -796,6 +802,8 @@ macro_rules! div_assign_impl { #[inline] fn div_assign(&mut self, other: $t) { *self /= other } } + + forward_ref_op_assign! { impl DivAssign, div_assign for $t, $t } )+) } @@ -841,6 +849,8 @@ macro_rules! rem_assign_impl { #[inline] fn rem_assign(&mut self, other: $t) { *self %= other } } + + forward_ref_op_assign! { impl RemAssign, rem_assign for $t, $t } )+) } diff --git a/src/libcore/ops/bit.rs b/src/libcore/ops/bit.rs index 0bc5e554cb347..7ac5fc4debf14 100644 --- a/src/libcore/ops/bit.rs +++ b/src/libcore/ops/bit.rs @@ -593,6 +593,8 @@ macro_rules! bitand_assign_impl { #[inline] fn bitand_assign(&mut self, other: $t) { *self &= other } } + + forward_ref_op_assign! { impl BitAndAssign, bitand_assign for $t, $t } )+) } @@ -638,6 +640,8 @@ macro_rules! bitor_assign_impl { #[inline] fn bitor_assign(&mut self, other: $t) { *self |= other } } + + forward_ref_op_assign! { impl BitOrAssign, bitor_assign for $t, $t } )+) } @@ -683,6 +687,8 @@ macro_rules! bitxor_assign_impl { #[inline] fn bitxor_assign(&mut self, other: $t) { *self ^= other } } + + forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for $t, $t } )+) } @@ -729,6 +735,8 @@ macro_rules! shl_assign_impl { *self <<= other } } + + forward_ref_op_assign! { impl ShlAssign, shl_assign for $t, $f } ) } @@ -793,6 +801,8 @@ macro_rules! shr_assign_impl { *self >>= other } } + + forward_ref_op_assign! { impl ShrAssign, shr_assign for $t, $f } ) } diff --git a/src/libcore/ops/deref.rs b/src/libcore/ops/deref.rs index ea8dd82087849..80c48c7b28efd 100644 --- a/src/libcore/ops/deref.rs +++ b/src/libcore/ops/deref.rs @@ -18,7 +18,7 @@ /// Implementing `Deref` for smart pointers makes accessing the data behind them /// convenient, which is why they implement `Deref`. On the other hand, the /// rules regarding `Deref` and [`DerefMut`] were designed specifically to -/// accomodate smart pointers. Because of this, **`Deref` should only be +/// accommodate smart pointers. Because of this, **`Deref` should only be /// implemented for smart pointers** to avoid confusion. /// /// For similar reasons, **this trait should never fail**. Failure during @@ -40,7 +40,7 @@ /// [book]: ../../book/second-edition/ch15-02-deref.html /// [`DerefMut`]: trait.DerefMut.html /// [more]: #more-on-deref-coercion -/// [ref-deref-op]: ../../reference/expressions.html#the-dereference-operator +/// [ref-deref-op]: ../../reference/expressions/operator-expr.html#the-dereference-operator /// [ref-deref-trait]: ../../reference/the-deref-trait.html /// [type coercions]: ../../reference/type-coercions.html /// @@ -103,7 +103,7 @@ impl<'a, T: ?Sized> Deref for &'a mut T { /// Implementing `DerefMut` for smart pointers makes mutating the data behind /// them convenient, which is why they implement `DerefMut`. On the other hand, /// the rules regarding [`Deref`] and `DerefMut` were designed specifically to -/// accomodate smart pointers. Because of this, **`DerefMut` should only be +/// accommodate smart pointers. Because of this, **`DerefMut` should only be /// implemented for smart pointers** to avoid confusion. /// /// For similar reasons, **this trait should never fail**. Failure during @@ -127,7 +127,7 @@ impl<'a, T: ?Sized> Deref for &'a mut T { /// [book]: ../../book/second-edition/ch15-02-deref.html /// [`Deref`]: trait.Deref.html /// [more]: #more-on-deref-coercion -/// [ref-deref-op]: ../../reference/expressions.html#the-dereference-operator +/// [ref-deref-op]: ../../reference/expressions/operator-expr.html#the-dereference-operator /// [ref-deref-trait]: ../../reference/the-deref-trait.html /// [type coercions]: ../../reference/type-coercions.html /// diff --git a/src/libcore/ops/generator.rs b/src/libcore/ops/generator.rs index 798c182bc6e38..dc7669d195c13 100644 --- a/src/libcore/ops/generator.rs +++ b/src/libcore/ops/generator.rs @@ -14,7 +14,7 @@ /// possible return values of a generator. Currently this corresponds to either /// a suspension point (`Yielded`) or a termination point (`Complete`). #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -#[cfg_attr(not(stage0), lang = "generator_state")] +#[lang = "generator_state"] #[unstable(feature = "generator_trait", issue = "43122")] pub enum GeneratorState { /// The generator suspended with a value. @@ -70,7 +70,7 @@ pub enum GeneratorState { /// More documentation of generators can be found in the unstable book. /// /// [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 -#[cfg_attr(not(stage0), lang = "generator")] +#[lang = "generator"] #[unstable(feature = "generator_trait", issue = "43122")] #[fundamental] pub trait Generator { diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 8975b680ca7fa..70ef4487334c9 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -150,7 +150,7 @@ //! [`Sub`]: trait.Sub.html //! [`Mul`]: trait.Mul.html //! [`clone`]: ../clone/trait.Clone.html#tymethod.clone -//! [operator precedence]: ../../reference/expressions.html#operator-precedence +//! [operator precedence]: ../../reference/expressions.html#expression-precedence #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index e788b66a1ec82..81e5cb5c35045 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -15,24 +15,19 @@ /// extracting those success or failure values from an existing instance and /// creating a new instance from a success or failure value. #[unstable(feature = "try_trait", issue = "42327")] -#[cfg_attr(stage0, - rustc_on_unimplemented = "the `?` operator can only be used in a \ - function that returns `Result` \ - (or another type that implements `{Try}`)")] -#[cfg_attr(not(stage0), - rustc_on_unimplemented( - on(all( - any(from_method="from_error", from_method="from_ok"), - from_desugaring="?"), - message="the `?` operator can only be used in a \ - function that returns `Result` \ - (or another type that implements `{Try}`)", - label="cannot use the `?` operator in a function that returns `{Self}`"), - on(all(from_method="into_result", from_desugaring="?"), - message="the `?` operator can only be applied to values \ - that implement `{Try}`", - label="the `?` operator cannot be applied to type `{Self}`") -))] +#[rustc_on_unimplemented( + on(all( + any(from_method="from_error", from_method="from_ok"), + from_desugaring="?"), + message="the `?` operator can only be used in a \ + function that returns `Result` \ + (or another type that implements `{Try}`)", + label="cannot use the `?` operator in a function that returns `{Self}`"), + on(all(from_method="into_result", from_desugaring="?"), + message="the `?` operator can only be applied to values \ + that implement `{Try}`", + label="the `?` operator cannot be applied to type `{Self}`") +)] pub trait Try { /// The type of this value when viewed as successful. #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/libcore/ops/unsize.rs b/src/libcore/ops/unsize.rs index 58da290cfb694..cd896859b16bc 100644 --- a/src/libcore/ops/unsize.rs +++ b/src/libcore/ops/unsize.rs @@ -42,7 +42,7 @@ use marker::Unsize; /// [unsize]: ../marker/trait.Unsize.html /// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "coerce_unsized", issue = "27732")] -#[lang="coerce_unsized"] +#[lang = "coerce_unsized"] pub trait CoerceUnsized { // Empty. } diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 138e04c7737e0..12e6e8430562a 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -146,7 +146,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use iter::{FromIterator, FusedIterator, TrustedLen}; -use mem; +use {mem, ops}; // Note that this is not a lang item per se, but it has a hidden dependency on // `Iterator`, which is one. The compiler assumes that the `next` method of @@ -607,6 +607,41 @@ impl Option { } } + /// Returns `None` if the option is `None`, otherwise calls `predicate` + /// with the wrapped value and returns: + /// + /// - `Some(t)` if `predicate` returns `true` (where `t` is the wrapped + /// value), and + /// - `None` if `predicate` returns `false`. + /// + /// This function works similar to `Iterator::filter()`. You can imagine + /// the `Option` being an iterator over one or zero elements. `filter()` + /// lets you decide which elements to keep. + /// + /// # Examples + /// + /// ```rust + /// #![feature(option_filter)] + /// + /// fn is_even(n: &i32) -> bool { + /// n % 2 == 0 + /// } + /// + /// assert_eq!(None.filter(is_even), None); + /// assert_eq!(Some(3).filter(is_even), None); + /// assert_eq!(Some(4).filter(is_even), Some(4)); + /// ``` + #[inline] + #[unstable(feature = "option_filter", issue = "45860")] + pub fn filter bool>(self, predicate: P) -> Self { + if let Some(x) = self { + if predicate(&x) { + return Some(x) + } + } + None + } + /// Returns the option if it contains a value, otherwise returns `optb`. /// /// # Examples @@ -1123,3 +1158,29 @@ impl> FromIterator> for Option { } } } + +/// The error type that results from applying the try operator (`?`) to a `None` value. If you wish +/// to allow `x?` (where `x` is an `Option`) to be converted into your error type, you can +/// implement `impl From` for `YourErrorType`. In that case, `x?` within a function that +/// returns `Result<_, YourErrorType>` will translate a `None` value into an `Err` result. +#[unstable(feature = "try_trait", issue = "42327")] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub struct NoneError; + +#[unstable(feature = "try_trait", issue = "42327")] +impl ops::Try for Option { + type Ok = T; + type Error = NoneError; + + fn into_result(self) -> Result { + self.ok_or(NoneError) + } + + fn from_ok(v: T) -> Self { + Some(v) + } + + fn from_error(_: NoneError) -> Self { + None + } +} diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 4041a3760e5ca..5e70c8283f454 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -27,8 +27,6 @@ use nonzero::NonZero; use cmp::Ordering::{self, Less, Equal, Greater}; -// FIXME #19649: intrinsic docs don't render, so these have no docs :( - #[stable(feature = "rust1", since = "1.0.0")] pub use intrinsics::copy_nonoverlapping; @@ -56,7 +54,7 @@ pub use intrinsics::write_bytes; /// This has all the same safety problems as `ptr::read` with respect to /// invalid pointers, types, and double drops. #[stable(feature = "drop_in_place", since = "1.8.0")] -#[lang="drop_in_place"] +#[lang = "drop_in_place"] #[allow(unconditional_recursion)] pub unsafe fn drop_in_place(to_drop: *mut T) { // Code here does not matter - this is replaced by the @@ -76,7 +74,6 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_ptr_null"))] pub const fn null() -> *const T { 0 as *const T } /// Creates a null mutable raw pointer. @@ -91,7 +88,6 @@ pub const fn null() -> *const T { 0 as *const T } /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_ptr_null_mut"))] pub const fn null_mut() -> *mut T { 0 as *mut T } /// Swaps the values at two mutable locations of the same type, without @@ -230,7 +226,7 @@ pub unsafe fn replace(dest: *mut T, mut src: T) -> T { /// moves the value out of `src` without preventing further usage of `src`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `src` is not used before the data is overwritten again (e.g. with `write`, -/// `zero_memory`, or `copy_memory`). Note that `*src = foo` counts as a use +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use /// because it will attempt to drop the value previously at `*src`. /// /// The pointer must be aligned; use `read_unaligned` if that is not the case. @@ -266,7 +262,7 @@ pub unsafe fn read(src: *const T) -> T { /// moves the value out of `src` without preventing further usage of `src`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `src` is not used before the data is overwritten again (e.g. with `write`, -/// `zero_memory`, or `copy_memory`). Note that `*src = foo` counts as a use +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use /// because it will attempt to drop the value previously at `*src`. /// /// # Examples @@ -399,7 +395,7 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// moves the value out of `src` without preventing further usage of `src`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `src` is not used before the data is overwritten again (e.g. with `write`, -/// `zero_memory`, or `copy_memory`). Note that `*src = foo` counts as a use +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use /// because it will attempt to drop the value previously at `*src`. /// /// # Examples @@ -476,6 +472,11 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { impl *const T { /// Returns `true` if the pointer is null. /// + /// Note that unsized types have many possible null pointers, as only the + /// raw data pointer is considered, not their length, vtable, etc. + /// Therefore, two pointers that are null may still not compare equal to + /// each other. + /// /// # Examples /// /// Basic usage: @@ -487,8 +488,10 @@ impl *const T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is_null(self) -> bool where T: Sized { - self == null() + pub fn is_null(self) -> bool { + // Compare via a cast to a thin pointer, so fat pointers are only + // considering their "data" part for null-ness. + (self as *const u8) == null() } /// Returns `None` if the pointer is null, or else returns a reference to @@ -519,7 +522,7 @@ impl *const T { /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[inline] - pub unsafe fn as_ref<'a>(self) -> Option<&'a T> where T: Sized { + pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { if self.is_null() { None } else { @@ -553,7 +556,7 @@ impl *const T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -686,7 +689,7 @@ impl *const T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -745,7 +748,7 @@ impl *const T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -873,7 +876,7 @@ impl *const T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `self` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// The pointer must be aligned; use `read_unaligned` if that is not the case. @@ -927,7 +930,7 @@ impl *const T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `self` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// # Examples @@ -963,7 +966,7 @@ impl *const T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `self` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// # Examples @@ -1107,6 +1110,11 @@ impl *const T { impl *mut T { /// Returns `true` if the pointer is null. /// + /// Note that unsized types have many possible null pointers, as only the + /// raw data pointer is considered, not their length, vtable, etc. + /// Therefore, two pointers that are null may still not compare equal to + /// each other. + /// /// # Examples /// /// Basic usage: @@ -1118,8 +1126,10 @@ impl *mut T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is_null(self) -> bool where T: Sized { - self == null_mut() + pub fn is_null(self) -> bool { + // Compare via a cast to a thin pointer, so fat pointers are only + // considering their "data" part for null-ness. + (self as *mut u8) == null_mut() } /// Returns `None` if the pointer is null, or else returns a reference to @@ -1150,7 +1160,7 @@ impl *mut T { /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[inline] - pub unsafe fn as_ref<'a>(self) -> Option<&'a T> where T: Sized { + pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { if self.is_null() { None } else { @@ -1184,7 +1194,7 @@ impl *mut T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -1274,7 +1284,7 @@ impl *mut T { /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[inline] - pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> where T: Sized { + pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { if self.is_null() { None } else { @@ -1384,7 +1394,7 @@ impl *mut T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -1443,7 +1453,7 @@ impl *mut T { /// /// Most platforms fundamentally can't even construct such an allocation. /// For instance, no known 64-bit platform can ever serve a request - /// for 2^63 bytes due to page-table limitations or splitting the address space. + /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory @@ -1571,7 +1581,7 @@ impl *mut T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `self` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// The pointer must be aligned; use `read_unaligned` if that is not the case. @@ -1625,7 +1635,7 @@ impl *mut T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `src` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// # Examples @@ -1661,7 +1671,7 @@ impl *mut T { /// moves the value out of `self` without preventing further usage of `self`. /// If `T` is not `Copy`, then care must be taken to ensure that the value at /// `self` is not used before the data is overwritten again (e.g. with `write`, - /// `zero_memory`, or `copy_memory`). Note that `*self = foo` counts as a use + /// `write_bytes`, or `copy`). Note that `*self = foo` counts as a use /// because it will attempt to drop the value previously at `*self`. /// /// # Examples @@ -2335,7 +2345,6 @@ impl Unique { /// /// `ptr` must be non-null. #[unstable(feature = "unique", issue = "27730")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_unique_new"))] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { Unique { pointer: NonZero::new_unchecked(ptr), _marker: PhantomData } } @@ -2470,7 +2479,6 @@ impl Shared { /// /// `ptr` must be non-null. #[unstable(feature = "shared", issue = "27730")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_shared_new"))] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { Shared { pointer: NonZero::new_unchecked(ptr), _marker: PhantomData } } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index ea064ca5c39fe..db5bffced10cc 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1060,7 +1060,7 @@ unsafe impl<'a, A> TrustedLen for IterMut<'a, A> {} /// [`Result`]: enum.Result.html /// [`into_iter`]: ../iter/trait.IntoIterator.html#tymethod.into_iter /// [`IntoIterator`]: ../iter/trait.IntoIterator.html -#[derive(Debug)] +#[derive(Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { inner: Option } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index ae243f3f246a5..49c51f4f04fdc 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -16,9 +16,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -// FIXME: after next stage0, change RangeInclusive { ... } back to ..= -use ops::RangeInclusive; - // How this module is organized. // // The library infrastructure for slices is fairly messy. There's @@ -43,7 +40,7 @@ use cmp; use fmt; use intrinsics::assume; use iter::*; -use ops::{FnMut, self}; +use ops::{FnMut, Try, self}; use option::Option; use option::Option::{None, Some}; use result::Result; @@ -394,23 +391,25 @@ impl SliceExt for [T] { fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result where F: FnMut(&'a T) -> Ordering { + let s = self; + let mut size = s.len(); + if size == 0 { + return Err(0); + } let mut base = 0usize; - let mut s = self; - - loop { - let (head, tail) = s.split_at(s.len() >> 1); - if tail.is_empty() { - return Err(base) - } - match f(&tail[0]) { - Less => { - base += head.len() + 1; - s = &tail[1..]; - } - Greater => s = head, - Equal => return Ok(base + head.len()), - } + while size > 1 { + let half = size / 2; + let mid = base + half; + // mid is always in [0, size). + // mid >= 0: by definition + // mid < size: mid = size / 2 + size / 4 + size / 8 ... + let cmp = f(unsafe { s.get_unchecked(mid) }); + base = if cmp == Greater { base } else { mid }; + size -= half; } + // base is always in [0, size) because base <= mid. + let cmp = f(unsafe { s.get_unchecked(base) }); + if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } } #[inline] @@ -1047,32 +1046,32 @@ impl SliceIndex<[T]> for ops::RangeToInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - (RangeInclusive { start: 0, end: self.end }).get(slice) + (0..=self.end).get(slice) } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - (RangeInclusive { start: 0, end: self.end }).get_mut(slice) + (0..=self.end).get_mut(slice) } #[inline] unsafe fn get_unchecked(self, slice: &[T]) -> &[T] { - (RangeInclusive { start: 0, end: self.end }).get_unchecked(slice) + (0..=self.end).get_unchecked(slice) } #[inline] unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] { - (RangeInclusive { start: 0, end: self.end }).get_unchecked_mut(slice) + (0..=self.end).get_unchecked_mut(slice) } #[inline] fn index(self, slice: &[T]) -> &[T] { - (RangeInclusive { start: 0, end: self.end }).index(slice) + (0..=self.end).index(slice) } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - (RangeInclusive { start: 0, end: self.end }).index_mut(slice) + (0..=self.end).index_mut(slice) } } @@ -1166,62 +1165,37 @@ macro_rules! iterator { self.next_back() } - fn all(&mut self, mut predicate: F) -> bool - where F: FnMut(Self::Item) -> bool, - { - self.search_while(true, move |elt| { - if predicate(elt) { - SearchWhile::Continue - } else { - SearchWhile::Done(false) - } - }) - } - - fn any(&mut self, mut predicate: F) -> bool - where F: FnMut(Self::Item) -> bool, - { - !self.all(move |elt| !predicate(elt)) - } - - fn find(&mut self, mut predicate: F) -> Option - where F: FnMut(&Self::Item) -> bool, + #[inline] + fn try_fold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try { - self.search_while(None, move |elt| { - if predicate(&elt) { - SearchWhile::Done(Some(elt)) - } else { - SearchWhile::Continue + // manual unrolling is needed when there are conditional exits from the loop + let mut accum = init; + unsafe { + while ptrdistance(self.ptr, self.end) >= 4 { + accum = f(accum, $mkref!(self.ptr.post_inc()))?; + accum = f(accum, $mkref!(self.ptr.post_inc()))?; + accum = f(accum, $mkref!(self.ptr.post_inc()))?; + accum = f(accum, $mkref!(self.ptr.post_inc()))?; } - }) - } - - fn position(&mut self, mut predicate: F) -> Option - where F: FnMut(Self::Item) -> bool, - { - let mut index = 0; - self.search_while(None, move |elt| { - if predicate(elt) { - SearchWhile::Done(Some(index)) - } else { - index += 1; - SearchWhile::Continue + while self.ptr != self.end { + accum = f(accum, $mkref!(self.ptr.post_inc()))?; } - }) + } + Try::from_ok(accum) } - fn rposition(&mut self, mut predicate: F) -> Option - where F: FnMut(Self::Item) -> bool, + #[inline] + fn fold(mut self, init: Acc, mut f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, { - let mut index = self.len(); - self.rsearch_while(None, move |elt| { - index -= 1; - if predicate(elt) { - SearchWhile::Done(Some(index)) - } else { - SearchWhile::Continue - } - }) + // Let LLVM unroll this, rather than using the default + // impl that would force the manual unrolling above + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x); + } + accum } } @@ -1243,59 +1217,37 @@ macro_rules! iterator { } } - fn rfind(&mut self, mut predicate: F) -> Option - where F: FnMut(&Self::Item) -> bool, - { - self.rsearch_while(None, move |elt| { - if predicate(&elt) { - SearchWhile::Done(Some(elt)) - } else { - SearchWhile::Continue - } - }) - } - - } - - // search_while is a generalization of the internal iteration methods. - impl<'a, T> $name<'a, T> { - // search through the iterator's element using the closure `g`. - // if no element was found, return `default`. - fn search_while(&mut self, default: Acc, mut g: G) -> Acc - where Self: Sized, - G: FnMut($elem) -> SearchWhile + #[inline] + fn try_rfold(&mut self, init: B, mut f: F) -> R where + Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try { // manual unrolling is needed when there are conditional exits from the loop + let mut accum = init; unsafe { while ptrdistance(self.ptr, self.end) >= 4 { - search_while!(g($mkref!(self.ptr.post_inc()))); - search_while!(g($mkref!(self.ptr.post_inc()))); - search_while!(g($mkref!(self.ptr.post_inc()))); - search_while!(g($mkref!(self.ptr.post_inc()))); + accum = f(accum, $mkref!(self.end.pre_dec()))?; + accum = f(accum, $mkref!(self.end.pre_dec()))?; + accum = f(accum, $mkref!(self.end.pre_dec()))?; + accum = f(accum, $mkref!(self.end.pre_dec()))?; } while self.ptr != self.end { - search_while!(g($mkref!(self.ptr.post_inc()))); + accum = f(accum, $mkref!(self.end.pre_dec()))?; } } - default + Try::from_ok(accum) } - fn rsearch_while(&mut self, default: Acc, mut g: G) -> Acc - where Self: Sized, - G: FnMut($elem) -> SearchWhile + #[inline] + fn rfold(mut self, init: Acc, mut f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, { - unsafe { - while ptrdistance(self.ptr, self.end) >= 4 { - search_while!(g($mkref!(self.end.pre_dec()))); - search_while!(g($mkref!(self.end.pre_dec()))); - search_while!(g($mkref!(self.end.pre_dec()))); - search_while!(g($mkref!(self.end.pre_dec()))); - } - while self.ptr != self.end { - search_while!(g($mkref!(self.end.pre_dec()))); - } + // Let LLVM unroll this, rather than using the default + // impl that would force the manual unrolling above + let mut accum = init; + while let Some(x) = self.next_back() { + accum = f(accum, x); } - default + accum } } } @@ -1329,24 +1281,6 @@ macro_rules! make_mut_slice { }} } -// An enum used for controlling the execution of `.search_while()`. -enum SearchWhile { - // Continue searching - Continue, - // Fold is complete and will return this value - Done(T), -} - -// helper macro for search while's control flow -macro_rules! search_while { - ($e:expr) => { - match $e { - SearchWhile::Continue => { } - SearchWhile::Done(done) => return done, - } - } -} - /// Immutable slice iterator /// /// This struct is created by the [`iter`] method on [slices]. @@ -1654,7 +1588,7 @@ impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for Split<'a, T, P> where P: FnMut(&T } } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, P> Clone for Split<'a, T, P> where P: Clone + FnMut(&T) -> bool { fn clone(&self) -> Split<'a, T, P> { @@ -2093,7 +2027,7 @@ pub struct Windows<'a, T:'a> { size: usize } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Clone for Windows<'a, T> { fn clone(&self) -> Windows<'a, T> { @@ -2195,7 +2129,7 @@ pub struct Chunks<'a, T:'a> { size: usize } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Clone for Chunks<'a, T> { fn clone(&self) -> Chunks<'a, T> { @@ -2450,6 +2384,22 @@ pub unsafe fn from_raw_parts_mut<'a, T>(p: *mut T, len: usize) -> &'a mut [T] { mem::transmute(Repr { data: p, len: len }) } +/// Converts a reference to T into a slice of length 1 (without copying). +#[unstable(feature = "from_ref", issue = "45703")] +pub fn from_ref(s: &T) -> &[T] { + unsafe { + from_raw_parts(s, 1) + } +} + +/// Converts a reference to T into a slice of length 1 (without copying). +#[unstable(feature = "from_ref", issue = "45703")] +pub fn from_ref_mut(s: &mut T) -> &mut [T] { + unsafe { + from_raw_parts_mut(s, 1) + } +} + // This function is public only because there is no other way to unit test heapsort. #[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "0")] #[doc(hidden)] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 62367b051fced..be5108238fc04 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -18,7 +18,6 @@ use self::pattern::Pattern; use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use char; -use convert::TryFrom; use fmt; use iter::{Map, Cloned, FusedIterator, TrustedLen}; use iter_private::TrustedRandomAccess; @@ -77,9 +76,12 @@ pub trait FromStr: Sized { /// Parses a string `s` to return a value of this type. /// - /// If parsing succeeds, return the value inside `Ok`, otherwise + /// If parsing succeeds, return the value inside [`Ok`], otherwise /// when the string is ill-formatted return an error specific to the - /// inside `Err`. The error type is specific to implementation of the trait. + /// inside [`Err`]. The error type is specific to implementation of the trait. + /// + /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err /// /// # Examples /// @@ -1406,16 +1408,6 @@ impl<'a> DoubleEndedIterator for LinesAny<'a> { #[allow(deprecated)] impl<'a> FusedIterator for LinesAny<'a> {} -/* -Section: Comparing strings -*/ - -/// Bytewise slice equality -#[inline] -fn eq_slice(a: &str, b: &str) -> bool { - a.as_bytes() == b.as_bytes() -} - /* Section: UTF-8 validation */ @@ -1591,7 +1583,6 @@ mod traits { use cmp::Ordering; use ops; use slice::{self, SliceIndex}; - use str::eq_slice; /// Implements ordering of strings. /// @@ -1612,7 +1603,7 @@ mod traits { impl PartialEq for str { #[inline] fn eq(&self, other: &str) -> bool { - eq_slice(self, other) + self.as_bytes() == other.as_bytes() } #[inline] fn ne(&self, other: &str) -> bool { !(*self).eq(other) } @@ -2198,7 +2189,7 @@ pub trait StrExt { #[stable(feature = "core", since = "1.6.0")] fn is_empty(&self) -> bool; #[stable(feature = "core", since = "1.6.0")] - fn parse<'a, T: TryFrom<&'a str>>(&'a self) -> Result; + fn parse(&self) -> Result; } // truncate `&str` to length at most equal to `max` @@ -2518,9 +2509,7 @@ impl StrExt for str { fn is_empty(&self) -> bool { self.len() == 0 } #[inline] - fn parse<'a, T>(&'a self) -> Result where T: TryFrom<&'a str> { - T::try_from(self) - } + fn parse(&self) -> Result { FromStr::from_str(self) } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 3dd08e6971066..4c6ff4d1bb475 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -103,9 +103,8 @@ use fmt; /// /// On some platforms this function may not do anything at all. #[inline] -#[unstable(feature = "hint_core_should_pause", issue = "41196")] -pub fn hint_core_should_pause() -{ +#[stable(feature = "spin_loop_hint", since = "1.24.0")] +pub fn spin_loop_hint() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] unsafe { asm!("pause" ::: "memory" : "volatile"); @@ -119,7 +118,9 @@ pub fn hint_core_should_pause() /// A boolean type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a `bool`. +/// This type has the same in-memory representation as a [`bool`]. +/// +/// [`bool`]: ../../../std/primitive.bool.html #[cfg(target_has_atomic = "8")] #[stable(feature = "rust1", since = "1.0.0")] pub struct AtomicBool { @@ -241,16 +242,17 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_atomic_bool_new"))] pub const fn new(v: bool) -> AtomicBool { AtomicBool { v: UnsafeCell::new(v as u8) } } - /// Returns a mutable reference to the underlying `bool`. + /// Returns a mutable reference to the underlying [`bool`]. /// /// This is safe because the mutable reference guarantees that no other threads are /// concurrently accessing the atomic data. /// + /// [`bool`]: ../../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -369,7 +371,7 @@ impl AtomicBool { unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// The return value is always the previous value. If it is equal to `current`, then the value /// was updated. @@ -378,6 +380,7 @@ impl AtomicBool { /// ordering of this operation. /// /// [`Ordering`]: enum.Ordering.html + /// [`bool`]: ../../../std/primitive.bool.html /// /// # Examples /// @@ -401,7 +404,7 @@ impl AtomicBool { } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// The return value is a result indicating whether the new value was written and containing /// the previous value. On success this value is guaranteed to be equal to `current`. @@ -412,6 +415,7 @@ impl AtomicBool { /// operation fails. The failure ordering can't be [`Release`] or [`AcqRel`] and must /// be equivalent or weaker than the success ordering. /// + /// [`bool`]: ../../../std/primitive.bool.html /// [`Ordering`]: enum.Ordering.html /// [`Release`]: enum.Ordering.html#variant.Release /// [`AcqRel`]: enum.Ordering.html#variant.Release @@ -452,7 +456,7 @@ impl AtomicBool { } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// Unlike [`compare_exchange`], this function is allowed to spuriously fail even when the /// comparison succeeds, which can result in more efficient code on some platforms. The @@ -465,6 +469,7 @@ impl AtomicBool { /// failure ordering can't be [`Release`] or [`AcqRel`] and must be equivalent or /// weaker than the success ordering. /// + /// [`bool`]: ../../../std/primitive.bool.html /// [`compare_exchange`]: #method.compare_exchange /// [`Ordering`]: enum.Ordering.html /// [`Release`]: enum.Ordering.html#variant.Release @@ -650,7 +655,6 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_atomic_ptr_new"))] pub const fn new(p: *mut T) -> AtomicPtr { AtomicPtr { p: UnsafeCell::new(p) } } @@ -920,16 +924,44 @@ impl AtomicPtr { } } +#[cfg(target_has_atomic = "8")] +#[stable(feature = "atomic_bool_from", since = "1.24.0")] +impl From for AtomicBool { + #[inline] + fn from(b: bool) -> Self { Self::new(b) } +} + +#[cfg(target_has_atomic = "ptr")] +#[stable(feature = "atomic_from", since = "1.23.0")] +impl From<*mut T> for AtomicPtr { + #[inline] + fn from(p: *mut T) -> Self { Self::new(p) } +} + #[cfg(target_has_atomic = "ptr")] macro_rules! atomic_int { - ($stable:meta, $const_unstable:meta, + ($stable:meta, $stable_cxchg:meta, $stable_debug:meta, $stable_access:meta, + $s_int_type:expr, $int_ref:expr, $int_type:ident $atomic_type:ident $atomic_init:ident) => { /// An integer type which can be safely shared between threads. /// - /// This type has the same in-memory representation as the underlying integer type. + /// This type has the same in-memory representation as the underlying + /// integer type, [` + #[doc = $s_int_type] + /// `]( + #[doc = $int_ref] + /// ). For more about the differences between atomic types and + /// non-atomic types, please see the [module-level documentation]. + /// + /// Please note that examples are shared between atomic variants of + /// primitive integer types, so it's normal that they are all + /// demonstrating [`AtomicIsize`]. + /// + /// [module-level documentation]: index.html + /// [`AtomicIsize`]: struct.AtomicIsize.html #[$stable] pub struct $atomic_type { v: UnsafeCell<$int_type>, @@ -946,6 +978,12 @@ macro_rules! atomic_int { } } + #[stable(feature = "atomic_from", since = "1.23.0")] + impl From<$int_type> for $atomic_type { + #[inline] + fn from(v: $int_type) -> Self { Self::new(v) } + } + #[$stable_debug] impl fmt::Debug for $atomic_type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -971,7 +1009,6 @@ macro_rules! atomic_int { /// ``` #[inline] #[$stable] - #[cfg_attr(not(stage0), $const_unstable)] pub const fn new(v: $int_type) -> Self { $atomic_type {v: UnsafeCell::new(v)} } @@ -1335,91 +1372,91 @@ macro_rules! atomic_int { #[cfg(target_has_atomic = "8")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_i8_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "i8", "../../../std/primitive.i8.html", i8 AtomicI8 ATOMIC_I8_INIT } #[cfg(target_has_atomic = "8")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_u8_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "u8", "../../../std/primitive.u8.html", u8 AtomicU8 ATOMIC_U8_INIT } #[cfg(target_has_atomic = "16")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_i16_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "i16", "../../../std/primitive.i16.html", i16 AtomicI16 ATOMIC_I16_INIT } #[cfg(target_has_atomic = "16")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_u16_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "u16", "../../../std/primitive.u16.html", u16 AtomicU16 ATOMIC_U16_INIT } #[cfg(target_has_atomic = "32")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_i32_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "i32", "../../../std/primitive.i32.html", i32 AtomicI32 ATOMIC_I32_INIT } #[cfg(target_has_atomic = "32")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_u32_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "u32", "../../../std/primitive.u32.html", u32 AtomicU32 ATOMIC_U32_INIT } #[cfg(target_has_atomic = "64")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_i64_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "i64", "../../../std/primitive.i64.html", i64 AtomicI64 ATOMIC_I64_INIT } #[cfg(target_has_atomic = "64")] atomic_int! { unstable(feature = "integer_atomics", issue = "32976"), - rustc_const_unstable(feature = "const_atomic_u64_new"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), unstable(feature = "integer_atomics", issue = "32976"), + "u64", "../../../std/primitive.u64.html", u64 AtomicU64 ATOMIC_U64_INIT } #[cfg(target_has_atomic = "ptr")] atomic_int!{ stable(feature = "rust1", since = "1.0.0"), - rustc_const_unstable(feature = "const_atomic_isize_new"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), stable(feature = "atomic_access", since = "1.15.0"), + "isize", "../../../std/primitive.isize.html", isize AtomicIsize ATOMIC_ISIZE_INIT } #[cfg(target_has_atomic = "ptr")] atomic_int!{ stable(feature = "rust1", since = "1.0.0"), - rustc_const_unstable(feature = "const_atomic_usize_new"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), stable(feature = "atomic_access", since = "1.15.0"), + "usize", "../../../std/primitive.usize.html", usize AtomicUsize ATOMIC_USIZE_INIT } @@ -1752,7 +1789,7 @@ pub fn fence(order: Ordering) { /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [memory barriers]: https://www.kernel.org/doc/Documentation/memory-barriers.txt #[inline] -#[stable(feature = "compiler_fences", since = "1.22.0")] +#[stable(feature = "compiler_fences", since = "1.21.0")] pub fn compiler_fence(order: Ordering) { unsafe { match order { diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index 6af031dee5845..6278d5e23e0d6 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. use core::array::FixedSizeArray; +use core::convert::TryFrom; #[test] fn fixed_size_array() { @@ -26,3 +27,25 @@ fn fixed_size_array() { assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_array).len(), 0); assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_zero_sized).len(), 0); } + +#[test] +fn array_try_from() { + macro_rules! test { + ($($N:expr)+) => { + $({ + type Array = [u8; $N]; + let array: Array = [0; $N]; + let slice: &[u8] = &array[..]; + + let result = <&Array>::try_from(slice); + assert_eq!(&array, result.unwrap()); + })+ + } + } + test! { + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 + } +} diff --git a/src/libcore/tests/char.rs b/src/libcore/tests/char.rs index 7c3b90c81536e..4e10ceac878b6 100644 --- a/src/libcore/tests/char.rs +++ b/src/libcore/tests/char.rs @@ -32,7 +32,6 @@ fn test_convert() { #[test] fn test_from_str() { assert_eq!(char::from_str("a").unwrap(), 'a'); - assert_eq!(char::try_from("a").unwrap(), 'a'); assert_eq!(char::from_str("\0").unwrap(), '\0'); assert_eq!(char::from_str("\u{D7FF}").unwrap(), '\u{d7FF}'); assert!(char::from_str("").is_err()); diff --git a/src/libcore/tests/hash/mod.rs b/src/libcore/tests/hash/mod.rs index e8ea6044f5ffe..8716421b424de 100644 --- a/src/libcore/tests/hash/mod.rs +++ b/src/libcore/tests/hash/mod.rs @@ -12,6 +12,7 @@ mod sip; use std::hash::{Hash, Hasher}; use std::default::Default; +use std::rc::Rc; struct MyHasher { hash: u64, @@ -64,18 +65,28 @@ fn test_writer_hasher() { assert_eq!(hash(& s), 97 + 0xFF); let s: Box = String::from("a").into_boxed_str(); assert_eq!(hash(& s), 97 + 0xFF); + let s: Rc<&str> = Rc::new("a"); + assert_eq!(hash(&s), 97 + 0xFF); let cs: &[u8] = &[1, 2, 3]; assert_eq!(hash(& cs), 9); let cs: Box<[u8]> = Box::new([1, 2, 3]); assert_eq!(hash(& cs), 9); - - // FIXME (#18248) Add tests for hashing Rc and Rc<[T]> + let cs: Rc<[u8]> = Rc::new([1, 2, 3]); + assert_eq!(hash(& cs), 9); let ptr = 5_usize as *const i32; assert_eq!(hash(&ptr), 5); let ptr = 5_usize as *mut i32; assert_eq!(hash(&ptr), 5); + + let cs: &mut [u8] = &mut [1, 2, 3]; + let ptr = cs.as_ptr(); + let slice_ptr = cs as *const [u8]; + assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); + + let slice_ptr = cs as *mut [u8]; + assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); } struct Custom { hash: u64 } diff --git a/src/libcore/tests/hash/sip.rs b/src/libcore/tests/hash/sip.rs index 4a9657e03404a..c6dd41798f2a7 100644 --- a/src/libcore/tests/hash/sip.rs +++ b/src/libcore/tests/hash/sip.rs @@ -243,24 +243,22 @@ fn test_siphash_2_4() { t += 1; } } -#[test] #[cfg(target_arch = "arm")] + +#[test] +#[cfg(target_pointer_width = "32")] fn test_hash_usize() { let val = 0xdeadbeef_deadbeef_u64; assert!(hash(&(val as u64)) != hash(&(val as usize))); assert_eq!(hash(&(val as u32)), hash(&(val as usize))); } -#[test] #[cfg(target_arch = "x86_64")] + +#[test] +#[cfg(target_pointer_width = "64")] fn test_hash_usize() { let val = 0xdeadbeef_deadbeef_u64; assert_eq!(hash(&(val as u64)), hash(&(val as usize))); assert!(hash(&(val as u32)) != hash(&(val as usize))); } -#[test] #[cfg(target_arch = "x86")] -fn test_hash_usize() { - let val = 0xdeadbeef_deadbeef_u64; - assert!(hash(&(val as u64)) != hash(&(val as usize))); - assert_eq!(hash(&(val as u32)), hash(&(val as usize))); -} #[test] fn test_hash_idempotent() { diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 3c4ea974fc9f2..5cac5b26d88bd 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -248,6 +248,25 @@ fn test_filter_map() { assert_eq!(it.collect::>(), [0*0, 2*2, 4*4, 6*6, 8*8]); } +#[test] +fn test_filter_map_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0*0, 2*2, 4*4, 6*6, 8*8]; + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_enumerate() { let xs = [0, 1, 2, 3, 4, 5]; @@ -282,7 +301,31 @@ fn test_iterator_enumerate_nth() { #[test] fn test_iterator_enumerate_count() { let xs = [0, 1, 2, 3, 4, 5]; - assert_eq!(xs.iter().count(), 6); + assert_eq!(xs.iter().enumerate().count(), 6); +} + +#[test] +fn test_iterator_enumerate_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + // steal a couple to get an interesting offset + assert_eq!(it.next(), Some((0, &0))); + assert_eq!(it.next(), Some((1, &1))); + let i = it.fold(2, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let mut it = xs.iter().enumerate(); + assert_eq!(it.next(), Some((0, &0))); + let i = it.rfold(xs.len() - 1, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -291,6 +334,25 @@ fn test_iterator_filter_count() { assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); } +#[test] +fn test_iterator_filter_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0, 2, 4, 6, 8]; + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.rfold(ys.len(), |i, &x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_peekable() { let xs = vec![0, 1, 2, 3, 4, 5]; @@ -381,6 +443,18 @@ fn test_iterator_peekable_last() { assert_eq!(it.last(), None); } +#[test] +fn test_iterator_peekable_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + /// This is an iterator that follows the Iterator contract, /// but it is not fused. After having returned None once, it will start /// producing elements if .next() is called again. @@ -470,6 +544,26 @@ fn test_iterator_skip_while() { assert_eq!(i, ys.len()); } +#[test] +fn test_iterator_skip_while_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip_while(|&x| *x < 15); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + #[test] fn test_iterator_skip() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; @@ -566,6 +660,45 @@ fn test_iterator_skip_last() { assert_eq!(it.last(), Some(&30)); } +#[test] +fn test_iterator_skip_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + + let it = xs.iter().skip(5); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().skip(5); + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 0); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 1); + +} + #[test] fn test_iterator_take() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; @@ -661,13 +794,22 @@ fn test_iterator_flat_map_fold() { let xs = [0, 3, 6]; let ys = [1, 2, 3, 4, 5, 6, 7]; let mut it = xs.iter().flat_map(|&x| x..x+3); - it.next(); - it.next_back(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); let i = it.fold(0, |i, x| { assert_eq!(x, ys[i]); i + 1 }); assert_eq!(i, ys.len()); + + let mut it = xs.iter().flat_map(|&x| x..x+3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -684,6 +826,32 @@ fn test_inspect() { assert_eq!(&xs[..], &ys[..]); } +#[test] +fn test_inspect_fold() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + } + assert_eq!(n, xs.len()); + + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + } + assert_eq!(n, xs.len()); +} + #[test] fn test_cycle() { let cycle_len = 3; @@ -1241,6 +1409,31 @@ fn test_fuse_count() { // Can't check len now because count consumes. } +#[test] +fn test_fuse_fold() { + let xs = [0, 1, 2]; + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + + let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` + let i = it.fuse().fold(0, |i, x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + #[test] fn test_once() { let mut it = once(42); @@ -1304,3 +1497,207 @@ fn test_step_replace_no_between() { assert_eq!(x, 1); assert_eq!(y, 5); } + +#[test] +fn test_rev_try_folds() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((1..10).rev().try_fold(7, f), (1..10).try_rfold(7, f)); + assert_eq!((1..10).rev().try_rfold(7, f), (1..10).try_fold(7, f)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().rev(); + assert_eq!(iter.try_fold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(&70)); + let mut iter = a.iter().rev(); + assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(&60)); +} + +#[test] +fn test_cloned_try_folds() { + let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let f = &|acc, x| i32::checked_add(2*acc, x); + let f_ref = &|acc, &x| i32::checked_add(2*acc, x); + assert_eq!(a.iter().cloned().try_fold(7, f), a.iter().try_fold(7, f_ref)); + assert_eq!(a.iter().cloned().try_rfold(7, f), a.iter().try_rfold(7, f_ref)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_fold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(60)); + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_rfold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(70)); +} + +#[test] +fn test_chain_try_folds() { + let c = || (0..10).chain(10..20); + + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!(c().try_fold(7, f), (0..20).try_fold(7, f)); + assert_eq!(c().try_rfold(7, f), (0..20).rev().try_fold(7, f)); + + let mut iter = c(); + assert_eq!(iter.position(|x| x == 5), Some(5)); + assert_eq!(iter.next(), Some(6), "stopped in front, state Both"); + assert_eq!(iter.position(|x| x == 13), Some(6)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Back"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc+x)), Some((15..20).sum())); + + let mut iter = c().rev(); // use rev to access try_rfold + assert_eq!(iter.position(|x| x == 15), Some(4)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Both"); + assert_eq!(iter.position(|x| x == 5), Some(8)); + assert_eq!(iter.next(), Some(4), "stopped in front, state Front"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc+x)), Some((0..4).sum())); + + let mut iter = c(); + iter.by_ref().rev().nth(14); // skip the last 15, ending in state Front + assert_eq!(iter.try_fold(7, f), (0..5).try_fold(7, f)); + + let mut iter = c(); + iter.nth(14); // skip the first 15, ending in state Back + assert_eq!(iter.try_rfold(7, f), (15..20).try_rfold(7, f)); +} + +#[test] +fn test_map_try_folds() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((0..10).map(|x| x+3).try_fold(7, f), (3..13).try_fold(7, f)); + assert_eq!((0..10).map(|x| x+3).try_rfold(7, f), (3..13).try_rfold(7, f)); + + let mut iter = (0..40).map(|x| x+10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(46)); +} + +#[test] +fn test_filter_try_folds() { + fn p(&x: &i32) -> bool { 0 <= x && x < 10 } + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((-10..20).filter(p).try_fold(7, f), (0..10).try_fold(7, f)); + assert_eq!((-10..20).filter(p).try_rfold(7, f), (0..10).try_rfold(7, f)); + + let mut iter = (0..40).filter(|&x| x % 2 == 1); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(25)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(31)); +} + +#[test] +fn test_filter_map_try_folds() { + let mp = &|x| if 0 <= x && x < 10 { Some(x*2) } else { None }; + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((-9..20).filter_map(mp).try_fold(7, f), (0..10).map(|x| 2*x).try_fold(7, f)); + assert_eq!((-9..20).filter_map(mp).try_rfold(7, f), (0..10).map(|x| 2*x).try_rfold(7, f)); + + let mut iter = (0..40).filter_map(|x| if x%2 == 1 { None } else { Some(x*2 + 10) }); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(38)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(78)); +} + +#[test] +fn test_enumerate_try_folds() { + let f = &|acc, (i, x)| usize::checked_add(2*acc, x/(i+1) + i); + assert_eq!((9..18).enumerate().try_fold(7, f), (0..9).map(|i| (i, i+9)).try_fold(7, f)); + assert_eq!((9..18).enumerate().try_rfold(7, f), (0..9).map(|i| (i, i+9)).try_rfold(7, f)); + + let mut iter = (100..200).enumerate(); + let f = &|acc, (i, x)| u8::checked_add(acc, u8::checked_div(x, i as u8 + 1)?); + assert_eq!(iter.try_fold(0, f), None); + assert_eq!(iter.next(), Some((7, 107))); + assert_eq!(iter.try_rfold(0, f), None); + assert_eq!(iter.next_back(), Some((11, 111))); +} + +#[test] +fn test_peek_try_fold() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((1..20).peekable().try_fold(7, f), (1..20).try_fold(7, f)); + let mut iter = (1..20).peekable(); + assert_eq!(iter.peek(), Some(&1)); + assert_eq!(iter.try_fold(7, f), (1..20).try_fold(7, f)); + + let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.peek(), Some(&40)); +} + +#[test] +fn test_skip_while_try_fold() { + let f = &|acc, x| i32::checked_add(2*acc, x); + fn p(&x: &i32) -> bool { (x % 10) <= 5 } + assert_eq!((1..20).skip_while(p).try_fold(7, f), (6..20).try_fold(7, f)); + let mut iter = (1..20).skip_while(p); + assert_eq!(iter.nth(5), Some(11)); + assert_eq!(iter.try_fold(7, f), (12..20).try_fold(7, f)); + + let mut iter = (0..50).skip_while(|&x| (x % 20) < 15); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(23)); +} + +#[test] +fn test_take_while_folds() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((1..20).take_while(|&x| x != 10).try_fold(7, f), (1..10).try_fold(7, f)); + let mut iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.try_fold(0, |x, y| Some(x+y)), Some((1..10).sum())); + assert_eq!(iter.next(), None, "flag should be set"); + let iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.fold(0, |x, y| x+y), (1..10).sum()); + + let mut iter = (10..50).take_while(|&x| x != 40); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); +} + +#[test] +fn test_skip_try_folds() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((1..20).skip(9).try_fold(7, f), (10..20).try_fold(7, f)); + assert_eq!((1..20).skip(9).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (0..30).skip(10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(24)); +} + +#[test] +fn test_take_try_folds() { + let f = &|acc, x| i32::checked_add(2*acc, x); + assert_eq!((10..30).take(10).try_fold(7, f), (10..20).try_fold(7, f)); + //assert_eq!((10..30).take(10).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (10..30).take(20); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + //assert_eq!(iter.try_rfold(0, i8::checked_add), None); + //assert_eq!(iter.next_back(), Some(24)); +} + +#[test] +fn test_flat_map_try_folds() { + let f = &|acc, x| i32::checked_add(acc*2/3, x); + let mr = &|x| (5*x)..(5*x + 5); + assert_eq!((0..10).flat_map(mr).try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).flat_map(mr).try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).flat_map(mr); + iter.next(); iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).flat_map(|x| (4*x)..(4*x + 4)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 47995597a0a91..0e445cdac358a 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -24,9 +24,10 @@ #![feature(i128_type)] #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] +#![feature(iterator_try_fold)] #![feature(iter_rfind)] +#![feature(iter_rfold)] #![feature(nonzero)] -#![feature(rand)] #![feature(raw)] #![feature(refcell_replace_swap)] #![feature(sip_hash_13)] @@ -38,15 +39,11 @@ #![feature(test)] #![feature(trusted_len)] #![feature(try_from)] +#![feature(try_trait)] #![feature(unique)] -#![feature(const_atomic_bool_new)] -#![feature(const_atomic_usize_new)] -#![feature(const_atomic_isize_new)] - extern crate core; extern crate test; -extern crate rand; mod any; mod array; diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index 86e59c736ba4a..f55a1c81463f7 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -121,3 +121,19 @@ fn test_transmute() { } } +#[test] +#[allow(dead_code)] +fn test_discriminant_send_sync() { + enum Regular { + A, + B(i32) + } + enum NotSendSync { + A(*const i32) + } + + fn is_send_sync() { } + + is_send_sync::>(); + is_send_sync::>(); +} diff --git a/src/libcore/tests/num/flt2dec/estimator.rs b/src/libcore/tests/num/flt2dec/estimator.rs index 0bca616ea9abc..857aae72c8a5b 100644 --- a/src/libcore/tests/num/flt2dec/estimator.rs +++ b/src/libcore/tests/num/flt2dec/estimator.rs @@ -8,11 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// FIXME https://github.com/kripken/emscripten/issues/4563 -// NB we have to actually not compile this test to avoid -// an undefined symbol error -#![cfg(not(target_os = "emscripten"))] - use core::num::flt2dec::estimator::*; #[test] diff --git a/src/libcore/tests/num/flt2dec/mod.rs b/src/libcore/tests/num/flt2dec/mod.rs index 0f4d19e709257..ef0178815f98b 100644 --- a/src/libcore/tests/num/flt2dec/mod.rs +++ b/src/libcore/tests/num/flt2dec/mod.rs @@ -9,10 +9,7 @@ // except according to those terms. use std::prelude::v1::*; -use std::{str, mem, i16, f32, f64, fmt}; -use std::__rand as rand; -use rand::{Rand, XorShiftRng}; -use rand::distributions::{IndependentSample, Range}; +use std::{str, i16, f32, f64, fmt}; use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded}; use core::num::flt2dec::{MAX_SIG_DIGITS, round_up, Part, Formatted, Sign}; @@ -464,87 +461,6 @@ pub fn more_shortest_sanity_test(mut f: F) where F: FnMut(&Decoded, &mut [u8] exp: 0, inclusive: false} => b"99999999999999999", 17); } -fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), - V: FnMut(usize) -> Decoded { - assert!(k <= 1024); - - let mut npassed = 0; // f(x) = Some(g(x)) - let mut nignored = 0; // f(x) = None - - for i in 0..n { - if (i & 0xfffff) == 0 { - println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})", - i, n, nignored, npassed, i - nignored - npassed); - } - - let decoded = v(i); - let mut buf1 = [0; 1024]; - if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { - let mut buf2 = [0; 1024]; - let (len2, e2) = g(&decoded, &mut buf2[..k]); - if e1 == e2 && &buf1[..len1] == &buf2[..len2] { - npassed += 1; - } else { - println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}", - i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1, - str::from_utf8(&buf2[..len2]).unwrap(), e2); - } - } else { - nignored += 1; - } - } - println!("{}({}): done, ignored={} passed={} failed={}", - func, k, nignored, npassed, n - nignored - npassed); - assert!(nignored + npassed == n, - "{}({}): {} out of {} values returns an incorrect value!", - func, k, n - nignored - npassed, n); - (npassed, nignored) -} - -pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); - let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000); - iterate("f32_random_equivalence_test", k, n, f, g, |_| { - let i: u32 = f32_range.ind_sample(&mut rng); - let x: f32 = unsafe {mem::transmute(i)}; - decode_finite(x) - }); -} - -pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); - let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); - iterate("f64_random_equivalence_test", k, n, f, g, |_| { - let i: u64 = f64_range.ind_sample(&mut rng); - let x: f64 = unsafe {mem::transmute(i)}; - decode_finite(x) - }); -} - -pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, - // so why not simply testing all of them? - // - // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), - // but with `-C opt-level=3 -C lto` this only takes about an hour or so. - - // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges - let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", - k, 0x7f7f_ffff, f, g, |i: usize| { - let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; - decode_finite(x) - }); - assert_eq!((npassed, nignored), (2121451881, 17643158)); -} - fn to_string_with_parts(mut f: F) -> String where F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a> { let mut buf = [0; 1024]; diff --git a/src/libcore/tests/num/flt2dec/strategy/grisu.rs b/src/libcore/tests/num/flt2dec/strategy/grisu.rs index 17fb99bcc9224..286b39d8cf3b3 100644 --- a/src/libcore/tests/num/flt2dec/strategy/grisu.rs +++ b/src/libcore/tests/num/flt2dec/strategy/grisu.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::i16; use super::super::*; use core::num::flt2dec::strategy::grisu::*; @@ -46,35 +45,6 @@ fn shortest_sanity_test() { more_shortest_sanity_test(format_shortest); } -#[test] -fn shortest_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); - f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); -} - -#[test] #[ignore] // it is too expensive -fn shortest_f32_exhaustive_equivalence_test() { - // it is hard to directly test the optimality of the output, but we can at least test if - // two different algorithms agree to each other. - // - // this reports the progress and the number of f32 values returned `None`. - // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: - // `done, ignored=17643158 passed=2121451881 failed=0`. - - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); -} - -#[test] #[ignore] // it is too expensive -fn shortest_f64_hard_random_equivalence_test() { - // this again probably has to use appropriate rustc flags. - - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f64_random_equivalence_test(format_shortest_opt, fallback, - MAX_SIG_DIGITS, 100_000_000); -} - #[test] fn exact_sanity_test() { // See comments in dragon.rs's exact_sanity_test for why this test is @@ -85,24 +55,6 @@ fn exact_sanity_test() { f32_exact_sanity_test(format_exact); } -#[test] -fn exact_f32_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; - for k in 1..21 { - f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), - |d, buf| fallback(d, buf, i16::MIN), k, 1_000); - } -} - -#[test] -fn exact_f64_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; - for k in 1..21 { - f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), - |d, buf| fallback(d, buf, i16::MIN), k, 1_000); - } -} - #[test] fn test_to_shortest_str() { to_shortest_str_test(format_shortest); diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index 400d53ce51a08..7eb5ff9885777 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::convert::TryFrom; +use core::convert::{TryFrom, TryInto}; use core::cmp::PartialEq; use core::fmt::Debug; use core::marker::Copy; +use core::num::TryFromIntError; use core::ops::{Add, Sub, Mul, Div, Rem}; use core::option::Option; use core::option::Option::{Some, None}; @@ -134,6 +135,13 @@ fn test_empty() { assert_eq!("".parse::().ok(), None); } +#[test] +fn test_infallible_try_from_int_error() { + let func = |x: i8| -> Result { Ok(x.try_into()?) }; + + assert!(func(0).is_ok()); +} + macro_rules! test_impl_from { ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs index 6bac55575fb18..22109e28edd9b 100644 --- a/src/libcore/tests/option.rs +++ b/src/libcore/tests/option.rs @@ -270,3 +270,30 @@ fn test_cloned() { assert_eq!(opt_ref_ref.clone().cloned(), Some(&val)); assert_eq!(opt_ref_ref.cloned().cloned(), Some(1)); } + +#[test] +fn test_try() { + fn try_option_some() -> Option { + let val = Some(1)?; + Some(val) + } + assert_eq!(try_option_some(), Some(1)); + + fn try_option_none() -> Option { + let val = None?; + Some(val) + } + assert_eq!(try_option_none(), None); + + fn try_option_ok() -> Result { + let val = Some(1)?; + Ok(val) + } + assert_eq!(try_option_ok(), Ok(1)); + + fn try_option_err() -> Result { + let val = None?; + Ok(val) + } + assert_eq!(try_option_err(), Err(NoneError)); +} diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs index c2d53840f8f57..98436f0e1d1cd 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -62,6 +62,39 @@ fn test_is_null() { let mq = unsafe { mp.offset(1) }; assert!(!mq.is_null()); + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let cs: *const [u8] = s; + assert!(!cs.is_null()); + + let ms: *mut [u8] = s; + assert!(!ms.is_null()); + + let cz: *const [u8] = &[]; + assert!(!cz.is_null()); + + let mz: *mut [u8] = &mut []; + assert!(!mz.is_null()); + + let ncs: *const [u8] = null::<[u8; 3]>(); + assert!(ncs.is_null()); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert!(nms.is_null()); + + // Pointers to unsized types -- trait objects + let ci: *const ToString = &3; + assert!(!ci.is_null()); + + let mi: *mut ToString = &mut 3; + assert!(!mi.is_null()); + + let nci: *const ToString = null::(); + assert!(nci.is_null()); + + let nmi: *mut ToString = null_mut::(); + assert!(nmi.is_null()); } #[test] @@ -85,6 +118,39 @@ fn test_as_ref() { let p = &u as *const isize; assert_eq!(p.as_ref().unwrap(), &2); } + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let cs: *const [u8] = s; + assert_eq!(cs.as_ref(), Some(&*s)); + + let ms: *mut [u8] = s; + assert_eq!(ms.as_ref(), Some(&*s)); + + let cz: *const [u8] = &[]; + assert_eq!(cz.as_ref(), Some(&[][..])); + + let mz: *mut [u8] = &mut []; + assert_eq!(mz.as_ref(), Some(&[][..])); + + let ncs: *const [u8] = null::<[u8; 3]>(); + assert_eq!(ncs.as_ref(), None); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert_eq!(nms.as_ref(), None); + + // Pointers to unsized types -- trait objects + let ci: *const ToString = &3; + assert!(ci.as_ref().is_some()); + + let mi: *mut ToString = &mut 3; + assert!(mi.as_ref().is_some()); + + let nci: *const ToString = null::(); + assert!(nci.as_ref().is_none()); + + let nmi: *mut ToString = null_mut::(); + assert!(nmi.as_ref().is_none()); } } @@ -103,6 +169,24 @@ fn test_as_mut() { let p = &mut u as *mut isize; assert!(p.as_mut().unwrap() == &mut 2); } + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let ms: *mut [u8] = s; + assert_eq!(ms.as_mut(), Some(s)); + + let mz: *mut [u8] = &mut []; + assert_eq!(mz.as_mut(), Some(&mut [][..])); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert_eq!(nms.as_mut(), None); + + // Pointers to unsized types -- trait objects + let mi: *mut ToString = &mut 3; + assert!(mi.as_mut().is_some()); + + let nmi: *mut ToString = null_mut::(); + assert!(nmi.as_mut().is_none()); } } diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs index 4c5f19dee1293..ce41bde8342ed 100644 --- a/src/libcore/tests/result.rs +++ b/src/libcore/tests/result.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::option::*; + fn op1() -> Result { Ok(666) } fn op2() -> Result { Err("sadface") } @@ -202,3 +204,30 @@ pub fn test_unwrap_or_default() { assert_eq!(op1().unwrap_or_default(), 666); assert_eq!(op2().unwrap_or_default(), 0); } + +#[test] +fn test_try() { + fn try_result_some() -> Option { + let val = Ok(1)?; + Some(val) + } + assert_eq!(try_result_some(), Some(1)); + + fn try_result_none() -> Option { + let val = Err(NoneError)?; + Some(val) + } + assert_eq!(try_result_none(), None); + + fn try_result_ok() -> Result { + let val = Ok(1)?; + Ok(val) + } + assert_eq!(try_result_ok(), Ok(1)); + + fn try_result_err() -> Result { + let val = Err(1)?; + Ok(val) + } + assert_eq!(try_result_err(), Err(1)); +} diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 8c31d2e83d352..fa4c2e9b3736f 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -8,29 +8,63 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::cmp::Ordering::{Equal, Greater, Less}; -use core::slice::heapsort; use core::result::Result::{Ok, Err}; -use rand::{Rng, XorShiftRng}; #[test] fn test_binary_search() { + let b: [i32; 0] = []; + assert_eq!(b.binary_search(&5), Err(0)); + + let b = [4]; + assert_eq!(b.binary_search(&3), Err(0)); + assert_eq!(b.binary_search(&4), Ok(0)); + assert_eq!(b.binary_search(&5), Err(1)); + let b = [1, 2, 4, 6, 8, 9]; - assert!(b.binary_search_by(|v| v.cmp(&6)) == Ok(3)); - assert!(b.binary_search_by(|v| v.cmp(&5)) == Err(3)); - let b = [1, 2, 4, 6, 7, 8, 9]; - assert!(b.binary_search_by(|v| v.cmp(&6)) == Ok(3)); - assert!(b.binary_search_by(|v| v.cmp(&5)) == Err(3)); - let b = [1, 2, 4, 6, 8, 9]; - assert!(b.binary_search_by(|v| v.cmp(&8)) == Ok(4)); - assert!(b.binary_search_by(|v| v.cmp(&7)) == Err(4)); + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&7), Err(4)); + assert_eq!(b.binary_search(&8), Ok(4)); + + let b = [1, 2, 4, 5, 6, 8]; + assert_eq!(b.binary_search(&9), Err(6)); + let b = [1, 2, 4, 6, 7, 8, 9]; - assert!(b.binary_search_by(|v| v.cmp(&8)) == Ok(5)); + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&8), Ok(5)); + let b = [1, 2, 4, 5, 6, 8, 9]; - assert!(b.binary_search_by(|v| v.cmp(&7)) == Err(5)); - assert!(b.binary_search_by(|v| v.cmp(&0)) == Err(0)); - let b = [1, 2, 4, 5, 6, 8]; - assert!(b.binary_search_by(|v| v.cmp(&9)) == Err(6)); + assert_eq!(b.binary_search(&7), Err(5)); + assert_eq!(b.binary_search(&0), Err(0)); + + let b = [1, 3, 3, 3, 7]; + assert_eq!(b.binary_search(&0), Err(0)); + assert_eq!(b.binary_search(&1), Ok(0)); + assert_eq!(b.binary_search(&2), Err(1)); + assert!(match b.binary_search(&3) { Ok(1...3) => true, _ => false }); + assert!(match b.binary_search(&3) { Ok(1...3) => true, _ => false }); + assert_eq!(b.binary_search(&4), Err(4)); + assert_eq!(b.binary_search(&5), Err(4)); + assert_eq!(b.binary_search(&6), Err(4)); + assert_eq!(b.binary_search(&7), Ok(4)); + assert_eq!(b.binary_search(&8), Err(5)); +} + +#[test] +// Test implementation specific behavior when finding equivalent elements. +// It is ok to break this test but when you do a crater run is highly advisable. +fn test_binary_search_implementation_details() { + let b = [1, 1, 2, 2, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(1)); + assert_eq!(b.binary_search(&2), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(6)); + let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(4)); + assert_eq!(b.binary_search(&3), Ok(8)); + let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(8)); } #[test] @@ -238,6 +272,23 @@ fn test_find_rfind() { assert_eq!(v.iter().rfind(|&&x| x <= 3), Some(&3)); } +#[test] +fn test_iter_folds() { + let a = [1, 2, 3, 4, 5]; // len>4 so the unroll is used + assert_eq!(a.iter().fold(0, |acc, &x| 2*acc + x), 57); + assert_eq!(a.iter().rfold(0, |acc, &x| 2*acc + x), 129); + let fold = |acc: i32, &x| acc.checked_mul(2)?.checked_add(x); + assert_eq!(a.iter().try_fold(0, &fold), Some(57)); + assert_eq!(a.iter().try_rfold(0, &fold), Some(129)); + + // short-circuiting try_fold, through other methods + let a = [0, 1, 2, 3, 5, 5, 5, 7, 8, 9]; + let mut iter = a.iter(); + assert_eq!(iter.position(|&x| x == 3), Some(3)); + assert_eq!(iter.rfind(|&&x| x == 5), Some(&5)); + assert_eq!(iter.len(), 2); +} + #[test] fn test_rotate() { const N: usize = 600; @@ -253,68 +304,3 @@ fn test_rotate() { assert_eq!(a[(i+k)%N], i); } } - -#[test] -fn sort_unstable() { - let mut v = [0; 600]; - let mut tmp = [0; 600]; - let mut rng = XorShiftRng::new_unseeded(); - - for len in (2..25).chain(500..510) { - let v = &mut v[0..len]; - let tmp = &mut tmp[0..len]; - - for &modulus in &[5, 10, 100, 1000] { - for _ in 0..100 { - for i in 0..len { - v[i] = rng.gen::() % modulus; - } - - // Sort in default order. - tmp.copy_from_slice(v); - tmp.sort_unstable(); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in ascending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| a.cmp(b)); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in descending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| b.cmp(a)); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - - // Test heapsort using `<` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a < b); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Test heapsort using `>` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a > b); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - } - } - } - - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap()); - v.sort_unstable(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - - // Should not panic. - [0i32; 0].sort_unstable(); - [(); 10].sort_unstable(); - [(); 100].sort_unstable(); - - let mut v = [0xDEADBEEFu64]; - v.sort_unstable(); - assert!(v == [0xDEADBEEF]); -} diff --git a/src/libcore/unit.rs b/src/libcore/unit.rs new file mode 100644 index 0000000000000..087ddf9688ab7 --- /dev/null +++ b/src/libcore/unit.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use iter::FromIterator; + +/// Collapses all unit items from an iterator into one. +/// +/// This is more useful when combined with higher-level abstractions, like +/// collecting to a `Result<(), E>` where you only care about errors: +/// +/// ``` +/// use std::io::*; +/// let data = vec![1, 2, 3, 4, 5]; +/// let res: Result<()> = data.iter() +/// .map(|x| writeln!(stdout(), "{}", x)) +/// .collect(); +/// assert!(res.is_ok()); +/// ``` +#[stable(feature = "unit_from_iter", since = "1.23.0")] +impl FromIterator<()> for () { + fn from_iter>(iter: I) -> Self { + iter.into_iter().for_each(|()| {}) + } +} diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 24430b2e377f5..44cdb5e8a3676 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -73,7 +73,9 @@ pub struct FormatSpec<'a> { /// Enum describing where an argument for a format can be located. #[derive(Copy, Clone, PartialEq)] pub enum Position<'a> { - /// The argument is located at a specific index. + /// The arugment is implied to be located at an index + ArgumentImplicitlyIs(usize), + /// The argument is located at a specific index given in the format ArgumentIs(usize), /// The argument has a name. ArgumentNamed(&'a str), @@ -275,7 +277,7 @@ impl<'a> Parser<'a> { None => { let i = self.curarg; self.curarg += 1; - ArgumentIs(i) + ArgumentImplicitlyIs(i) } }; @@ -517,7 +519,7 @@ mod tests { fn format_nothing() { same("{}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: fmtdflt(), })]); } @@ -595,7 +597,7 @@ mod tests { fn format_counts() { same("{:10s}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, @@ -607,7 +609,7 @@ mod tests { })]); same("{:10$.10s}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, @@ -619,7 +621,7 @@ mod tests { })]); same("{:.*s}", &[NextArgument(Argument { - position: ArgumentIs(1), + position: ArgumentImplicitlyIs(1), format: FormatSpec { fill: None, align: AlignUnknown, @@ -631,7 +633,7 @@ mod tests { })]); same("{:.10$s}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, @@ -643,7 +645,7 @@ mod tests { })]); same("{:a$.b$s}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, @@ -658,7 +660,7 @@ mod tests { fn format_flags() { same("{:-}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, @@ -670,7 +672,7 @@ mod tests { })]); same("{:+#}", &[NextArgument(Argument { - position: ArgumentIs(0), + position: ArgumentImplicitlyIs(0), format: FormatSpec { fill: None, align: AlignUnknown, diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs index a0eacc817ca81..81fa0374f549e 100644 --- a/src/libgetopts/lib.rs +++ b/src/libgetopts/lib.rs @@ -731,7 +731,9 @@ pub fn usage(brief: &str, opts: &[OptGroup]) -> String { } } - // FIXME: #5516 should be graphemes not codepoints + // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7) + // should be graphemes not codepoints + // // here we just need to indent the start of the description let rowlen = row.chars().count(); if rowlen < 24 { @@ -749,14 +751,17 @@ pub fn usage(brief: &str, opts: &[OptGroup]) -> String { desc_normalized_whitespace.push(' '); } - // FIXME: #5516 should be graphemes not codepoints + // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7) + // should be graphemes not codepoints let mut desc_rows = Vec::new(); each_split_within(&desc_normalized_whitespace[..], 54, |substr| { desc_rows.push(substr.to_owned()); true }); - // FIXME: #5516 should be graphemes not codepoints + // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7) + // should be graphemes not codepoints + // // wrapped description row.push_str(&desc_rows.join(&desc_sep[..])); diff --git a/src/liblibc b/src/liblibc index 44e4018e1a377..1a2f9639f8d29 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 44e4018e1a37716286ec98cb5b7dd7d33ecaf940 +Subproject commit 1a2f9639f8d293cefbe050053a574decbfe863f7 diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 8be6f6470231d..29a9e1aadaf3c 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -20,13 +20,13 @@ html_root_url = "https://doc.rust-lang.org/nightly/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] #![deny(warnings)] - -#![feature(staged_api)] - #![panic_runtime] +#![allow(unused_features)] + +#![feature(core_intrinsics)] +#![feature(libc)] #![feature(panic_runtime)] -#![cfg_attr(unix, feature(libc))] -#![cfg_attr(any(target_os = "redox", windows), feature(core_intrinsics))] +#![feature(staged_api)] // Rust's "try" function, but if we're aborting on panics we just call the // function as there's nothing else we need to do here. @@ -59,7 +59,9 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { libc::abort(); } - #[cfg(any(target_os = "redox", windows))] + #[cfg(any(target_os = "redox", + windows, + all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn abort() -> ! { core::intrinsics::abort(); } @@ -92,7 +94,6 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { // binaries, but it should never be called as we don't link in an unwinding // runtime at all. pub mod personalities { - #[no_mangle] #[cfg(not(all(target_os = "windows", target_env = "gnu", diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 558286f4ec070..6b8da7a51ceb8 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -34,9 +34,7 @@ #![feature(core_intrinsics)] #![feature(lang_items)] #![feature(libc)] -#![cfg_attr(not(any(target_env = "msvc", - all(windows, target_arch = "x86_64", target_env = "gnu"))), - feature(panic_unwind))] +#![feature(panic_unwind)] #![feature(raw)] #![feature(staged_api)] #![feature(unwind_attributes)] @@ -80,6 +78,10 @@ mod imp; #[path = "emcc.rs"] mod imp; +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +#[path = "wasm32.rs"] +mod imp; + mod dwarf; mod windows; diff --git a/src/libpanic_unwind/wasm32.rs b/src/libpanic_unwind/wasm32.rs new file mode 100644 index 0000000000000..8aed61b3c385a --- /dev/null +++ b/src/libpanic_unwind/wasm32.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unwinding for wasm32 +//! +//! Right now we don't support this, so this is just stubs + +use alloc::boxed::Box; +use core::any::Any; +use core::intrinsics; + +pub fn payload() -> *mut u8 { + 0 as *mut u8 +} + +pub unsafe fn cleanup(_ptr: *mut u8) -> Box { + intrinsics::abort() +} + +pub unsafe fn panic(_data: Box) -> u32 { + intrinsics::abort() +} diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 2c540c8de8fc8..4a6841aedca12 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -50,6 +50,7 @@ mod diagnostic; pub use diagnostic::{Diagnostic, Level}; use std::{ascii, fmt, iter}; +use std::rc::Rc; use std::str::FromStr; use syntax::ast; @@ -58,7 +59,7 @@ use syntax::parse::{self, token}; use syntax::symbol::Symbol; use syntax::tokenstream; use syntax_pos::DUMMY_SP; -use syntax_pos::SyntaxContext; +use syntax_pos::{FileMap, Pos, SyntaxContext}; use syntax_pos::hygiene::Mark; /// The main type provided by this crate, representing an abstract stream of @@ -94,7 +95,7 @@ impl FromStr for TokenStream { // notify the expansion info that it is unhygienic let mark = Mark::fresh(mark); mark.set_expn_info(expn_info); - let span = call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark)); + let span = call_site.with_ctxt(call_site.ctxt().apply_mark(mark)); let stream = parse::parse_stream_from_source_str(name, src, sess, Some(span)); Ok(__internal::token_stream_wrap(stream)) }) @@ -173,12 +174,13 @@ impl TokenStream { /// A region of source code, along with macro expansion information. #[unstable(feature = "proc_macro", issue = "38356")] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Span(syntax_pos::Span); -#[unstable(feature = "proc_macro", issue = "38356")] -impl Default for Span { - fn default() -> Span { +impl Span { + /// A span that resolves at the macro definition site. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn def_site() -> Span { ::__internal::with_sess(|(_, mark)| { let call_site = mark.expn_info().unwrap().call_site; Span(call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark))) @@ -190,7 +192,7 @@ impl Default for Span { /// This is needed to implement a custom quoter. #[unstable(feature = "proc_macro", issue = "38356")] pub fn quote_span(span: Span) -> TokenStream { - TokenStream(quote::Quote::quote(&span.0)) + quote::Quote::quote(span) } macro_rules! diagnostic_method { @@ -211,12 +213,132 @@ impl Span { ::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site)) } + /// The original source file into which this span points. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn source_file(&self) -> SourceFile { + SourceFile { + filemap: __internal::lookup_char_pos(self.0.lo()).file, + } + } + + /// Get the starting line/column in the source file for this span. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn start(&self) -> LineColumn { + let loc = __internal::lookup_char_pos(self.0.lo()); + LineColumn { + line: loc.line, + column: loc.col.to_usize() + } + } + + /// Get the ending line/column in the source file for this span. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn end(&self) -> LineColumn { + let loc = __internal::lookup_char_pos(self.0.hi()); + LineColumn { + line: loc.line, + column: loc.col.to_usize() + } + } + + /// Create a new span encompassing `self` and `other`. + /// + /// Returns `None` if `self` and `other` are from different files. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn join(&self, other: Span) -> Option { + let self_loc = __internal::lookup_char_pos(self.0.lo()); + let other_loc = __internal::lookup_char_pos(self.0.lo()); + + if self_loc.file.name != other_loc.file.name { return None } + + Some(Span(self.0.to(other.0))) + } + diagnostic_method!(error, Level::Error); diagnostic_method!(warning, Level::Warning); diagnostic_method!(note, Level::Note); diagnostic_method!(help, Level::Help); } +/// A line-column pair representing the start or end of a `Span`. +#[unstable(feature = "proc_macro", issue = "38356")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineColumn { + /// The 1-indexed line in the source file on which the span starts or ends (inclusive). + line: usize, + /// The 0-indexed column (in UTF-8 characters) in the source file on which + /// the span starts or ends (inclusive). + column: usize +} + +/// The source file of a given `Span`. +#[unstable(feature = "proc_macro", issue = "38356")] +#[derive(Clone)] +pub struct SourceFile { + filemap: Rc, +} + +impl SourceFile { + /// Get the path to this source file as a string. + /// + /// ### Note + /// If the code span associated with this `SourceFile` was generated by an external macro, this + /// may not be an actual path on the filesystem. Use [`is_real`] to check. + /// + /// Also note that even if `is_real` returns `true`, if `-Z remap-path-prefix-*` was passed on + /// the command line, the path as given may not actually be valid. + /// + /// [`is_real`]: #method.is_real + # [unstable(feature = "proc_macro", issue = "38356")] + pub fn as_str(&self) -> &str { + &self.filemap.name + } + + /// Returns `true` if this source file is a real source file, and not generated by an external + /// macro's expansion. + # [unstable(feature = "proc_macro", issue = "38356")] + pub fn is_real(&self) -> bool { + // This is a hack until intercrate spans are implemented and we can have real source files + // for spans generated in external macros. + // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 + self.filemap.is_real_file() + } +} + +#[unstable(feature = "proc_macro", issue = "38356")] +impl AsRef for SourceFile { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +#[unstable(feature = "proc_macro", issue = "38356")] +impl fmt::Debug for SourceFile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SourceFile") + .field("path", &self.as_str()) + .field("is_real", &self.is_real()) + .finish() + } +} + +#[unstable(feature = "proc_macro", issue = "38356")] +impl PartialEq for SourceFile { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.filemap, &other.filemap) + } +} + +#[unstable(feature = "proc_macro", issue = "38356")] +impl Eq for SourceFile {} + +#[unstable(feature = "proc_macro", issue = "38356")] +impl PartialEq for SourceFile { + fn eq(&self, other: &str) -> bool { + self.as_ref() == other + } +} + /// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`). #[unstable(feature = "proc_macro", issue = "38356")] #[derive(Clone, Debug)] @@ -230,7 +352,7 @@ pub struct TokenTree { #[unstable(feature = "proc_macro", issue = "38356")] impl From for TokenTree { fn from(kind: TokenNode) -> TokenTree { - TokenTree { span: Span::default(), kind: kind } + TokenTree { span: Span::def_site(), kind: kind } } } @@ -367,7 +489,7 @@ impl Literal { pub fn string(string: &str) -> Literal { let mut escaped = String::new(); for ch in string.chars() { - escaped.extend(ch.escape_unicode()); + escaped.extend(ch.escape_debug()); } Literal(token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None)) } @@ -607,7 +729,7 @@ impl TokenTree { #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] pub mod __internal { - pub use quote::{Quoter, __rt}; + pub use quote::{LiteralKind, Quoter, unquote}; use std::cell::Cell; @@ -618,10 +740,14 @@ pub mod __internal { use syntax::parse::{self, ParseSess}; use syntax::parse::token::{self, Token}; use syntax::tokenstream; - use syntax_pos::DUMMY_SP; + use syntax_pos::{BytePos, Loc, DUMMY_SP}; use super::{TokenStream, LexError}; + pub fn lookup_char_pos(pos: BytePos) -> Loc { + with_sess(|(sess, _)| sess.codemap().lookup_char_pos(pos)) + } + pub fn new_token_stream(item: P) -> TokenStream { let token = Token::interpolated(token::NtItem(item)); TokenStream(tokenstream::TokenTree::Token(DUMMY_SP, token).into()) diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs index 8c1f6bfc11a5f..8b5add1a0f0d7 100644 --- a/src/libproc_macro/quote.rs +++ b/src/libproc_macro/quote.rs @@ -11,253 +11,255 @@ //! # Quasiquoter //! This file contains the implementation internals of the quasiquoter provided by `quote!`. -//! This quasiquoter uses macros 2.0 hygiene to reliably use items from `__rt`, -//! including re-exported API `libsyntax`, to build a `syntax::tokenstream::TokenStream` -//! and wrap it into a `proc_macro::TokenStream`. +//! This quasiquoter uses macros 2.0 hygiene to reliably access +//! items from `proc_macro`, to build a `proc_macro::TokenStream`. + +use {Delimiter, Literal, Spacing, Span, Term, TokenNode, TokenStream, TokenTree}; -use syntax::ast::Ident; use syntax::ext::base::{ExtCtxt, ProcMacro}; -use syntax::parse::token::{self, Token, Lit}; -use syntax::symbol::Symbol; -use syntax::tokenstream::{Delimited, TokenTree, TokenStream, TokenStreamBuilder}; -use syntax_pos::{DUMMY_SP, Span}; -use syntax_pos::hygiene::SyntaxContext; +use syntax::parse::token; +use syntax::tokenstream; pub struct Quoter; -pub mod __rt { - pub use syntax::ast::Ident; - pub use syntax::parse::token; - pub use syntax::symbol::Symbol; - pub use syntax::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree, Delimited}; - pub use super::{ctxt, span}; - - pub fn unquote + Clone>(tokens: &T) -> TokenStream { - T::into(tokens.clone()).0 - } -} - -pub fn ctxt() -> SyntaxContext { - ::__internal::with_sess(|(_, mark)| SyntaxContext::empty().apply_mark(mark)) -} - -pub fn span() -> Span { - ::Span::default().0 +pub fn unquote + Clone>(tokens: &T) -> TokenStream { + T::into(tokens.clone()) } pub trait Quote { - fn quote(&self) -> TokenStream; + fn quote(self) -> TokenStream; } macro_rules! quote_tok { - (,) => { Token::Comma }; - (.) => { Token::Dot }; - (:) => { Token::Colon }; - (::) => { Token::ModSep }; - (!) => { Token::Not }; - (<) => { Token::Lt }; - (>) => { Token::Gt }; - (_) => { Token::Underscore }; - (0) => { Token::Literal(token::Lit::Integer(Symbol::intern("0")), None) }; - (&) => { Token::BinOp(token::And) }; - ($i:ident) => { Token::Ident(Ident { name: Symbol::intern(stringify!($i)), ctxt: ctxt() }) }; + (,) => { TokenNode::Op(',', Spacing::Alone) }; + (.) => { TokenNode::Op('.', Spacing::Alone) }; + (:) => { TokenNode::Op(':', Spacing::Alone) }; + (::) => { + [ + TokenNode::Op(':', Spacing::Joint), + TokenNode::Op(':', Spacing::Alone) + ].iter().cloned().collect::() + }; + (!) => { TokenNode::Op('!', Spacing::Alone) }; + (<) => { TokenNode::Op('<', Spacing::Alone) }; + (>) => { TokenNode::Op('>', Spacing::Alone) }; + (_) => { TokenNode::Op('_', Spacing::Alone) }; + (0) => { TokenNode::Literal(::Literal::integer(0)) }; + (&) => { TokenNode::Op('&', Spacing::Alone) }; + ($i:ident) => { TokenNode::Term(Term::intern(stringify!($i))) }; } macro_rules! quote_tree { - ((unquote $($t:tt)*)) => { TokenStream::from($($t)*) }; + ((unquote $($t:tt)*)) => { $($t)* }; ((quote $($t:tt)*)) => { ($($t)*).quote() }; - (($($t:tt)*)) => { delimit(token::Paren, quote!($($t)*)) }; - ([$($t:tt)*]) => { delimit(token::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { delimit(token::Brace, quote!($($t)*)) }; - (rt) => { quote!(::__internal::__rt) }; - ($t:tt) => { TokenStream::from(TokenTree::Token(span(), quote_tok!($t))) }; -} - -fn delimit(delim: token::DelimToken, stream: TokenStream) -> TokenStream { - TokenTree::Delimited(span(), Delimited { delim: delim, tts: stream.into() }).into() + (($($t:tt)*)) => { TokenNode::Group(Delimiter::Parenthesis, quote!($($t)*)) }; + ([$($t:tt)*]) => { TokenNode::Group(Delimiter::Bracket, quote!($($t)*)) }; + ({$($t:tt)*}) => { TokenNode::Group(Delimiter::Brace, quote!($($t)*)) }; + ($t:tt) => { quote_tok!($t) }; } macro_rules! quote { () => { TokenStream::empty() }; - ($($t:tt)*) => { [ $( quote_tree!($t), )* ].iter().cloned().collect::() }; + ($($t:tt)*) => { + [ + $(TokenStream::from(quote_tree!($t)),)* + ].iter().cloned().collect::() + }; } impl ProcMacro for Quoter { - fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, _: Span, stream: TokenStream) -> TokenStream { + fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, + _: ::syntax_pos::Span, + stream: tokenstream::TokenStream) + -> tokenstream::TokenStream { let mut info = cx.current_expansion.mark.expn_info().unwrap(); info.callee.allow_internal_unstable = true; cx.current_expansion.mark.set_expn_info(info); - ::__internal::set_sess(cx, || quote!(::TokenStream { 0: (quote stream) })) + ::__internal::set_sess(cx, || TokenStream(stream).quote().0) } } impl Quote for Option { - fn quote(&self) -> TokenStream { - match *self { - Some(ref t) => quote!(Some((quote t))), + fn quote(self) -> TokenStream { + match self { + Some(t) => quote!(Some((quote t))), None => quote!(None), } } } impl Quote for TokenStream { - fn quote(&self) -> TokenStream { - let mut builder = TokenStreamBuilder::new(); - builder.push(quote!(rt::TokenStreamBuilder::new())); - - let mut trees = self.trees(); - loop { - let (mut tree, mut is_joint) = match trees.next_as_stream() { - Some(next) => next.as_tree(), - None => return builder.add(quote!(.build())).build(), - }; - if let TokenTree::Token(_, Token::Dollar) = tree { - let (next_tree, next_is_joint) = match trees.next_as_stream() { - Some(next) => next.as_tree(), - None => panic!("unexpected trailing `$` in `quote!`"), - }; - match next_tree { - TokenTree::Token(_, Token::Ident(..)) => { - builder.push(quote!(.add(rt::unquote(&(unquote next_tree))))); - continue - } - TokenTree::Token(_, Token::Dollar) => { - tree = next_tree; - is_joint = next_is_joint; + fn quote(self) -> TokenStream { + if self.is_empty() { + return quote!(::TokenStream::empty()); + } + let mut after_dollar = false; + let tokens = self.into_iter().filter_map(|tree| { + if after_dollar { + after_dollar = false; + match tree.kind { + TokenNode::Term(_) => { + return Some(quote!(::__internal::unquote(&(unquote tree)),)); } + TokenNode::Op('$', _) => {} _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), } + } else if let TokenNode::Op('$', _) = tree.kind { + after_dollar = true; + return None; } - builder.push(match is_joint { - true => quote!(.add((quote tree).joint())), - false => quote!(.add(rt::TokenStream::from((quote tree)))), - }); + Some(quote!(::TokenStream::from((quote tree)),)) + }).collect::(); + + if after_dollar { + panic!("unexpected trailing `$` in `quote!`"); } + + quote!([(unquote tokens)].iter().cloned().collect::<::TokenStream>()) } } impl Quote for TokenTree { - fn quote(&self) -> TokenStream { - match *self { - TokenTree::Token(span, ref token) => quote! { - rt::TokenTree::Token((quote span), (quote token)) - }, - TokenTree::Delimited(span, ref delimited) => quote! { - rt::TokenTree::Delimited((quote span), (quote delimited)) - }, - } + fn quote(self) -> TokenStream { + quote!(::TokenTree { span: (quote self.span), kind: (quote self.kind) }) } } -impl Quote for Delimited { - fn quote(&self) -> TokenStream { - quote!(rt::Delimited { delim: (quote self.delim), tts: (quote self.stream()).into() }) +impl Quote for TokenNode { + fn quote(self) -> TokenStream { + macro_rules! gen_match { + ($($i:ident($($arg:ident),+)),*) => { + match self { + $(TokenNode::$i($($arg),+) => quote! { + ::TokenNode::$i($((quote $arg)),+) + },)* + } + } + } + + gen_match! { Op(op, kind), Group(delim, tokens), Term(term), Literal(lit) } } } -impl<'a> Quote for &'a str { - fn quote(&self) -> TokenStream { - TokenTree::Token(span(), Token::Literal(token::Lit::Str_(Symbol::intern(self)), None)) - .into() +impl Quote for char { + fn quote(self) -> TokenStream { + TokenNode::Literal(Literal::character(self)).into() } } -impl Quote for usize { - fn quote(&self) -> TokenStream { - let integer_symbol = Symbol::intern(&self.to_string()); - TokenTree::Token(DUMMY_SP, Token::Literal(token::Lit::Integer(integer_symbol), None)) - .into() +impl<'a> Quote for &'a str { + fn quote(self) -> TokenStream { + TokenNode::Literal(Literal::string(self)).into() } } -impl Quote for Ident { - fn quote(&self) -> TokenStream { - quote!(rt::Ident { name: (quote self.name), ctxt: rt::ctxt() }) +impl Quote for usize { + fn quote(self) -> TokenStream { + TokenNode::Literal(Literal::integer(self as i128)).into() } } -impl Quote for Symbol { - fn quote(&self) -> TokenStream { - quote!(rt::Symbol::intern((quote &*self.as_str()))) +impl Quote for Term { + fn quote(self) -> TokenStream { + quote!(::Term::intern((quote self.as_str()))) } } impl Quote for Span { - fn quote(&self) -> TokenStream { - quote!(rt::span()) + fn quote(self) -> TokenStream { + quote!(::Span::def_site()) } } -impl Quote for Token { - fn quote(&self) -> TokenStream { - macro_rules! gen_match { - ($($i:ident),*; $($t:tt)*) => { - match *self { - $( Token::$i => quote!(rt::token::$i), )* - $( $t )* +macro_rules! literals { + ($($i:ident),*; $($raw:ident),*) => { + pub enum LiteralKind { + $($i,)* + $($raw(usize),)* + } + + impl LiteralKind { + pub fn with_contents_and_suffix(self, contents: Term, suffix: Option) + -> Literal { + let contents = contents.0; + let suffix = suffix.map(|t| t.0); + match self { + $(LiteralKind::$i => { + Literal(token::Literal(token::Lit::$i(contents), suffix)) + })* + $(LiteralKind::$raw(n) => { + Literal(token::Literal(token::Lit::$raw(contents, n), suffix)) + })* } } } - gen_match! { - Eq, Lt, Le, EqEq, Ne, Ge, Gt, AndAnd, OrOr, Not, Tilde, At, Dot, DotDot, DotDotDot, - DotDotEq, Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar, - Question, Underscore; - - Token::OpenDelim(delim) => quote!(rt::token::OpenDelim((quote delim))), - Token::CloseDelim(delim) => quote!(rt::token::CloseDelim((quote delim))), - Token::BinOp(tok) => quote!(rt::token::BinOp((quote tok))), - Token::BinOpEq(tok) => quote!(rt::token::BinOpEq((quote tok))), - Token::Ident(ident) => quote!(rt::token::Ident((quote ident))), - Token::Lifetime(ident) => quote!(rt::token::Lifetime((quote ident))), - Token::Literal(lit, sfx) => quote!(rt::token::Literal((quote lit), (quote sfx))), - _ => panic!("Unhandled case!"), + impl Literal { + fn kind_contents_and_suffix(self) -> (LiteralKind, Term, Option) { + let (lit, suffix) = match self.0 { + token::Literal(lit, suffix) => (lit, suffix), + _ => panic!("unsupported literal {:?}", self.0), + }; + + let (kind, contents) = match lit { + $(token::Lit::$i(contents) => (LiteralKind::$i, contents),)* + $(token::Lit::$raw(contents, n) => (LiteralKind::$raw(n), contents),)* + }; + (kind, Term(contents), suffix.map(Term)) + } } - } -} -impl Quote for token::BinOpToken { - fn quote(&self) -> TokenStream { - macro_rules! gen_match { - ($($i:ident),*) => { - match *self { - $( token::BinOpToken::$i => quote!(rt::token::BinOpToken::$i), )* + impl Quote for LiteralKind { + fn quote(self) -> TokenStream { + match self { + $(LiteralKind::$i => quote! { + ::__internal::LiteralKind::$i + },)* + $(LiteralKind::$raw(n) => quote! { + ::__internal::LiteralKind::$raw((quote n)) + },)* } } } - gen_match!(Plus, Minus, Star, Slash, Percent, Caret, And, Or, Shl, Shr) + impl Quote for Literal { + fn quote(self) -> TokenStream { + let (kind, contents, suffix) = self.kind_contents_and_suffix(); + quote! { + (quote kind).with_contents_and_suffix((quote contents), (quote suffix)) + } + } + } } } -impl Quote for Lit { - fn quote(&self) -> TokenStream { +literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw); + +impl Quote for Delimiter { + fn quote(self) -> TokenStream { macro_rules! gen_match { - ($($i:ident),*; $($raw:ident),*) => { - match *self { - $( Lit::$i(lit) => quote!(rt::token::Lit::$i((quote lit))), )* - $( Lit::$raw(lit, n) => { - quote!(::syntax::parse::token::Lit::$raw((quote lit), (quote n))) - })* + ($($i:ident),*) => { + match self { + $(Delimiter::$i => { quote!(::Delimiter::$i) })* } } } - gen_match!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw) + gen_match!(Parenthesis, Brace, Bracket, None) } } -impl Quote for token::DelimToken { - fn quote(&self) -> TokenStream { +impl Quote for Spacing { + fn quote(self) -> TokenStream { macro_rules! gen_match { ($($i:ident),*) => { - match *self { - $(token::DelimToken::$i => { quote!(rt::token::DelimToken::$i) })* + match self { + $(Spacing::$i => { quote!(::Spacing::$i) })* } } } - gen_match!(Paren, Bracket, Brace, NoDelim) + gen_match!(Alone, Joint) } } diff --git a/src/libprofiler_builtins/Cargo.toml b/src/libprofiler_builtins/Cargo.toml index eb31f5730d191..04f456917b957 100644 --- a/src/libprofiler_builtins/Cargo.toml +++ b/src/libprofiler_builtins/Cargo.toml @@ -15,4 +15,4 @@ doc = false core = { path = "../libcore" } [build-dependencies] -cc = "1.0" +cc = "1.0.1" diff --git a/src/libprofiler_builtins/build.rs b/src/libprofiler_builtins/build.rs index 8508b2dae2c56..dd88dd933f691 100644 --- a/src/libprofiler_builtins/build.rs +++ b/src/libprofiler_builtins/build.rs @@ -56,5 +56,5 @@ fn main() { cfg.file(Path::new("../libcompiler_builtins/compiler-rt/lib/profile").join(src)); } - cfg.compile("libprofiler-rt.a"); + cfg.compile("profiler-rt"); } diff --git a/src/librand/Cargo.toml b/src/librand/Cargo.toml deleted file mode 100644 index eda5f217565de..0000000000000 --- a/src/librand/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rand" -version = "0.0.0" - -[lib] -name = "rand" -path = "lib.rs" -doc = false - -[dependencies] -core = { path = "../libcore" } diff --git a/src/librand/chacha.rs b/src/librand/chacha.rs deleted file mode 100644 index e355eb44e4667..0000000000000 --- a/src/librand/chacha.rs +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The ChaCha random number generator. - -use core::fmt; -use {Rand, Rng, SeedableRng}; - -const KEY_WORDS: usize = 8; // 8 words for the 256-bit key -const STATE_WORDS: usize = 16; -const CHACHA_ROUNDS: usize = 20; // Cryptographically secure from 8 upwards as of this writing - -/// A random number generator that uses the ChaCha20 algorithm [1]. -/// -/// The ChaCha algorithm is widely accepted as suitable for -/// cryptographic purposes, but this implementation has not been -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. -/// -/// [1]: D. J. Bernstein, [*ChaCha, a variant of -/// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Copy, Clone)] -pub struct ChaChaRng { - buffer: [u32; STATE_WORDS], // Internal buffer of output - state: [u32; STATE_WORDS], // Initial state - index: usize, // Index into state -} - -impl fmt::Debug for ChaChaRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ChaChaRng") - .field("buffer", &self.buffer.iter()) - .field("state", &self.state.iter()) - .field("index", &self.index) - .finish() - } -} - -static EMPTY: ChaChaRng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS, -}; - - -macro_rules! quarter_round{ - ($a: expr, $b: expr, $c: expr, $d: expr) => {{ - $a = $a.wrapping_add($b); $d = $d ^ $a; $d = $d.rotate_left(16); - $c = $c.wrapping_add($d); $b = $b ^ $c; $b = $b.rotate_left(12); - $a = $a.wrapping_add($b); $d = $d ^ $a; $d = $d.rotate_left( 8); - $c = $c.wrapping_add($d); $b = $b ^ $c; $b = $b.rotate_left( 7); - }} -} - -macro_rules! double_round{ - ($x: expr) => {{ - // Column round - quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]); - quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]); - quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]); - quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]); - // Diagonal round - quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]); - quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]); - quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]); - quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]); - }} -} - -#[inline] -fn core(output: &mut [u32; STATE_WORDS], input: &[u32; STATE_WORDS]) { - *output = *input; - - for _ in 0..CHACHA_ROUNDS / 2 { - double_round!(output); - } - - for i in 0..STATE_WORDS { - output[i] = output[i].wrapping_add(input[i]); - } -} - -impl ChaChaRng { - /// Create an ChaCha random number generator using the default - /// fixed key of 8 zero words. - pub fn new_unseeded() -> ChaChaRng { - let mut rng = EMPTY; - rng.init(&[0; KEY_WORDS]); - rng - } - - /// Sets the internal 128-bit ChaCha counter to - /// a user-provided value. This permits jumping - /// arbitrarily ahead (or backwards) in the pseudorandom stream. - /// - /// Since the nonce words are used to extend the counter to 128 bits, - /// users wishing to obtain the conventional ChaCha pseudorandom stream - /// associated with a particular nonce can call this function with - /// arguments `0, desired_nonce`. - pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { - self.state[12] = (counter_low >> 0) as u32; - self.state[13] = (counter_low >> 32) as u32; - self.state[14] = (counter_high >> 0) as u32; - self.state[15] = (counter_high >> 32) as u32; - self.index = STATE_WORDS; // force recomputation - } - - /// Initializes `self.state` with the appropriate key and constants - /// - /// We deviate slightly from the ChaCha specification regarding - /// the nonce, which is used to extend the counter to 128 bits. - /// This is provably as strong as the original cipher, though, - /// since any distinguishing attack on our variant also works - /// against ChaCha with a chosen-nonce. See the XSalsa20 [1] - /// security proof for a more involved example of this. - /// - /// The modified word layout is: - /// ```text - /// constant constant constant constant - /// key key key key - /// key key key key - /// counter counter counter counter - /// ``` - /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 - /// nonce.*](http://cr.yp.to/papers.html#xsalsa) - fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = 0x61707865; - self.state[1] = 0x3320646E; - self.state[2] = 0x79622D32; - self.state[3] = 0x6B206574; - - for i in 0..KEY_WORDS { - self.state[4 + i] = key[i]; - } - - self.state[12] = 0; - self.state[13] = 0; - self.state[14] = 0; - self.state[15] = 0; - - self.index = STATE_WORDS; - } - - /// Refill the internal output buffer (`self.buffer`) - fn update(&mut self) { - core(&mut self.buffer, &self.state); - self.index = 0; - // update 128-bit counter - self.state[12] += 1; - if self.state[12] != 0 { - return; - } - self.state[13] += 1; - if self.state[13] != 0 { - return; - } - self.state[14] += 1; - if self.state[14] != 0 { - return; - } - self.state[15] += 1; - } -} - -impl Rng for ChaChaRng { - #[inline] - fn next_u32(&mut self) -> u32 { - if self.index == STATE_WORDS { - self.update(); - } - - let value = self.buffer[self.index % STATE_WORDS]; - self.index += 1; - value - } -} - -impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { - fn reseed(&mut self, seed: &'a [u32]) { - // reset state - self.init(&[0; KEY_WORDS]); - // set key in place - let key = &mut self.state[4..4 + KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed) { - *k = *s; - } - } - - /// Create a ChaCha generator from a seed, - /// obtained from a variable-length u32 array. - /// Only up to 8 words are used; if less than 8 - /// words are used, the remaining are set to zero. - fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = EMPTY; - rng.reseed(seed); - rng - } -} - -impl Rand for ChaChaRng { - fn rand(other: &mut R) -> ChaChaRng { - let mut key: [u32; KEY_WORDS] = [0; KEY_WORDS]; - for word in &mut key { - *word = other.gen(); - } - SeedableRng::from_seed(&key[..]) - } -} - - -#[cfg(test)] -mod tests { - use std::prelude::v1::*; - - use {Rng, SeedableRng}; - use super::ChaChaRng; - - #[test] - fn test_rng_rand_seeded() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); - let mut ra: ChaChaRng = SeedableRng::from_seed(&*s); - let mut rb: ChaChaRng = SeedableRng::from_seed(&*s); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_seeded() { - let seed: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - let mut ra: ChaChaRng = SeedableRng::from_seed(seed); - let mut rb: ChaChaRng = SeedableRng::from_seed(seed); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_reseed() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); - let mut r: ChaChaRng = SeedableRng::from_seed(&*s); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); - } - - #[test] - #[rustfmt_skip] - fn test_rng_true_values() { - // Test vectors 1 and 2 from - // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed: &[_] = &[0; 8]; - let mut ra: ChaChaRng = SeedableRng::from_seed(seed); - - let v = (0..16).map(|_| ra.next_u32()).collect::>(); - assert_eq!(v, - vec![0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, - 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, - 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, - 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2]); - - let v = (0..16).map(|_| ra.next_u32()).collect::>(); - assert_eq!(v, - vec![0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, - 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, - 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, - 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b]); - - - let seed: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - let mut ra: ChaChaRng = SeedableRng::from_seed(seed); - - // Store the 17*i-th 32-bit word, - // i.e., the i-th word of the i-th 16-word block - let mut v: Vec = Vec::new(); - for _ in 0..16 { - v.push(ra.next_u32()); - for _ in 0..16 { - ra.next_u32(); - } - } - - assert_eq!(v, - vec![0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, - 0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384, - 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, - 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4]); - } - - #[test] - fn test_rng_clone() { - let seed: &[_] = &[0; 8]; - let mut rng: ChaChaRng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); - for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); - } - } -} diff --git a/src/librand/distributions/exponential.rs b/src/librand/distributions/exponential.rs deleted file mode 100644 index 3337cc2a6273c..0000000000000 --- a/src/librand/distributions/exponential.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The exponential distribution. - -use core::fmt; - -#[cfg(not(test))] // only necessary for no_std -use FloatMath; - -use {Rand, Rng}; -use distributions::{IndependentSample, Sample, ziggurat, ziggurat_tables}; - -/// A wrapper around an `f64` to generate Exp(1) random numbers. -/// -/// See `Exp` for the general exponential distribution. Note that this has to -/// be unwrapped before use as an `f64` (using either `*` or `mem::transmute` -/// is safe). -/// -/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The -/// exact description in the paper was adjusted to use tables for the -/// exponential distribution rather than normal. -/// -/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to -/// Generate Normal Random -/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield -/// College, Oxford -#[derive(Copy, Clone)] -pub struct Exp1(pub f64); - -// This could be done via `-rng.gen::().ln()` but that is slower. -impl Rand for Exp1 { - #[inline] - fn rand(rng: &mut R) -> Exp1 { - #[inline] - fn pdf(x: f64) -> f64 { - (-x).exp() - } - #[inline] - fn zero_case(rng: &mut R, _u: f64) -> f64 { - ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() - } - - Exp1(ziggurat(rng, - false, - &ziggurat_tables::ZIG_EXP_X, - &ziggurat_tables::ZIG_EXP_F, - pdf, - zero_case)) - } -} - -impl fmt::Debug for Exp1 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Exp1") - .field(&self.0) - .finish() - } -} - -/// The exponential distribution `Exp(lambda)`. -/// -/// This distribution has density function: `f(x) = lambda * -/// exp(-lambda * x)` for `x > 0`. -#[derive(Copy, Clone)] -pub struct Exp { - /// `lambda` stored as `1/lambda`, since this is what we scale by. - lambda_inverse: f64, -} - -impl Exp { - /// Construct a new `Exp` with the given shape parameter - /// `lambda`. Panics if `lambda <= 0`. - pub fn new(lambda: f64) -> Exp { - assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0"); - Exp { lambda_inverse: 1.0 / lambda } - } -} - -impl Sample for Exp { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for Exp { - fn ind_sample(&self, rng: &mut R) -> f64 { - let Exp1(n) = rng.gen::(); - n * self.lambda_inverse - } -} - -impl fmt::Debug for Exp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Exp") - .field("lambda_inverse", &self.lambda_inverse) - .finish() - } -} - -#[cfg(test)] -mod tests { - use distributions::{IndependentSample, Sample}; - use super::Exp; - - #[test] - fn test_exp() { - let mut exp = Exp::new(10.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.ind_sample(&mut rng) >= 0.0); - } - } - #[test] - #[should_panic] - fn test_exp_invalid_lambda_zero() { - Exp::new(0.0); - } - #[test] - #[should_panic] - fn test_exp_invalid_lambda_neg() { - Exp::new(-10.0); - } -} - -#[cfg(test)] -mod bench { - extern crate test; - - use self::test::Bencher; - use std::mem::size_of; - use super::Exp; - use distributions::Sample; - - #[bench] - fn rand_exp(b: &mut Bencher) { - let mut rng = ::test::weak_rng(); - let mut exp = Exp::new(2.71828 * 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - exp.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; - } -} diff --git a/src/librand/distributions/gamma.rs b/src/librand/distributions/gamma.rs deleted file mode 100644 index e796197ab5bf2..0000000000000 --- a/src/librand/distributions/gamma.rs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Gamma and derived distributions. - -use core::fmt; - -use self::GammaRepr::*; -use self::ChiSquaredRepr::*; - -#[cfg(not(test))] // only necessary for no_std -use FloatMath; - -use {Open01, Rng}; -use super::normal::StandardNormal; -use super::{Exp, IndependentSample, Sample}; - -/// The Gamma distribution `Gamma(shape, scale)` distribution. -/// -/// The density function of this distribution is -/// -/// ```text -/// f(x) = x^(k - 1) * exp(-x / θ) / (Γ(k) * θ^k) -/// ``` -/// -/// where `Γ` is the Gamma function, `k` is the shape and `θ` is the -/// scale and both `k` and `θ` are strictly positive. -/// -/// The algorithm used is that described by Marsaglia & Tsang 2000[1], -/// falling back to directly sampling from an Exponential for `shape -/// == 1`, and using the boosting technique described in [1] for -/// `shape < 1`. -/// -/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method -/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3 -/// (September 2000), -/// 363-372. DOI:[10.1145/358407.358414](http://doi.acm.org/10.1145/358407.358414) -pub struct Gamma { - repr: GammaRepr, -} - -impl fmt::Debug for Gamma { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Gamma") - .field("repr", - &match self.repr { - GammaRepr::Large(_) => "Large", - GammaRepr::One(_) => "Exp", - GammaRepr::Small(_) => "Small" - }) - .finish() - } -} - -enum GammaRepr { - Large(GammaLargeShape), - One(Exp), - Small(GammaSmallShape), -} - -// These two helpers could be made public, but saving the -// match-on-Gamma-enum branch from using them directly (e.g. if one -// knows that the shape is always > 1) doesn't appear to be much -// faster. - -/// Gamma distribution where the shape parameter is less than 1. -/// -/// Note, samples from this require a compulsory floating-point `pow` -/// call, which makes it significantly slower than sampling from a -/// gamma distribution where the shape parameter is greater than or -/// equal to 1. -/// -/// See `Gamma` for sampling from a Gamma distribution with general -/// shape parameters. -struct GammaSmallShape { - inv_shape: f64, - large_shape: GammaLargeShape, -} - -/// Gamma distribution where the shape parameter is larger than 1. -/// -/// See `Gamma` for sampling from a Gamma distribution with general -/// shape parameters. -struct GammaLargeShape { - scale: f64, - c: f64, - d: f64, -} - -impl Gamma { - /// Construct an object representing the `Gamma(shape, scale)` - /// distribution. - /// - /// Panics if `shape <= 0` or `scale <= 0`. - pub fn new(shape: f64, scale: f64) -> Gamma { - assert!(shape > 0.0, "Gamma::new called with shape <= 0"); - assert!(scale > 0.0, "Gamma::new called with scale <= 0"); - - let repr = if shape == 1.0 { - One(Exp::new(1.0 / scale)) - } else if 0.0 <= shape && shape < 1.0 { - Small(GammaSmallShape::new_raw(shape, scale)) - } else { - Large(GammaLargeShape::new_raw(shape, scale)) - }; - Gamma { repr } - } -} - -impl GammaSmallShape { - fn new_raw(shape: f64, scale: f64) -> GammaSmallShape { - GammaSmallShape { - inv_shape: 1. / shape, - large_shape: GammaLargeShape::new_raw(shape + 1.0, scale), - } - } -} - -impl GammaLargeShape { - fn new_raw(shape: f64, scale: f64) -> GammaLargeShape { - let d = shape - 1. / 3.; - GammaLargeShape { - scale, - c: 1. / (9. * d).sqrt(), - d, - } - } -} - -impl Sample for Gamma { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} -impl Sample for GammaSmallShape { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} -impl Sample for GammaLargeShape { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for Gamma { - fn ind_sample(&self, rng: &mut R) -> f64 { - match self.repr { - Small(ref g) => g.ind_sample(rng), - One(ref g) => g.ind_sample(rng), - Large(ref g) => g.ind_sample(rng), - } - } -} -impl IndependentSample for GammaSmallShape { - fn ind_sample(&self, rng: &mut R) -> f64 { - let Open01(u) = rng.gen::>(); - - self.large_shape.ind_sample(rng) * u.powf(self.inv_shape) - } -} -impl IndependentSample for GammaLargeShape { - fn ind_sample(&self, rng: &mut R) -> f64 { - loop { - let StandardNormal(x) = rng.gen::(); - let v_cbrt = 1.0 + self.c * x; - if v_cbrt <= 0.0 { - // a^3 <= 0 iff a <= 0 - continue; - } - - let v = v_cbrt * v_cbrt * v_cbrt; - let Open01(u) = rng.gen::>(); - - let x_sqr = x * x; - if u < 1.0 - 0.0331 * x_sqr * x_sqr || - u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) { - return self.d * v * self.scale; - } - } - } -} - -/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of -/// freedom. -/// -/// For `k > 0` integral, this distribution is the sum of the squares -/// of `k` independent standard normal random variables. For other -/// `k`, this uses the equivalent characterization `χ²(k) = Gamma(k/2, -/// 2)`. -pub struct ChiSquared { - repr: ChiSquaredRepr, -} - -impl fmt::Debug for ChiSquared { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ChiSquared") - .field("repr", - &match self.repr { - ChiSquaredRepr::DoFExactlyOne => "DoFExactlyOne", - ChiSquaredRepr::DoFAnythingElse(_) => "DoFAnythingElse", - }) - .finish() - } -} - -enum ChiSquaredRepr { - // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1, - // e.g. when alpha = 1/2 as it would be for this case, so special- - // casing and using the definition of N(0,1)^2 is faster. - DoFExactlyOne, - DoFAnythingElse(Gamma), -} - -impl ChiSquared { - /// Create a new chi-squared distribution with degrees-of-freedom - /// `k`. Panics if `k < 0`. - pub fn new(k: f64) -> ChiSquared { - let repr = if k == 1.0 { - DoFExactlyOne - } else { - assert!(k > 0.0, "ChiSquared::new called with `k` < 0"); - DoFAnythingElse(Gamma::new(0.5 * k, 2.0)) - }; - ChiSquared { repr: repr } - } -} - -impl Sample for ChiSquared { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for ChiSquared { - fn ind_sample(&self, rng: &mut R) -> f64 { - match self.repr { - DoFExactlyOne => { - // k == 1 => N(0,1)^2 - let StandardNormal(norm) = rng.gen::(); - norm * norm - } - DoFAnythingElse(ref g) => g.ind_sample(rng), - } - } -} - -/// The Fisher F distribution `F(m, n)`. -/// -/// This distribution is equivalent to the ratio of two normalized -/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) / -/// (χ²(n)/n)`. -pub struct FisherF { - numer: ChiSquared, - denom: ChiSquared, - // denom_dof / numer_dof so that this can just be a straight - // multiplication, rather than a division. - dof_ratio: f64, -} - -impl FisherF { - /// Create a new `FisherF` distribution, with the given - /// parameter. Panics if either `m` or `n` are not positive. - pub fn new(m: f64, n: f64) -> FisherF { - assert!(m > 0.0, "FisherF::new called with `m < 0`"); - assert!(n > 0.0, "FisherF::new called with `n < 0`"); - - FisherF { - numer: ChiSquared::new(m), - denom: ChiSquared::new(n), - dof_ratio: n / m, - } - } -} - -impl Sample for FisherF { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for FisherF { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio - } -} - -impl fmt::Debug for FisherF { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FisherF") - .field("numer", &self.numer) - .field("denom", &self.denom) - .field("dof_ratio", &self.dof_ratio) - .finish() - } -} - -/// The Student t distribution, `t(nu)`, where `nu` is the degrees of -/// freedom. -pub struct StudentT { - chi: ChiSquared, - dof: f64, -} - -impl StudentT { - /// Create a new Student t distribution with `n` degrees of - /// freedom. Panics if `n <= 0`. - pub fn new(n: f64) -> StudentT { - assert!(n > 0.0, "StudentT::new called with `n <= 0`"); - StudentT { - chi: ChiSquared::new(n), - dof: n, - } - } -} - -impl Sample for StudentT { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for StudentT { - fn ind_sample(&self, rng: &mut R) -> f64 { - let StandardNormal(norm) = rng.gen::(); - norm * (self.dof / self.chi.ind_sample(rng)).sqrt() - } -} - -impl fmt::Debug for StudentT { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("StudentT") - .field("chi", &self.chi) - .field("dof", &self.dof) - .finish() - } -} - -#[cfg(test)] -mod tests { - use distributions::{IndependentSample, Sample}; - use super::{ChiSquared, FisherF, StudentT}; - - #[test] - fn test_chi_squared_one() { - let mut chi = ChiSquared::new(1.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - chi.sample(&mut rng); - chi.ind_sample(&mut rng); - } - } - #[test] - fn test_chi_squared_small() { - let mut chi = ChiSquared::new(0.5); - let mut rng = ::test::rng(); - for _ in 0..1000 { - chi.sample(&mut rng); - chi.ind_sample(&mut rng); - } - } - #[test] - fn test_chi_squared_large() { - let mut chi = ChiSquared::new(30.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - chi.sample(&mut rng); - chi.ind_sample(&mut rng); - } - } - #[test] - #[should_panic] - fn test_chi_squared_invalid_dof() { - ChiSquared::new(-1.0); - } - - #[test] - fn test_f() { - let mut f = FisherF::new(2.0, 32.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - f.sample(&mut rng); - f.ind_sample(&mut rng); - } - } - - #[test] - fn test_t() { - let mut t = StudentT::new(11.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - t.sample(&mut rng); - t.ind_sample(&mut rng); - } - } -} - -#[cfg(test)] -mod bench { - extern crate test; - use self::test::Bencher; - use std::mem::size_of; - use distributions::IndependentSample; - use super::Gamma; - - - #[bench] - fn bench_gamma_large_shape(b: &mut Bencher) { - let gamma = Gamma::new(10., 1.0); - let mut rng = ::test::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; - } - - #[bench] - fn bench_gamma_small_shape(b: &mut Bencher) { - let gamma = Gamma::new(0.1, 1.0); - let mut rng = ::test::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; - } -} diff --git a/src/librand/distributions/mod.rs b/src/librand/distributions/mod.rs deleted file mode 100644 index 47967a719d397..0000000000000 --- a/src/librand/distributions/mod.rs +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Sampling from random distributions. -//! -//! This is a generalization of `Rand` to allow parameters to control the -//! exact properties of the generated values, e.g. the mean and standard -//! deviation of a normal distribution. The `Sample` trait is the most -//! general, and allows for generating values that change some state -//! internally. The `IndependentSample` trait is for generating values -//! that do not need to record state. - -use core::fmt; - -#[cfg(not(test))] // only necessary for no_std -use core::num::Float; - -use core::marker::PhantomData; - -use {Rand, Rng}; - -pub use self::range::Range; -pub use self::gamma::{ChiSquared, FisherF, Gamma, StudentT}; -pub use self::normal::{LogNormal, Normal}; -pub use self::exponential::Exp; - -pub mod range; -pub mod gamma; -pub mod normal; -pub mod exponential; - -/// Types that can be used to create a random instance of `Support`. -pub trait Sample { - /// Generate a random value of `Support`, using `rng` as the - /// source of randomness. - fn sample(&mut self, rng: &mut R) -> Support; -} - -/// `Sample`s that do not require keeping track of state. -/// -/// Since no state is recorded, each sample is (statistically) -/// independent of all others, assuming the `Rng` used has this -/// property. -// FIXME maybe having this separate is overkill (the only reason is to -// take &self rather than &mut self)? or maybe this should be the -// trait called `Sample` and the other should be `DependentSample`. -pub trait IndependentSample: Sample { - /// Generate a random value. - fn ind_sample(&self, _: &mut R) -> Support; -} - -/// A wrapper for generating types that implement `Rand` via the -/// `Sample` & `IndependentSample` traits. -pub struct RandSample { - _marker: PhantomData, -} - -impl RandSample { - pub fn new() -> RandSample { - RandSample { _marker: PhantomData } - } -} - -impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { - self.ind_sample(rng) - } -} - -impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { - rng.gen() - } -} - -impl fmt::Debug for RandSample { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad("RandSample { .. }") - } -} - -/// A value with a particular weight for use with `WeightedChoice`. -pub struct Weighted { - /// The numerical weight of this item - pub weight: usize, - /// The actual item which is being weighted - pub item: T, -} - -impl fmt::Debug for Weighted { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Weighted") - .field("weight", &self.weight) - .field("item", &self.item) - .finish() - } -} - -/// A distribution that selects from a finite collection of weighted items. -/// -/// Each item has an associated weight that influences how likely it -/// is to be chosen: higher weight is more likely. -/// -/// The `Clone` restriction is a limitation of the `Sample` and -/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for -/// all `T`, as is `usize`, so one can store references or indices into -/// another vector. -pub struct WeightedChoice<'a, T: 'a> { - items: &'a mut [Weighted], - weight_range: Range, -} - -impl<'a, T: Clone> WeightedChoice<'a, T> { - /// Create a new `WeightedChoice`. - /// - /// Panics if: - /// - `v` is empty - /// - the total weight is 0 - /// - the total weight is larger than a `usize` can contain. - pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { - // strictly speaking, this is subsumed by the total weight == 0 case - assert!(!items.is_empty(), - "WeightedChoice::new called with no items"); - - let mut running_total = 0_usize; - - // we convert the list from individual weights to cumulative - // weights so we can binary search. This *could* drop elements - // with weight == 0 as an optimisation. - for item in &mut *items { - running_total = match running_total.checked_add(item.weight) { - Some(n) => n, - None => { - panic!("WeightedChoice::new called with a total weight larger than a usize \ - can contain") - } - }; - - item.weight = running_total; - } - assert!(running_total != 0, - "WeightedChoice::new called with a total weight of 0"); - - WeightedChoice { - items, - // we're likely to be generating numbers in this range - // relatively often, so might as well cache it - weight_range: Range::new(0, running_total), - } - } -} - -impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { - fn sample(&mut self, rng: &mut R) -> T { - self.ind_sample(rng) - } -} - -impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { - fn ind_sample(&self, rng: &mut R) -> T { - // we want to find the first element that has cumulative - // weight > sample_weight, which we do by binary since the - // cumulative weights of self.items are sorted. - - // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.ind_sample(rng); - - // short circuit when it's the first item - if sample_weight < self.items[0].weight { - return self.items[0].item.clone(); - } - - let mut idx = 0; - let mut modifier = self.items.len(); - - // now we know that every possibility has an element to the - // left, so we can just search for the last element that has - // cumulative weight <= sample_weight, then the next one will - // be "it". (Note that this greatest element will never be the - // last element of the vector, since sample_weight is chosen - // in [0, total_weight) and the cumulative weight of the last - // one is exactly the total weight.) - while modifier > 1 { - let i = idx + modifier / 2; - if self.items[i].weight <= sample_weight { - // we're small, so look to the right, but allow this - // exact element still. - idx = i; - // we need the `/ 2` to round up otherwise we'll drop - // the trailing elements when `modifier` is odd. - modifier += 1; - } else { - // otherwise we're too big, so go left. (i.e. do - // nothing) - } - modifier /= 2; - } - return self.items[idx + 1].item.clone(); - } -} - -impl<'a, T: fmt::Debug> fmt::Debug for WeightedChoice<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("WeightedChoice") - .field("items", &self.items) - .field("weight_range", &self.weight_range) - .finish() - } -} - -mod ziggurat_tables; - -/// Sample a random number using the Ziggurat method (specifically the -/// ZIGNOR variant from Doornik 2005). Most of the arguments are -/// directly from the paper: -/// -/// * `rng`: source of randomness -/// * `symmetric`: whether this is a symmetric distribution, or one-sided with P(x < 0) = 0. -/// * `X`: the $x_i$ abscissae. -/// * `F`: precomputed values of the PDF at the $x_i$, (i.e. $f(x_i)$) -/// * `F_DIFF`: precomputed values of $f(x_i) - f(x_{i+1})$ -/// * `pdf`: the probability density function -/// * `zero_case`: manual sampling from the tail when we chose the -/// bottom box (i.e. i == 0) -// the perf improvement (25-50%) is definitely worth the extra code -// size from force-inlining. -#[inline(always)] -fn ziggurat(rng: &mut R, - symmetric: bool, - x_tab: ziggurat_tables::ZigTable, - f_tab: ziggurat_tables::ZigTable, - mut pdf: P, - mut zero_case: Z) - -> f64 - where P: FnMut(f64) -> f64, - Z: FnMut(&mut R, f64) -> f64 -{ - const SCALE: f64 = (1u64 << 53) as f64; - loop { - // reimplement the f64 generation as an optimisation suggested - // by the Doornik paper: we have a lot of precision-space - // (i.e. there are 11 bits of the 64 of a u64 to use after - // creating a f64), so we might as well reuse some to save - // generating a whole extra random number. (Seems to be 15% - // faster.) - // - // This unfortunately misses out on the benefits of direct - // floating point generation if an RNG like dSMFT is - // used. (That is, such RNGs create floats directly, highly - // efficiently and overload next_f32/f64, so by not calling it - // this may be slower than it would be otherwise.) - // FIXME: investigate/optimise for the above. - let bits: u64 = rng.gen(); - let i = (bits & 0xff) as usize; - let f = (bits >> 11) as f64 / SCALE; - - // u is either U(-1, 1) or U(0, 1) depending on if this is a - // symmetric distribution or not. - let u = if symmetric { 2.0 * f - 1.0 } else { f }; - let x = u * x_tab[i]; - - let test_x = if symmetric { x.abs() } else { x }; - - // algebraically equivalent to |u| < x_tab[i+1]/x_tab[i] (or u < x_tab[i+1]/x_tab[i]) - if test_x < x_tab[i + 1] { - return x; - } - if i == 0 { - return zero_case(rng, u); - } - // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::() < pdf(x) { - return x; - } - } -} - -#[cfg(test)] -mod tests { - use {Rand, Rng}; - use super::{IndependentSample, RandSample, Sample, Weighted, WeightedChoice}; - - #[derive(PartialEq, Debug)] - struct ConstRand(usize); - impl Rand for ConstRand { - fn rand(_: &mut R) -> ConstRand { - ConstRand(0) - } - } - - // 0, 1, 2, 3, ... - struct CountingRng { - i: u32, - } - impl Rng for CountingRng { - fn next_u32(&mut self) -> u32 { - self.i += 1; - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - self.next_u32() as u64 - } - } - - #[test] - fn test_rand_sample() { - let mut rand_sample = RandSample::::new(); - - assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0)); - } - #[test] - #[rustfmt_skip] - fn test_weighted_choice() { - // this makes assumptions about the internal implementation of - // WeightedChoice, specifically: it doesn't reorder the items, - // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to - // 1, internally; modulo a modulo operation). - - macro_rules! t { - ($items:expr, $expected:expr) => {{ - let mut items = $items; - let wc = WeightedChoice::new(&mut items); - let expected = $expected; - - let mut rng = CountingRng { i: 0 }; - - for &val in &expected { - assert_eq!(wc.ind_sample(&mut rng), val) - } - }} - } - - t!(vec![Weighted { weight: 1, item: 10 }], - [10]); - - // skip some - t!(vec![Weighted { weight: 0, item: 20 }, - Weighted { weight: 2, item: 21 }, - Weighted { weight: 0, item: 22 }, - Weighted { weight: 1, item: 23 }], - [21, 21, 23]); - - // different weights - t!(vec![Weighted { weight: 4, item: 30 }, - Weighted { weight: 3, item: 31 }], - [30, 30, 30, 30, 31, 31, 31]); - - // check that we're binary searching - // correctly with some vectors of odd - // length. - t!(vec![Weighted { weight: 1, item: 40 }, - Weighted { weight: 1, item: 41 }, - Weighted { weight: 1, item: 42 }, - Weighted { weight: 1, item: 43 }, - Weighted { weight: 1, item: 44 }], - [40, 41, 42, 43, 44]); - t!(vec![Weighted { weight: 1, item: 50 }, - Weighted { weight: 1, item: 51 }, - Weighted { weight: 1, item: 52 }, - Weighted { weight: 1, item: 53 }, - Weighted { weight: 1, item: 54 }, - Weighted { weight: 1, item: 55 }, - Weighted { weight: 1, item: 56 }], - [50, 51, 52, 53, 54, 55, 56]); - } - - #[test] - #[should_panic] - fn test_weighted_choice_no_items() { - WeightedChoice::::new(&mut []); - } - #[test] - #[should_panic] - #[rustfmt_skip] - fn test_weighted_choice_zero_weight() { - WeightedChoice::new(&mut [Weighted { weight: 0, item: 0 }, - Weighted { weight: 0, item: 1 }]); - } - #[test] - #[should_panic] - #[rustfmt_skip] - fn test_weighted_choice_weight_overflows() { - let x = (!0) as usize / 2; // x + x + 2 is the overflow - WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, - Weighted { weight: 1, item: 1 }, - Weighted { weight: x, item: 2 }, - Weighted { weight: 1, item: 3 }]); - } -} diff --git a/src/librand/distributions/normal.rs b/src/librand/distributions/normal.rs deleted file mode 100644 index e1518dab21c2b..0000000000000 --- a/src/librand/distributions/normal.rs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The normal and derived distributions. - -use core::fmt; - -#[cfg(not(test))] // only necessary for no_std -use FloatMath; - -use {Open01, Rand, Rng}; -use distributions::{IndependentSample, Sample, ziggurat, ziggurat_tables}; - -/// A wrapper around an `f64` to generate N(0, 1) random numbers -/// (a.k.a. a standard normal, or Gaussian). -/// -/// See `Normal` for the general normal distribution. That this has to -/// be unwrapped before use as an `f64` (using either `*` or -/// `mem::transmute` is safe). -/// -/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. -/// -/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to -/// Generate Normal Random -/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield -/// College, Oxford -#[derive(Copy, Clone)] -pub struct StandardNormal(pub f64); - -impl Rand for StandardNormal { - fn rand(rng: &mut R) -> StandardNormal { - #[inline] - fn pdf(x: f64) -> f64 { - (-x * x / 2.0).exp() - } - #[inline] - fn zero_case(rng: &mut R, u: f64) -> f64 { - // compute a random number in the tail by hand - - // strange initial conditions, because the loop is not - // do-while, so the condition should be true on the first - // run, they get overwritten anyway (0 < 1, so these are - // good). - let mut x = 1.0f64; - let mut y = 0.0f64; - - while -2.0 * y < x * x { - let Open01(x_) = rng.gen::>(); - let Open01(y_) = rng.gen::>(); - - x = x_.ln() / ziggurat_tables::ZIG_NORM_R; - y = y_.ln(); - } - - if u < 0.0 { - x - ziggurat_tables::ZIG_NORM_R - } else { - ziggurat_tables::ZIG_NORM_R - x - } - } - - StandardNormal(ziggurat(rng, - true, // this is symmetric - &ziggurat_tables::ZIG_NORM_X, - &ziggurat_tables::ZIG_NORM_F, - pdf, - zero_case)) - } -} - -impl fmt::Debug for StandardNormal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("StandardNormal") - .field(&self.0) - .finish() - } -} - -/// The normal distribution `N(mean, std_dev**2)`. -/// -/// This uses the ZIGNOR variant of the Ziggurat method, see -/// `StandardNormal` for more details. -#[derive(Copy, Clone)] -pub struct Normal { - mean: f64, - std_dev: f64, -} - -impl Normal { - /// Construct a new `Normal` distribution with the given mean and - /// standard deviation. - /// - /// # Panics - /// - /// Panics if `std_dev < 0`. - pub fn new(mean: f64, std_dev: f64) -> Normal { - assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0"); - Normal { - mean, - std_dev, - } - } -} - -impl Sample for Normal { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for Normal { - fn ind_sample(&self, rng: &mut R) -> f64 { - let StandardNormal(n) = rng.gen::(); - self.mean + self.std_dev * n - } -} - -impl fmt::Debug for Normal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Normal") - .field("mean", &self.mean) - .field("std_dev", &self.std_dev) - .finish() - } -} - - -/// The log-normal distribution `ln N(mean, std_dev**2)`. -/// -/// If `X` is log-normal distributed, then `ln(X)` is `N(mean, -/// std_dev**2)` distributed. -#[derive(Copy, Clone)] -pub struct LogNormal { - norm: Normal, -} - -impl LogNormal { - /// Construct a new `LogNormal` distribution with the given mean - /// and standard deviation. - /// - /// # Panics - /// - /// Panics if `std_dev < 0`. - pub fn new(mean: f64, std_dev: f64) -> LogNormal { - assert!(std_dev >= 0.0, "LogNormal::new called with `std_dev` < 0"); - LogNormal { norm: Normal::new(mean, std_dev) } - } -} - -impl Sample for LogNormal { - fn sample(&mut self, rng: &mut R) -> f64 { - self.ind_sample(rng) - } -} - -impl IndependentSample for LogNormal { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.norm.ind_sample(rng).exp() - } -} - -impl fmt::Debug for LogNormal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LogNormal") - .field("norm", &self.norm) - .finish() - } -} - -#[cfg(test)] -mod tests { - use distributions::{IndependentSample, Sample}; - use super::{LogNormal, Normal}; - - #[test] - fn test_normal() { - let mut norm = Normal::new(10.0, 10.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - norm.sample(&mut rng); - norm.ind_sample(&mut rng); - } - } - #[test] - #[should_panic] - fn test_normal_invalid_sd() { - Normal::new(10.0, -1.0); - } - - - #[test] - fn test_log_normal() { - let mut lnorm = LogNormal::new(10.0, 10.0); - let mut rng = ::test::rng(); - for _ in 0..1000 { - lnorm.sample(&mut rng); - lnorm.ind_sample(&mut rng); - } - } - #[test] - #[should_panic] - fn test_log_normal_invalid_sd() { - LogNormal::new(10.0, -1.0); - } -} - -#[cfg(test)] -mod bench { - extern crate test; - use self::test::Bencher; - use std::mem::size_of; - use distributions::Sample; - use super::Normal; - - #[bench] - fn rand_normal(b: &mut Bencher) { - let mut rng = ::test::weak_rng(); - let mut normal = Normal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - normal.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; - } -} diff --git a/src/librand/distributions/range.rs b/src/librand/distributions/range.rs deleted file mode 100644 index f2f8132e5b47b..0000000000000 --- a/src/librand/distributions/range.rs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating numbers between two others. - -// this is surprisingly complicated to be both generic & correct - -use core::fmt; -use core::marker::Sized; -use Rng; -use distributions::{IndependentSample, Sample}; - -/// Sample values uniformly between two bounds. -/// -/// This gives a uniform distribution (assuming the RNG used to sample -/// it is itself uniform & the `SampleRange` implementation for the -/// given type is correct), even for edge cases like `low = 0`, -/// `high = 170`, for which a naive modulo operation would return -/// numbers less than 85 with double the probability to those greater -/// than 85. -/// -/// Types should attempt to sample in `[low, high)`, i.e., not -/// including `high`, but this may be very difficult. All the -/// primitive integer types satisfy this property, and the float types -/// normally satisfy it, but rounding may mean `high` can occur. -pub struct Range { - low: X, - range: X, - accept_zone: X, -} - -impl Range { - /// Create a new `Range` instance that samples uniformly from - /// `[low, high)`. Panics if `low >= high`. - pub fn new(low: X, high: X) -> Range { - assert!(low < high, "Range::new called with `low >= high`"); - SampleRange::construct_range(low, high) - } -} - -impl Sample for Range { - #[inline] - fn sample(&mut self, rng: &mut R) -> Sup { - self.ind_sample(rng) - } -} - -impl IndependentSample for Range { - fn ind_sample(&self, rng: &mut R) -> Sup { - SampleRange::sample_range(self, rng) - } -} - -impl fmt::Debug for Range { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Range") - .field("low", &self.low) - .field("range", &self.range) - .field("accept_zone", &self.accept_zone) - .finish() - } -} - -/// The helper trait for types that have a sensible way to sample -/// uniformly between two values. This should not be used directly, -/// and is only to facilitate `Range`. -#[doc(hidden)] -pub trait SampleRange: Sized { - /// Construct the `Range` object that `sample_range` - /// requires. This should not ever be called directly, only via - /// `Range::new`, which will check that `low < high`, so this - /// function doesn't have to repeat the check. - fn construct_range(low: Self, high: Self) -> Range; - - /// Sample a value from the given `Range` with the given `Rng` as - /// a source of randomness. - fn sample_range(r: &Range, rng: &mut R) -> Self; -} - -macro_rules! integer_impl { - ($ty:ident, $unsigned:ident) => { - impl SampleRange for $ty { - // we play free and fast with unsigned vs signed here - // (when $ty is signed), but that's fine, since the - // contract of this macro is for $ty and $unsigned to be - // "bit-equal", so casting between them is a no-op & a - // bijection. - - fn construct_range(low: $ty, high: $ty) -> Range<$ty> { - let range = (high as $unsigned).wrapping_sub(low as $unsigned); - let unsigned_max: $unsigned = $unsigned::max_value(); - - // this is the largest number that fits into $unsigned - // that `range` divides evenly, so, if we've sampled - // `n` uniformly from this region, then `n % range` is - // uniform in [0, range) - let zone = unsigned_max - unsigned_max % range; - - Range { - low, - range: range as $ty, - accept_zone: zone as $ty - } - } - #[inline] - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - loop { - // rejection sample - let v = rng.gen::<$unsigned>(); - // until we find something that fits into the - // region which r.range evenly divides (this will - // be uniformly distributed) - if v < r.accept_zone as $unsigned { - // and return it, with some adjustments - return r.low.wrapping_add((v % r.range as $unsigned) as $ty); - } - } - } - } - } -} - -integer_impl! { i8, u8 } -integer_impl! { i16, u16 } -integer_impl! { i32, u32 } -integer_impl! { i64, u64 } -integer_impl! { isize, usize } -integer_impl! { u8, u8 } -integer_impl! { u16, u16 } -integer_impl! { u32, u32 } -integer_impl! { u64, u64 } -integer_impl! { usize, usize } - -macro_rules! float_impl { - ($ty:ty) => { - impl SampleRange for $ty { - fn construct_range(low: $ty, high: $ty) -> Range<$ty> { - Range { - low, - range: high - low, - accept_zone: 0.0 // unused - } - } - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - r.low + r.range * rng.gen::<$ty>() - } - } - } -} - -float_impl! { f32 } -float_impl! { f64 } - -#[cfg(test)] -mod tests { - use distributions::{IndependentSample, Sample}; - use super::Range; - - #[should_panic] - #[test] - fn test_range_bad_limits_equal() { - Range::new(10, 10); - } - #[should_panic] - #[test] - fn test_range_bad_limits_flipped() { - Range::new(10, 5); - } - - #[test] - fn test_integers() { - let mut rng = ::test::rng(); - macro_rules! t { - ($($ty:ident),*) => {{ - $( - let v: &[($ty, $ty)] = &[(0, 10), - (10, 127), - ($ty::min_value(), $ty::max_value())]; - for &(low, high) in v { - let mut sampler: Range<$ty> = Range::new(low, high); - for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); - assert!(low <= v && v < high); - } - } - )* - }} - } - t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize) - } - - #[test] - fn test_floats() { - let mut rng = ::test::rng(); - macro_rules! t { - ($($ty:ty),*) => {{ - $( - let v: &[($ty, $ty)] = &[(0.0, 100.0), - (-1e35, -1e25), - (1e-35, 1e-25), - (-1e35, 1e35)]; - for &(low, high) in v { - let mut sampler: Range<$ty> = Range::new(low, high); - for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); - assert!(low <= v && v < high); - } - } - )* - }} - } - - t!(f32, f64) - } - -} diff --git a/src/librand/distributions/ziggurat_tables.rs b/src/librand/distributions/ziggurat_tables.rs deleted file mode 100644 index 7dfb0f131a2c5..0000000000000 --- a/src/librand/distributions/ziggurat_tables.rs +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Tables for distributions which are sampled using the ziggurat -// algorithm. Autogenerated by `ziggurat_tables.py`. - -pub type ZigTable = &'static [f64; 257]; -pub const ZIG_NORM_R: f64 = 3.654152885361008796; -#[rustfmt_skip] -pub static ZIG_NORM_X: [f64; 257] = - [3.910757959537090045, 3.654152885361008796, 3.449278298560964462, 3.320244733839166074, - 3.224575052047029100, 3.147889289517149969, 3.083526132001233044, 3.027837791768635434, - 2.978603279880844834, 2.934366867207854224, 2.894121053612348060, 2.857138730872132548, - 2.822877396825325125, 2.790921174000785765, 2.760944005278822555, 2.732685359042827056, - 2.705933656121858100, 2.680514643284522158, 2.656283037575502437, 2.633116393630324570, - 2.610910518487548515, 2.589575986706995181, 2.569035452680536569, 2.549221550323460761, - 2.530075232158516929, 2.511544441625342294, 2.493583041269680667, 2.476149939669143318, - 2.459208374333311298, 2.442725318198956774, 2.426670984935725972, 2.411018413899685520, - 2.395743119780480601, 2.380822795170626005, 2.366237056715818632, 2.351967227377659952, - 2.337996148795031370, 2.324308018869623016, 2.310888250599850036, 2.297723348901329565, - 2.284800802722946056, 2.272108990226823888, 2.259637095172217780, 2.247375032945807760, - 2.235313384928327984, 2.223443340090905718, 2.211756642882544366, 2.200245546609647995, - 2.188902771624720689, 2.177721467738641614, 2.166695180352645966, 2.155817819875063268, - 2.145083634046203613, 2.134487182844320152, 2.124023315687815661, 2.113687150684933957, - 2.103474055713146829, 2.093379631137050279, 2.083399693996551783, 2.073530263516978778, - 2.063767547809956415, 2.054107931648864849, 2.044547965215732788, 2.035084353727808715, - 2.025713947862032960, 2.016433734904371722, 2.007240830558684852, 1.998132471356564244, - 1.989106007615571325, 1.980158896898598364, 1.971288697931769640, 1.962493064942461896, - 1.953769742382734043, 1.945116560006753925, 1.936531428273758904, 1.928012334050718257, - 1.919557336591228847, 1.911164563769282232, 1.902832208548446369, 1.894558525668710081, - 1.886341828534776388, 1.878180486290977669, 1.870072921069236838, 1.862017605397632281, - 1.854013059758148119, 1.846057850283119750, 1.838150586580728607, 1.830289919680666566, - 1.822474540091783224, 1.814703175964167636, 1.806974591348693426, 1.799287584547580199, - 1.791640986550010028, 1.784033659547276329, 1.776464495522344977, 1.768932414909077933, - 1.761436365316706665, 1.753975320315455111, 1.746548278279492994, 1.739154261283669012, - 1.731792314050707216, 1.724461502945775715, 1.717160915015540690, 1.709889657069006086, - 1.702646854797613907, 1.695431651932238548, 1.688243209434858727, 1.681080704722823338, - 1.673943330923760353, 1.666830296159286684, 1.659740822855789499, 1.652674147080648526, - 1.645629517902360339, 1.638606196773111146, 1.631603456932422036, 1.624620582830568427, - 1.617656869570534228, 1.610711622367333673, 1.603784156023583041, 1.596873794420261339, - 1.589979870021648534, 1.583101723393471438, 1.576238702733332886, 1.569390163412534456, - 1.562555467528439657, 1.555733983466554893, 1.548925085471535512, 1.542128153226347553, - 1.535342571438843118, 1.528567729435024614, 1.521803020758293101, 1.515047842773992404, - 1.508301596278571965, 1.501563685112706548, 1.494833515777718391, 1.488110497054654369, - 1.481394039625375747, 1.474683555695025516, 1.467978458615230908, 1.461278162507407830, - 1.454582081885523293, 1.447889631277669675, 1.441200224845798017, 1.434513276002946425, - 1.427828197027290358, 1.421144398672323117, 1.414461289772464658, 1.407778276843371534, - 1.401094763676202559, 1.394410150925071257, 1.387723835686884621, 1.381035211072741964, - 1.374343665770030531, 1.367648583594317957, 1.360949343030101844, 1.354245316759430606, - 1.347535871177359290, 1.340820365893152122, 1.334098153216083604, 1.327368577624624679, - 1.320630975217730096, 1.313884673146868964, 1.307128989027353860, 1.300363230327433728, - 1.293586693733517645, 1.286798664489786415, 1.279998415710333237, 1.273185207661843732, - 1.266358287014688333, 1.259516886060144225, 1.252660221891297887, 1.245787495544997903, - 1.238897891102027415, 1.231990574742445110, 1.225064693752808020, 1.218119375481726552, - 1.211153726239911244, 1.204166830140560140, 1.197157747875585931, 1.190125515422801650, - 1.183069142678760732, 1.175987612011489825, 1.168879876726833800, 1.161744859441574240, - 1.154581450355851802, 1.147388505416733873, 1.140164844363995789, 1.132909248648336975, - 1.125620459211294389, 1.118297174115062909, 1.110938046009249502, 1.103541679420268151, - 1.096106627847603487, 1.088631390649514197, 1.081114409698889389, 1.073554065787871714, - 1.065948674757506653, 1.058296483326006454, 1.050595664586207123, 1.042844313139370538, - 1.035040439828605274, 1.027181966030751292, 1.019266717460529215, 1.011292417434978441, - 1.003256679539591412, 0.995156999629943084, 0.986990747093846266, 0.978755155288937750, - 0.970447311058864615, 0.962064143217605250, 0.953602409875572654, 0.945058684462571130, - 0.936429340280896860, 0.927710533396234771, 0.918898183643734989, 0.909987953490768997, - 0.900975224455174528, 0.891855070726792376, 0.882622229578910122, 0.873271068082494550, - 0.863795545546826915, 0.854189171001560554, 0.844444954902423661, 0.834555354079518752, - 0.824512208745288633, 0.814306670128064347, 0.803929116982664893, 0.793369058833152785, - 0.782615023299588763, 0.771654424216739354, 0.760473406422083165, 0.749056662009581653, - 0.737387211425838629, 0.725446140901303549, 0.713212285182022732, 0.700661841097584448, - 0.687767892786257717, 0.674499822827436479, 0.660822574234205984, 0.646695714884388928, - 0.632072236375024632, 0.616896989996235545, 0.601104617743940417, 0.584616766093722262, - 0.567338257040473026, 0.549151702313026790, 0.529909720646495108, 0.509423329585933393, - 0.487443966121754335, 0.463634336771763245, 0.437518402186662658, 0.408389134588000746, - 0.375121332850465727, 0.335737519180459465, 0.286174591747260509, 0.215241895913273806, - 0.000000000000000000]; -#[rustfmt_skip] -pub static ZIG_NORM_F: [f64; 257] = - [0.000477467764586655, 0.001260285930498598, 0.002609072746106363, 0.004037972593371872, - 0.005522403299264754, 0.007050875471392110, 0.008616582769422917, 0.010214971439731100, - 0.011842757857943104, 0.013497450601780807, 0.015177088307982072, 0.016880083152595839, - 0.018605121275783350, 0.020351096230109354, 0.022117062707379922, 0.023902203305873237, - 0.025705804008632656, 0.027527235669693315, 0.029365939758230111, 0.031221417192023690, - 0.033093219458688698, 0.034980941461833073, 0.036884215688691151, 0.038802707404656918, - 0.040736110656078753, 0.042684144916619378, 0.044646552251446536, 0.046623094902089664, - 0.048613553216035145, 0.050617723861121788, 0.052635418276973649, 0.054666461325077916, - 0.056710690106399467, 0.058767952921137984, 0.060838108349751806, 0.062921024437977854, - 0.065016577971470438, 0.067124653828023989, 0.069245144397250269, 0.071377949059141965, - 0.073522973714240991, 0.075680130359194964, 0.077849336702372207, 0.080030515814947509, - 0.082223595813495684, 0.084428509570654661, 0.086645194450867782, 0.088873592068594229, - 0.091113648066700734, 0.093365311913026619, 0.095628536713353335, 0.097903279039215627, - 0.100189498769172020, 0.102487158942306270, 0.104796225622867056, 0.107116667775072880, - 0.109448457147210021, 0.111791568164245583, 0.114145977828255210, 0.116511665626037014, - 0.118888613443345698, 0.121276805485235437, 0.123676228202051403, 0.126086870220650349, - 0.128508722280473636, 0.130941777174128166, 0.133386029692162844, 0.135841476571757352, - 0.138308116449064322, 0.140785949814968309, 0.143274978974047118, 0.145775208006537926, - 0.148286642733128721, 0.150809290682410169, 0.153343161060837674, 0.155888264725064563, - 0.158444614156520225, 0.161012223438117663, 0.163591108232982951, 0.166181285765110071, - 0.168782774801850333, 0.171395595638155623, 0.174019770082499359, 0.176655321444406654, - 0.179302274523530397, 0.181960655600216487, 0.184630492427504539, 0.187311814224516926, - 0.190004651671193070, 0.192709036904328807, 0.195425003514885592, 0.198152586546538112, - 0.200891822495431333, 0.203642749311121501, 0.206405406398679298, 0.209179834621935651, - 0.211966076307852941, 0.214764175252008499, 0.217574176725178370, 0.220396127481011589, - 0.223230075764789593, 0.226076071323264877, 0.228934165415577484, 0.231804410825248525, - 0.234686861873252689, 0.237581574432173676, 0.240488605941449107, 0.243408015423711988, - 0.246339863502238771, 0.249284212419516704, 0.252241126056943765, 0.255210669955677150, - 0.258192911338648023, 0.261187919133763713, 0.264195763998317568, 0.267216518344631837, - 0.270250256366959984, 0.273297054069675804, 0.276356989296781264, 0.279430141762765316, - 0.282516593084849388, 0.285616426816658109, 0.288729728483353931, 0.291856585618280984, - 0.294997087801162572, 0.298151326697901342, 0.301319396102034120, 0.304501391977896274, - 0.307697412505553769, 0.310907558127563710, 0.314131931597630143, 0.317370638031222396, - 0.320623784958230129, 0.323891482377732021, 0.327173842814958593, 0.330470981380537099, - 0.333783015832108509, 0.337110066638412809, 0.340452257045945450, 0.343809713148291340, - 0.347182563958251478, 0.350570941482881204, 0.353974980801569250, 0.357394820147290515, - 0.360830600991175754, 0.364282468130549597, 0.367750569780596226, 0.371235057669821344, - 0.374736087139491414, 0.378253817247238111, 0.381788410875031348, 0.385340034841733958, - 0.388908860020464597, 0.392495061461010764, 0.396098818517547080, 0.399720314981931668, - 0.403359739222868885, 0.407017284331247953, 0.410693148271983222, 0.414387534042706784, - 0.418100649839684591, 0.421832709231353298, 0.425583931339900579, 0.429354541031341519, - 0.433144769114574058, 0.436954852549929273, 0.440785034667769915, 0.444635565397727750, - 0.448506701509214067, 0.452398706863882505, 0.456311852680773566, 0.460246417814923481, - 0.464202689050278838, 0.468180961407822172, 0.472181538469883255, 0.476204732721683788, - 0.480250865911249714, 0.484320269428911598, 0.488413284707712059, 0.492530263646148658, - 0.496671569054796314, 0.500837575128482149, 0.505028667945828791, 0.509245245998136142, - 0.513487720749743026, 0.517756517232200619, 0.522052074674794864, 0.526374847174186700, - 0.530725304406193921, 0.535103932383019565, 0.539511234259544614, 0.543947731192649941, - 0.548413963257921133, 0.552910490428519918, 0.557437893621486324, 0.561996775817277916, - 0.566587763258951771, 0.571211506738074970, 0.575868682975210544, 0.580559996103683473, - 0.585286179266300333, 0.590047996335791969, 0.594846243770991268, 0.599681752622167719, - 0.604555390700549533, 0.609468064928895381, 0.614420723892076803, 0.619414360609039205, - 0.624450015550274240, 0.629528779928128279, 0.634651799290960050, 0.639820277456438991, - 0.645035480824251883, 0.650298743114294586, 0.655611470583224665, 0.660975147780241357, - 0.666391343912380640, 0.671861719900766374, 0.677388036222513090, 0.682972161648791376, - 0.688616083008527058, 0.694321916130032579, 0.700091918140490099, 0.705928501336797409, - 0.711834248882358467, 0.717811932634901395, 0.723864533472881599, 0.729995264565802437, - 0.736207598131266683, 0.742505296344636245, 0.748892447223726720, 0.755373506511754500, - 0.761953346841546475, 0.768637315803334831, 0.775431304986138326, 0.782341832659861902, - 0.789376143571198563, 0.796542330428254619, 0.803849483176389490, 0.811307874318219935, - 0.818929191609414797, 0.826726833952094231, 0.834716292992930375, 0.842915653118441077, - 0.851346258465123684, 0.860033621203008636, 0.869008688043793165, 0.878309655816146839, - 0.887984660763399880, 0.898095921906304051, 0.908726440060562912, 0.919991505048360247, - 0.932060075968990209, 0.945198953453078028, 0.959879091812415930, 0.977101701282731328, - 1.000000000000000000]; -pub const ZIG_EXP_R: f64 = 7.697117470131050077; -#[rustfmt_skip] -pub static ZIG_EXP_X: [f64; 257] = - [8.697117470131052741, 7.697117470131050077, 6.941033629377212577, 6.478378493832569696, - 6.144164665772472667, 5.882144315795399869, 5.666410167454033697, 5.482890627526062488, - 5.323090505754398016, 5.181487281301500047, 5.054288489981304089, 4.938777085901250530, - 4.832939741025112035, 4.735242996601741083, 4.644491885420085175, 4.559737061707351380, - 4.480211746528421912, 4.405287693473573185, 4.334443680317273007, 4.267242480277365857, - 4.203313713735184365, 4.142340865664051464, 4.084051310408297830, 4.028208544647936762, - 3.974606066673788796, 3.923062500135489739, 3.873417670399509127, 3.825529418522336744, - 3.779270992411667862, 3.734528894039797375, 3.691201090237418825, 3.649195515760853770, - 3.608428813128909507, 3.568825265648337020, 3.530315889129343354, 3.492837654774059608, - 3.456332821132760191, 3.420748357251119920, 3.386035442460300970, 3.352149030900109405, - 3.319047470970748037, 3.286692171599068679, 3.255047308570449882, 3.224079565286264160, - 3.193757903212240290, 3.164053358025972873, 3.134938858084440394, 3.106389062339824481, - 3.078380215254090224, 3.050890016615455114, 3.023897504455676621, 2.997382949516130601, - 2.971327759921089662, 2.945714394895045718, 2.920526286512740821, 2.895747768600141825, - 2.871364012015536371, 2.847360965635188812, 2.823725302450035279, 2.800444370250737780, - 2.777506146439756574, 2.754899196562344610, 2.732612636194700073, 2.710636095867928752, - 2.688959688741803689, 2.667573980773266573, 2.646469963151809157, 2.625639026797788489, - 2.605072938740835564, 2.584763820214140750, 2.564704126316905253, 2.544886627111869970, - 2.525304390037828028, 2.505950763528594027, 2.486819361740209455, 2.467904050297364815, - 2.449198932978249754, 2.430698339264419694, 2.412396812688870629, 2.394289099921457886, - 2.376370140536140596, 2.358635057409337321, 2.341079147703034380, 2.323697874390196372, - 2.306486858283579799, 2.289441870532269441, 2.272558825553154804, 2.255833774367219213, - 2.239262898312909034, 2.222842503111036816, 2.206569013257663858, 2.190438966723220027, - 2.174449009937774679, 2.158595893043885994, 2.142876465399842001, 2.127287671317368289, - 2.111826546019042183, 2.096490211801715020, 2.081275874393225145, 2.066180819490575526, - 2.051202409468584786, 2.036338080248769611, 2.021585338318926173, 2.006941757894518563, - 1.992404978213576650, 1.977972700957360441, 1.963642687789548313, 1.949412758007184943, - 1.935280786297051359, 1.921244700591528076, 1.907302480018387536, 1.893452152939308242, - 1.879691795072211180, 1.866019527692827973, 1.852433515911175554, 1.838931967018879954, - 1.825513128903519799, 1.812175288526390649, 1.798916770460290859, 1.785735935484126014, - 1.772631179231305643, 1.759600930889074766, 1.746643651946074405, 1.733757834985571566, - 1.720942002521935299, 1.708194705878057773, 1.695514524101537912, 1.682900062917553896, - 1.670349953716452118, 1.657862852574172763, 1.645437439303723659, 1.633072416535991334, - 1.620766508828257901, 1.608518461798858379, 1.596327041286483395, 1.584191032532688892, - 1.572109239386229707, 1.560080483527888084, 1.548103603714513499, 1.536177455041032092, - 1.524300908219226258, 1.512472848872117082, 1.500692176842816750, 1.488957805516746058, - 1.477268661156133867, 1.465623682245745352, 1.454021818848793446, 1.442462031972012504, - 1.430943292938879674, 1.419464582769983219, 1.408024891569535697, 1.396623217917042137, - 1.385258568263121992, 1.373929956328490576, 1.362636402505086775, 1.351376933258335189, - 1.340150580529504643, 1.328956381137116560, 1.317793376176324749, 1.306660610415174117, - 1.295557131686601027, 1.284481990275012642, 1.273434238296241139, 1.262412929069615330, - 1.251417116480852521, 1.240445854334406572, 1.229498195693849105, 1.218573192208790124, - 1.207669893426761121, 1.196787346088403092, 1.185924593404202199, 1.175080674310911677, - 1.164254622705678921, 1.153445466655774743, 1.142652227581672841, 1.131873919411078511, - 1.121109547701330200, 1.110358108727411031, 1.099618588532597308, 1.088889961938546813, - 1.078171191511372307, 1.067461226479967662, 1.056759001602551429, 1.046063435977044209, - 1.035373431790528542, 1.024687873002617211, 1.014005623957096480, 1.003325527915696735, - 0.992646405507275897, 0.981967053085062602, 0.971286240983903260, 0.960602711668666509, - 0.949915177764075969, 0.939222319955262286, 0.928522784747210395, 0.917815182070044311, - 0.907098082715690257, 0.896370015589889935, 0.885629464761751528, 0.874874866291025066, - 0.864104604811004484, 0.853317009842373353, 0.842510351810368485, 0.831682837734273206, - 0.820832606554411814, 0.809957724057418282, 0.799056177355487174, 0.788125868869492430, - 0.777164609759129710, 0.766170112735434672, 0.755139984181982249, 0.744071715500508102, - 0.732962673584365398, 0.721810090308756203, 0.710611050909655040, 0.699362481103231959, - 0.688061132773747808, 0.676703568029522584, 0.665286141392677943, 0.653804979847664947, - 0.642255960424536365, 0.630634684933490286, 0.618936451394876075, 0.607156221620300030, - 0.595288584291502887, 0.583327712748769489, 0.571267316532588332, 0.559100585511540626, - 0.546820125163310577, 0.534417881237165604, 0.521885051592135052, 0.509211982443654398, - 0.496388045518671162, 0.483401491653461857, 0.470239275082169006, 0.456886840931420235, - 0.443327866073552401, 0.429543940225410703, 0.415514169600356364, 0.401214678896277765, - 0.386617977941119573, 0.371692145329917234, 0.356399760258393816, 0.340696481064849122, - 0.324529117016909452, 0.307832954674932158, 0.290527955491230394, 0.272513185478464703, - 0.253658363385912022, 0.233790483059674731, 0.212671510630966620, 0.189958689622431842, - 0.165127622564187282, 0.137304980940012589, 0.104838507565818778, 0.063852163815001570, - 0.000000000000000000]; -#[rustfmt_skip] -pub static ZIG_EXP_F: [f64; 257] = - [0.000167066692307963, 0.000454134353841497, 0.000967269282327174, 0.001536299780301573, - 0.002145967743718907, 0.002788798793574076, 0.003460264777836904, 0.004157295120833797, - 0.004877655983542396, 0.005619642207205489, 0.006381905937319183, 0.007163353183634991, - 0.007963077438017043, 0.008780314985808977, 0.009614413642502212, 0.010464810181029981, - 0.011331013597834600, 0.012212592426255378, 0.013109164931254991, 0.014020391403181943, - 0.014945968011691148, 0.015885621839973156, 0.016839106826039941, 0.017806200410911355, - 0.018786700744696024, 0.019780424338009740, 0.020787204072578114, 0.021806887504283581, - 0.022839335406385240, 0.023884420511558174, 0.024942026419731787, 0.026012046645134221, - 0.027094383780955803, 0.028188948763978646, 0.029295660224637411, 0.030414443910466622, - 0.031545232172893622, 0.032687963508959555, 0.033842582150874358, 0.035009037697397431, - 0.036187284781931443, 0.037377282772959382, 0.038578995503074871, 0.039792391023374139, - 0.041017441380414840, 0.042254122413316254, 0.043502413568888197, 0.044762297732943289, - 0.046033761076175184, 0.047316792913181561, 0.048611385573379504, 0.049917534282706379, - 0.051235237055126281, 0.052564494593071685, 0.053905310196046080, 0.055257689676697030, - 0.056621641283742870, 0.057997175631200659, 0.059384305633420280, 0.060783046445479660, - 0.062193415408541036, 0.063615431999807376, 0.065049117786753805, 0.066494496385339816, - 0.067951593421936643, 0.069420436498728783, 0.070901055162371843, 0.072393480875708752, - 0.073897746992364746, 0.075413888734058410, 0.076941943170480517, 0.078481949201606435, - 0.080033947542319905, 0.081597980709237419, 0.083174093009632397, 0.084762330532368146, - 0.086362741140756927, 0.087975374467270231, 0.089600281910032886, 0.091237516631040197, - 0.092887133556043569, 0.094549189376055873, 0.096223742550432825, 0.097910853311492213, - 0.099610583670637132, 0.101322997425953631, 0.103048160171257702, 0.104786139306570145, - 0.106537004050001632, 0.108300825451033755, 0.110077676405185357, 0.111867631670056283, - 0.113670767882744286, 0.115487163578633506, 0.117316899211555525, 0.119160057175327641, - 0.121016721826674792, 0.122886979509545108, 0.124770918580830933, 0.126668629437510671, - 0.128580204545228199, 0.130505738468330773, 0.132445327901387494, 0.134399071702213602, - 0.136367070926428829, 0.138349428863580176, 0.140346251074862399, 0.142357645432472146, - 0.144383722160634720, 0.146424593878344889, 0.148480375643866735, 0.150551185001039839, - 0.152637142027442801, 0.154738369384468027, 0.156854992369365148, 0.158987138969314129, - 0.161134939917591952, 0.163298528751901734, 0.165478041874935922, 0.167673618617250081, - 0.169885401302527550, 0.172113535315319977, 0.174358169171353411, 0.176619454590494829, - 0.178897546572478278, 0.181192603475496261, 0.183504787097767436, 0.185834262762197083, - 0.188181199404254262, 0.190545769663195363, 0.192928149976771296, 0.195328520679563189, - 0.197747066105098818, 0.200183974691911210, 0.202639439093708962, 0.205113656293837654, - 0.207606827724221982, 0.210119159388988230, 0.212650861992978224, 0.215202151075378628, - 0.217773247148700472, 0.220364375843359439, 0.222975768058120111, 0.225607660116683956, - 0.228260293930716618, 0.230933917169627356, 0.233628783437433291, 0.236345152457059560, - 0.239083290262449094, 0.241843469398877131, 0.244625969131892024, 0.247431075665327543, - 0.250259082368862240, 0.253110290015629402, 0.255985007030415324, 0.258883549749016173, - 0.261806242689362922, 0.264753418835062149, 0.267725419932044739, 0.270722596799059967, - 0.273745309652802915, 0.276793928448517301, 0.279868833236972869, 0.282970414538780746, - 0.286099073737076826, 0.289255223489677693, 0.292439288161892630, 0.295651704281261252, - 0.298892921015581847, 0.302163400675693528, 0.305463619244590256, 0.308794066934560185, - 0.312155248774179606, 0.315547685227128949, 0.318971912844957239, 0.322428484956089223, - 0.325917972393556354, 0.329440964264136438, 0.332998068761809096, 0.336589914028677717, - 0.340217149066780189, 0.343880444704502575, 0.347580494621637148, 0.351318016437483449, - 0.355093752866787626, 0.358908472948750001, 0.362762973354817997, 0.366658079781514379, - 0.370594648435146223, 0.374573567615902381, 0.378595759409581067, 0.382662181496010056, - 0.386773829084137932, 0.390931736984797384, 0.395136981833290435, 0.399390684475231350, - 0.403694012530530555, 0.408048183152032673, 0.412454465997161457, 0.416914186433003209, - 0.421428728997616908, 0.425999541143034677, 0.430628137288459167, 0.435316103215636907, - 0.440065100842354173, 0.444876873414548846, 0.449753251162755330, 0.454696157474615836, - 0.459707615642138023, 0.464789756250426511, 0.469944825283960310, 0.475175193037377708, - 0.480483363930454543, 0.485871987341885248, 0.491343869594032867, 0.496901987241549881, - 0.502549501841348056, 0.508289776410643213, 0.514126393814748894, 0.520063177368233931, - 0.526104213983620062, 0.532253880263043655, 0.538516872002862246, 0.544898237672440056, - 0.551403416540641733, 0.558038282262587892, 0.564809192912400615, 0.571723048664826150, - 0.578787358602845359, 0.586010318477268366, 0.593400901691733762, 0.600968966365232560, - 0.608725382079622346, 0.616682180915207878, 0.624852738703666200, 0.633251994214366398, - 0.641896716427266423, 0.650805833414571433, 0.660000841079000145, 0.669506316731925177, - 0.679350572264765806, 0.689566496117078431, 0.700192655082788606, 0.711274760805076456, - 0.722867659593572465, 0.735038092431424039, 0.747868621985195658, 0.761463388849896838, - 0.775956852040116218, 0.791527636972496285, 0.808421651523009044, 0.826993296643051101, - 0.847785500623990496, 0.871704332381204705, 0.900469929925747703, 0.938143680862176477, - 1.000000000000000000]; diff --git a/src/librand/isaac.rs b/src/librand/isaac.rs deleted file mode 100644 index 96ce0905e384d..0000000000000 --- a/src/librand/isaac.rs +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The ISAAC random number generator. - -#![allow(non_camel_case_types)] - -use core::fmt; -use core::slice; -use core::iter::repeat; -use core::num::Wrapping as w; - -use {Rand, Rng, SeedableRng}; - -type w32 = w; -type w64 = w; - -const RAND_SIZE_LEN: usize = 8; -const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; -const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; - -/// A random number generator that uses the ISAAC algorithm[1]. -/// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. -/// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) -#[derive(Copy)] -pub struct IsaacRng { - cnt: u32, - rsl: [w32; RAND_SIZE_USIZE], - mem: [w32; RAND_SIZE_USIZE], - a: w32, - b: w32, - c: w32, -} - -impl fmt::Debug for IsaacRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("IsaacRng") - .field("cnt", &self.cnt) - .field("rsl", &self.rsl.iter()) - .field("mem", &self.mem.iter()) - .field("a", &self.a) - .field("b", &self.b) - .field("c", &self.c) - .finish() - } -} - -static EMPTY: IsaacRng = IsaacRng { - cnt: 0, - rsl: [w(0); RAND_SIZE_USIZE], - mem: [w(0); RAND_SIZE_USIZE], - a: w(0), - b: w(0), - c: w(0), -}; - -impl IsaacRng { - /// Create an ISAAC random number generator using the default - /// fixed seed. - pub fn new_unseeded() -> IsaacRng { - let mut rng = EMPTY; - rng.init(false); - rng - } - - /// Initializes `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a = a ^ (b << 11); - d = d + a; - b = b + c; - - b = b ^ (c >> 2); - e = e + b; - c = c + d; - - c = c ^ (d << 8); - f = f + c; - d = d + e; - - d = d ^ (e >> 16); - g = g + d; - e = e + f; - - e = e ^ (f << 10); - h = h + e; - f = f + g; - - f = f ^ (g >> 4); - a = a + f; - g = g + h; - - g = g ^ (h << 8); - b = b + g; - h = h + a; - - h = h ^ (a >> 9); - c = c + h; - a = a + b; - }} - } - - for _ in 0..4 { - mix!(); - } - - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE_USIZE).step_by(8) { - a = a + $arr[i]; - b = b + $arr[i + 1]; - c = c + $arr[i + 2]; - d = d + $arr[i + 3]; - e = e + $arr[i + 4]; - f = f + $arr[i + 5]; - g = g + $arr[i + 6]; - h = h + $arr[i + 7]; - mix!(); - self.mem[i] = a; - self.mem[i + 1] = b; - self.mem[i + 2] = c; - self.mem[i + 3] = d; - self.mem[i + 4] = e; - self.mem[i + 5] = f; - self.mem[i + 6] = g; - self.mem[i + 7] = h; - } - }} - } - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE_USIZE).step_by(8) { - mix!(); - self.mem[i] = a; - self.mem[i + 1] = b; - self.mem[i + 2] = c; - self.mem[i + 3] = d; - self.mem[i + 4] = e; - self.mem[i + 5] = f; - self.mem[i + 6] = g; - self.mem[i + 7] = h; - } - } - - self.isaac(); - } - - /// Refills the output buffer (`self.rsl`) - #[inline] - fn isaac(&mut self) { - self.c = self.c + w(1); - // abbreviations - let mut a = self.a; - let mut b = self.b + self.c; - - const MIDPOINT: usize = RAND_SIZE_USIZE / 2; - - macro_rules! ind { - ($x:expr) => (self.mem[($x >> 2).0 as usize & (RAND_SIZE_USIZE - 1)] ) - } - - let r = [(0, MIDPOINT), (MIDPOINT, 0)]; - for &(mr_offset, m2_offset) in &r { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a << $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a >> $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } - - for i in (0..MIDPOINT).step_by(4) { - rngstepp!(i + 0, 13); - rngstepn!(i + 1, 6); - rngstepp!(i + 2, 2); - rngstepn!(i + 3, 16); - } - } - - self.a = a; - self.b = b; - self.cnt = RAND_SIZE; - } -} - -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for IsaacRng { - fn clone(&self) -> IsaacRng { - *self - } -} - -impl Rng for IsaacRng { - #[inline] - fn next_u32(&mut self) -> u32 { - if self.cnt == 0 { - // make some more numbers - self.isaac(); - } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!(self.cnt < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[(self.cnt % RAND_SIZE) as usize].0 - } -} - -impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - fn reseed(&mut self, seed: &'a [u32]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().cloned().chain(repeat(0)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); - } - - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u32]) -> IsaacRng { - let mut rng = EMPTY; - rng.reseed(seed); - rng - } -} - -impl Rand for IsaacRng { - fn rand(other: &mut R) -> IsaacRng { - let mut ret = EMPTY; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } -} - -const RAND_SIZE_64_LEN: usize = 8; -const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; - -/// A random number generator that uses ISAAC-64[1], the 64-bit -/// variant of the ISAAC algorithm. -/// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. -/// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) -#[derive(Copy)] -pub struct Isaac64Rng { - cnt: usize, - rsl: [w64; RAND_SIZE_64], - mem: [w64; RAND_SIZE_64], - a: w64, - b: w64, - c: w64, -} - -impl fmt::Debug for Isaac64Rng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Isaac64Rng") - .field("cnt", &self.cnt) - .field("rsl", &self.rsl.iter()) - .field("mem", &self.mem.iter()) - .field("a", &self.a) - .field("b", &self.b) - .field("c", &self.c) - .finish() - } -} - -static EMPTY_64: Isaac64Rng = Isaac64Rng { - cnt: 0, - rsl: [w(0); RAND_SIZE_64], - mem: [w(0); RAND_SIZE_64], - a: w(0), - b: w(0), - c: w(0), -}; - -impl Isaac64Rng { - /// Create a 64-bit ISAAC random number generator using the - /// default fixed seed. - pub fn new_unseeded() -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.init(false); - rng - } - - /// Initializes `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - macro_rules! init { - ($var:ident) => ( - let mut $var = w(0x9e3779b97f4a7c13); - ) - } - init!(a); - init!(b); - init!(c); - init!(d); - init!(e); - init!(f); - init!(g); - init!(h); - - macro_rules! mix { - () => {{ - a = a - e; - f = f ^ (h >> 9); - h = h + a; - - b = b - f; - g = g ^ (a << 9); - a = a + b; - - c = c - g; - h = h ^ (b >> 23); - b = b + c; - - d = d - h; - a = a ^ (c << 15); - c = c + d; - - e = e - a; - b = b ^ (d >> 14); - d = d + e; - - f = f - b; - c = c ^ (e << 20); - e = e + f; - - g = g - c; - d = d ^ (f >> 17); - f = f + g; - - h = h - d; - e = e ^ (g << 14); - g = g + h; - }} - } - - for _ in 0..4 { - mix!(); - } - - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - a = a + $arr[i]; - b = b + $arr[i + 1]; - c = c + $arr[i + 2]; - d = d + $arr[i + 3]; - e = e + $arr[i + 4]; - f = f + $arr[i + 5]; - g = g + $arr[i + 6]; - h = h + $arr[i + 7]; - mix!(); - self.mem[i] = a; - self.mem[i + 1] = b; - self.mem[i + 2] = c; - self.mem[i + 3] = d; - self.mem[i + 4] = e; - self.mem[i + 5] = f; - self.mem[i + 6] = g; - self.mem[i + 7] = h; - } - }} - } - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - mix!(); - self.mem[i] = a; - self.mem[i + 1] = b; - self.mem[i + 2] = c; - self.mem[i + 3] = d; - self.mem[i + 4] = e; - self.mem[i + 5] = f; - self.mem[i + 6] = g; - self.mem[i + 7] = h; - } - } - - self.isaac64(); - } - - /// Refills the output buffer (`self.rsl`) - fn isaac64(&mut self) { - self.c = self.c + w(1); - // abbreviations - let mut a = self.a; - let mut b = self.b + self.c; - const MIDPOINT: usize = RAND_SIZE_64 / 2; - const MP_VEC: [(usize, usize); 2] = [(0, MIDPOINT), (MIDPOINT, 0)]; - macro_rules! ind { - ($x:expr) => { - *self.mem.get_unchecked((($x >> 3).0 as usize) & (RAND_SIZE_64 - 1)) - } - } - - for &(mr_offset, m2_offset) in &MP_VEC { - for base in (0..MIDPOINT / 4).map(|i| i * 4) { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a << $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a >> $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - rngstepp!(0, 21); - rngstepn!(1, 5); - rngstepp!(2, 12); - rngstepn!(3, 33); - } - } - - self.a = a; - self.b = b; - self.cnt = RAND_SIZE_64; - } -} - -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for Isaac64Rng { - fn clone(&self) -> Isaac64Rng { - *self - } -} - -impl Rng for Isaac64Rng { - // FIXME #7771: having next_u32 like this should be unnecessary - #[inline] - fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 - } - - #[inline] - fn next_u64(&mut self) -> u64 { - if self.cnt == 0 { - // make some more numbers - self.isaac64(); - } - self.cnt -= 1; - - // See corresponding location in IsaacRng.next_u32 for - // explanation. - debug_assert!(self.cnt < RAND_SIZE_64); - self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 - } -} - -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().cloned().chain(repeat(0)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); - } - - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.reseed(seed); - rng - } -} - -impl Rand for Isaac64Rng { - fn rand(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - - ret.init(true); - return ret; - } -} - - -#[cfg(test)] -mod tests { - use std::prelude::v1::*; - - use {Rng, SeedableRng}; - use super::{Isaac64Rng, IsaacRng}; - - #[test] - fn test_rng_32_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); - let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - #[test] - fn test_rng_64_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_32_seeded() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); - let mut rb: IsaacRng = SeedableRng::from_seed(seed); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - #[test] - fn test_rng_64_seeded() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_32_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); - } - #[test] - fn test_rng_64_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); - } - - #[test] - #[rustfmt_skip] - fn test_rng_32_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); - // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u32()).collect::>(); - assert_eq!(v, - vec![2558573138, 873787463, 263499565, 2103644246, 3595684709, - 4203127393, 264982119, 2765226902, 2737944514, 3900253796]); - - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: IsaacRng = SeedableRng::from_seed(seed); - // skip forward to the 10000th number - for _ in 0..10000 { - rb.next_u32(); - } - - let v = (0..10).map(|_| rb.next_u32()).collect::>(); - assert_eq!(v, - vec![3676831399, 3183332890, 2834741178, 3854698763, 2717568474, - 1576568959, 3507990155, 179069555, 141456972, 2478885421]); - } - #[test] - #[rustfmt_skip] - fn test_rng_64_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u64()).collect::>(); - assert_eq!(v, - vec![547121783600835980, 14377643087320773276, 17351601304698403469, - 1238879483818134882, 11952566807690396487, 13970131091560099343, - 4469761996653280935, 15552757044682284409, 6860251611068737823, - 13722198873481261842]); - - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - // skip forward to the 10000th number - for _ in 0..10000 { - rb.next_u64(); - } - - let v = (0..10).map(|_| rb.next_u64()).collect::>(); - assert_eq!(v, - vec![18143823860592706164, 8491801882678285927, 2699425367717515619, - 17196852593171130876, 2606123525235546165, 15790932315217671084, - 596345674630742204, 9947027391921273664, 11788097613744130851, - 10391409374914919106]); - - } - - #[test] - fn test_rng_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); - for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); - } - } -} diff --git a/src/librand/lib.rs b/src/librand/lib.rs deleted file mode 100644 index 90b3020fff9d1..0000000000000 --- a/src/librand/lib.rs +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Interface to random number generators in Rust. -//! -//! This is an experimental library which lives underneath the standard library -//! in its dependency chain. This library is intended to define the interface -//! for random number generation and also provide utilities around doing so. It -//! is not recommended to use this library directly, but rather the official -//! interface through `std::rand`. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - test(attr(deny(warnings))))] -#![deny(warnings)] -#![deny(missing_debug_implementations)] -#![no_std] -#![unstable(feature = "rand", - reason = "use `rand` from crates.io", - issue = "27703")] -#![feature(core_intrinsics)] -#![feature(staged_api)] -#![feature(iterator_step_by)] -#![feature(custom_attribute)] -#![feature(specialization)] -#![allow(unused_attributes)] - -#![cfg_attr(not(test), feature(core_float))] // only necessary for no_std -#![cfg_attr(test, feature(test, rand))] - -#![allow(deprecated)] - -#[cfg(test)] -#[macro_use] -extern crate std; - -use core::fmt; -use core::f64; -use core::intrinsics; -use core::marker::PhantomData; - -pub use isaac::{Isaac64Rng, IsaacRng}; -pub use chacha::ChaChaRng; - -use distributions::{IndependentSample, Range}; -use distributions::range::SampleRange; - -#[cfg(test)] -const RAND_BENCH_N: u64 = 100; - -pub mod distributions; -pub mod isaac; -pub mod chacha; -pub mod reseeding; -mod rand_impls; - -// Temporary trait to implement a few floating-point routines -// needed by librand; this is necessary because librand doesn't -// depend on libstd. This will go away when librand is integrated -// into libstd. -#[doc(hidden)] -trait FloatMath: Sized { - fn exp(self) -> Self; - fn ln(self) -> Self; - fn sqrt(self) -> Self; - fn powf(self, n: Self) -> Self; -} - -impl FloatMath for f64 { - #[inline] - fn exp(self) -> f64 { - unsafe { intrinsics::expf64(self) } - } - - #[inline] - fn ln(self) -> f64 { - unsafe { intrinsics::logf64(self) } - } - - #[inline] - fn powf(self, n: f64) -> f64 { - unsafe { intrinsics::powf64(self, n) } - } - - #[inline] - fn sqrt(self) -> f64 { - if self < 0.0 { - f64::NAN - } else { - unsafe { intrinsics::sqrtf64(self) } - } - } -} - -/// A type that can be randomly generated using an `Rng`. -#[doc(hidden)] -pub trait Rand: Sized { - /// Generates a random instance of this type using the specified source of - /// randomness. - fn rand(rng: &mut R) -> Self; -} - -/// A random number generator. -pub trait Rng: Sized { - /// Return the next random u32. - /// - /// This rarely needs to be called directly, prefer `r.gen()` to - /// `r.next_u32()`. - // FIXME #7771: Should be implemented in terms of next_u64 - fn next_u32(&mut self) -> u32; - - /// Return the next random u64. - /// - /// By default this is implemented in terms of `next_u32`. An - /// implementation of this trait must provide at least one of - /// these two methods. Similarly to `next_u32`, this rarely needs - /// to be called directly, prefer `r.gen()` to `r.next_u64()`. - fn next_u64(&mut self) -> u64 { - ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) - } - - /// Return the next random f32 selected from the half-open - /// interval `[0, 1)`. - /// - /// By default this is implemented in terms of `next_u32`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. - fn next_f32(&mut self) -> f32 { - const MANTISSA_BITS: usize = 24; - const IGNORED_BITS: usize = 8; - const SCALE: f32 = (1u64 << MANTISSA_BITS) as f32; - - // using any more than `MANTISSA_BITS` bits will - // cause (e.g.) 0xffff_ffff to correspond to 1 - // exactly, so we need to drop some (8 for f32, 11 - // for f64) to guarantee the open end. - (self.next_u32() >> IGNORED_BITS) as f32 / SCALE - } - - /// Return the next random f64 selected from the half-open - /// interval `[0, 1)`. - /// - /// By default this is implemented in terms of `next_u64`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. - fn next_f64(&mut self) -> f64 { - const MANTISSA_BITS: usize = 53; - const IGNORED_BITS: usize = 11; - const SCALE: f64 = (1u64 << MANTISSA_BITS) as f64; - - (self.next_u64() >> IGNORED_BITS) as f64 / SCALE - } - - /// Fill `dest` with random data. - /// - /// This has a default implementation in terms of `next_u64` and - /// `next_u32`, but should be overridden by implementations that - /// offer a more efficient solution than just calling those - /// methods repeatedly. - /// - /// This method does *not* have a requirement to bear any fixed - /// relationship to the other methods, for example, it does *not* - /// have to result in the same output as progressively filling - /// `dest` with `self.gen::()`, and any such behavior should - /// not be relied upon. - /// - /// This method should guarantee that `dest` is entirely filled - /// with new data, and may panic if this is impossible - /// (e.g. reading past the end of a file that is being used as the - /// source of randomness). - fn fill_bytes(&mut self, dest: &mut [u8]) { - // this could, in theory, be done by transmuting dest to a - // [u64], but this is (1) likely to be undefined behaviour for - // LLVM, (2) has to be very careful about alignment concerns, - // (3) adds more `unsafe` that needs to be checked, (4) - // probably doesn't give much performance gain if - // optimisations are on. - let mut count = 0; - let mut num = 0; - for byte in dest { - if count == 0 { - // we could micro-optimise here by generating a u32 if - // we only need a few more bytes to fill the vector - // (i.e. at most 4). - num = self.next_u64(); - count = 8; - } - - *byte = (num & 0xff) as u8; - num >>= 8; - count -= 1; - } - } - - /// Return a random value of a `Rand` type. - #[inline(always)] - fn gen(&mut self) -> T { - Rand::rand(self) - } - - /// Return an iterator that will yield an infinite number of randomly - /// generated items. - fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> { - Generator { - rng: self, - _marker: PhantomData, - } - } - - /// Generate a random value in the range [`low`, `high`). - /// - /// This is a convenience wrapper around - /// `distributions::Range`. If this function will be called - /// repeatedly with the same arguments, one should use `Range`, as - /// that will amortize the computations that allow for perfect - /// uniformity, as they only happen on initialization. - /// - /// # Panics - /// - /// Panics if `low >= high`. - fn gen_range(&mut self, low: T, high: T) -> T { - assert!(low < high, "Rng.gen_range called with low >= high"); - Range::new(low, high).ind_sample(self) - } - - /// Return a bool with a 1 in n chance of true - fn gen_weighted_bool(&mut self, n: usize) -> bool { - n <= 1 || self.gen_range(0, n) == 0 - } - - /// Return an iterator of random characters from the set A-Z,a-z,0-9. - fn gen_ascii_chars<'a>(&'a mut self) -> AsciiGenerator<'a, Self> { - AsciiGenerator { rng: self } - } - - /// Return a random element from `values`. - /// - /// Return `None` if `values` is empty. - fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> { - if values.is_empty() { - None - } else { - Some(&values[self.gen_range(0, values.len())]) - } - } - - /// Shuffle a mutable slice in place. - fn shuffle(&mut self, values: &mut [T]) { - let mut i = values.len(); - while i >= 2 { - // invariant: elements with index >= i have been locked in place. - i -= 1; - // lock element i in place. - values.swap(i, self.gen_range(0, i + 1)); - } - } -} - -/// Iterator which will generate a stream of random items. -/// -/// This iterator is created via the `gen_iter` method on `Rng`. -pub struct Generator<'a, T, R: 'a> { - rng: &'a mut R, - _marker: PhantomData, -} - -impl<'a, T: Rand, R: Rng> Iterator for Generator<'a, T, R> { - type Item = T; - - fn next(&mut self) -> Option { - Some(self.rng.gen()) - } -} - -impl<'a, T, R: fmt::Debug> fmt::Debug for Generator<'a, T, R> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Generator") - .field("rng", &self.rng) - .finish() - } -} - -/// Iterator which will continuously generate random ascii characters. -/// -/// This iterator is created via the `gen_ascii_chars` method on `Rng`. -pub struct AsciiGenerator<'a, R: 'a> { - rng: &'a mut R, -} - -impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { - type Item = char; - - fn next(&mut self) -> Option { - const GEN_ASCII_STR_CHARSET: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - Some(*self.rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char) - } -} - -impl<'a, R: fmt::Debug> fmt::Debug for AsciiGenerator<'a, R> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("AsciiGenerator") - .field("rng", &self.rng) - .finish() - } -} - -/// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times. -pub trait SeedableRng: Rng { - /// Reseed an RNG with the given seed. - fn reseed(&mut self, _: Seed); - - /// Create a new RNG with the given seed. - fn from_seed(seed: Seed) -> Self; -} - -/// An Xorshift[1] random number -/// generator. -/// -/// The Xorshift algorithm is not suitable for cryptographic purposes -/// but is very fast. If you do not know for sure that it fits your -/// requirements, use a more secure one such as `IsaacRng` or `OsRng`. -/// -/// [1]: Marsaglia, George (July 2003). ["Xorshift -/// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of -/// Statistical Software*. Vol. 8 (Issue 14). -#[derive(Clone, Debug)] -pub struct XorShiftRng { - x: u32, - y: u32, - z: u32, - w: u32, -} - -impl XorShiftRng { - /// Creates a new XorShiftRng instance which is not seeded. - /// - /// The initial values of this RNG are constants, so all generators created - /// by this function will yield the same stream of random numbers. It is - /// highly recommended that this is created through `SeedableRng` instead of - /// this function - pub fn new_unseeded() -> XorShiftRng { - XorShiftRng { - x: 0x193a6754, - y: 0xa8a7d469, - z: 0x97830e05, - w: 0x113ba7bb, - } - } -} - -impl Rng for XorShiftRng { - #[inline] - fn next_u32(&mut self) -> u32 { - let x = self.x; - let t = x ^ (x << 11); - self.x = self.y; - self.y = self.z; - self.z = self.w; - let w = self.w; - self.w = w ^ (w >> 19) ^ (t ^ (t >> 8)); - self.w - } -} - -impl SeedableRng<[u32; 4]> for XorShiftRng { - /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. - fn reseed(&mut self, seed: [u32; 4]) { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng.reseed called with an all zero seed."); - - self.x = seed[0]; - self.y = seed[1]; - self.z = seed[2]; - self.w = seed[3]; - } - - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. - fn from_seed(seed: [u32; 4]) -> XorShiftRng { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng::from_seed called with an all zero seed."); - - XorShiftRng { - x: seed[0], - y: seed[1], - z: seed[2], - w: seed[3], - } - } -} - -impl Rand for XorShiftRng { - fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32) = rng.gen(); - while tuple == (0, 0, 0, 0) { - tuple = rng.gen(); - } - let (x, y, z, w) = tuple; - XorShiftRng { - x, - y, - z, - w, - } - } -} - -/// A wrapper for generating floating point numbers uniformly in the -/// open interval `(0,1)` (not including either endpoint). -/// -/// Use `Closed01` for the closed interval `[0,1]`, and the default -/// `Rand` implementation for `f32` and `f64` for the half-open -/// `[0,1)`. -pub struct Open01(pub F); - -impl fmt::Debug for Open01 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Open01") - .field(&self.0) - .finish() - } -} - -/// A wrapper for generating floating point numbers uniformly in the -/// closed interval `[0,1]` (including both endpoints). -/// -/// Use `Open01` for the closed interval `(0,1)`, and the default -/// `Rand` implementation of `f32` and `f64` for the half-open -/// `[0,1)`. -pub struct Closed01(pub F); - -impl fmt::Debug for Closed01 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Closed01") - .field(&self.0) - .finish() - } -} - -#[cfg(test)] -mod test { - use std::__rand as rand; - - pub struct MyRng { - inner: R, - } - - impl ::Rng for MyRng { - fn next_u32(&mut self) -> u32 { - rand::Rng::next_u32(&mut self.inner) - } - } - - pub fn rng() -> MyRng { - MyRng { inner: rand::thread_rng() } - } - - pub fn weak_rng() -> MyRng { - MyRng { inner: rand::thread_rng() } - } -} diff --git a/src/librand/rand_impls.rs b/src/librand/rand_impls.rs deleted file mode 100644 index b0d824da3ab47..0000000000000 --- a/src/librand/rand_impls.rs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of `Rand` for the built-in types. - -use core::char; -use core::mem; - -use {Rand, Rng}; - -impl Rand for isize { - #[inline] - fn rand(rng: &mut R) -> isize { - if mem::size_of::() == 4 { - rng.gen::() as isize - } else { - rng.gen::() as isize - } - } -} - -impl Rand for i8 { - #[inline] - fn rand(rng: &mut R) -> i8 { - rng.next_u32() as i8 - } -} - -impl Rand for i16 { - #[inline] - fn rand(rng: &mut R) -> i16 { - rng.next_u32() as i16 - } -} - -impl Rand for i32 { - #[inline] - fn rand(rng: &mut R) -> i32 { - rng.next_u32() as i32 - } -} - -impl Rand for i64 { - #[inline] - fn rand(rng: &mut R) -> i64 { - rng.next_u64() as i64 - } -} - -impl Rand for usize { - #[inline] - fn rand(rng: &mut R) -> usize { - if mem::size_of::() == 4 { - rng.gen::() as usize - } else { - rng.gen::() as usize - } - } -} - -impl Rand for u8 { - #[inline] - fn rand(rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Rand for u16 { - #[inline] - fn rand(rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Rand for u32 { - #[inline] - fn rand(rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Rand for u64 { - #[inline] - fn rand(rng: &mut R) -> u64 { - rng.next_u64() - } -} - -macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { - mod $mod_name { - use {Rand, Rng, Open01, Closed01}; - - const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; - - impl Rand for $ty { - /// Generate a floating point number in the half-open - /// interval `[0,1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, - /// and `Open01` for the open interval `(0,1)`. - #[inline] - fn rand(rng: &mut R) -> $ty { - rng.$method_name() - } - } - impl Rand for Open01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Open01<$ty> { - // add a small amount (specifically 2 bits below - // the precision of f64/f32 at 1.0), so that small - // numbers are larger than 0, but large numbers - // aren't pushed to/above 1. - Open01(rng.$method_name() + 0.25 / SCALE) - } - } - impl Rand for Closed01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Closed01<$ty> { - // rescale so that 1.0 - epsilon becomes 1.0 - // precisely. - Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) - } - } - } - } -} -float_impls! { f64_rand_impls, f64, 53, next_f64 } -float_impls! { f32_rand_impls, f32, 24, next_f32 } - -impl Rand for char { - #[inline] - fn rand(rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; - loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - if let Some(c) = char::from_u32(rng.next_u32() & CHAR_MASK) { - return c; - } - } - } -} - -impl Rand for bool { - #[inline] - fn rand(rng: &mut R) -> bool { - rng.gen::() & 1 == 1 - } -} - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : Rand ),* - > Rand for ( $( $tyvar ),* , ) { - - #[inline] - fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { - ( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - $( - _rng.gen::<$tyvar>() - ),* - , - ) - } - } - } -} - -impl Rand for () { - #[inline] - fn rand(_: &mut R) -> () { - () - } -} -tuple_impl!{A} -tuple_impl!{A, B} -tuple_impl!{A, B, C} -tuple_impl!{A, B, C, D} -tuple_impl!{A, B, C, D, E} -tuple_impl!{A, B, C, D, E, F} -tuple_impl!{A, B, C, D, E, F, G} -tuple_impl!{A, B, C, D, E, F, G, H} -tuple_impl!{A, B, C, D, E, F, G, H, I} -tuple_impl!{A, B, C, D, E, F, G, H, I, J} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L} - -impl Rand for Option { - #[inline] - fn rand(rng: &mut R) -> Option { - if rng.gen() { Some(rng.gen()) } else { None } - } -} diff --git a/src/librand/reseeding.rs b/src/librand/reseeding.rs deleted file mode 100644 index 2821b7a8232df..0000000000000 --- a/src/librand/reseeding.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A wrapper around another RNG that reseeds it after it -//! generates a certain number of random bytes. - -use core::fmt; -use {Rng, SeedableRng}; - -/// How many bytes of entropy the underling RNG is allowed to generate -/// before it is reseeded. -const DEFAULT_GENERATION_THRESHOLD: usize = 32 * 1024; - -/// A wrapper around any RNG which reseeds the underlying RNG after it -/// has generated a certain number of random bytes. -pub struct ReseedingRng { - rng: R, - generation_threshold: usize, - bytes_generated: usize, - /// Controls the behavior when reseeding the RNG. - pub reseeder: Rsdr, -} - -impl> ReseedingRng { - /// Create a new `ReseedingRng` with the given parameters. - /// - /// # Arguments - /// - /// * `rng`: the random number generator to use. - /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. - /// * `reseeder`: the reseeding object to use. - pub fn new(rng: R, generation_threshold: usize, reseeder: Rsdr) -> ReseedingRng { - ReseedingRng { - rng, - generation_threshold, - bytes_generated: 0, - reseeder, - } - } - - /// Reseed the internal RNG if the number of bytes that have been - /// generated exceed the threshold. - pub fn reseed_if_necessary(&mut self) { - if self.bytes_generated >= self.generation_threshold { - self.reseeder.reseed(&mut self.rng); - self.bytes_generated = 0; - } - } -} - -impl> Rng for ReseedingRng { - fn next_u32(&mut self) -> u32 { - self.reseed_if_necessary(); - self.bytes_generated += 4; - self.rng.next_u32() - } - - fn next_u64(&mut self) -> u64 { - self.reseed_if_necessary(); - self.bytes_generated += 8; - self.rng.next_u64() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.reseed_if_necessary(); - self.bytes_generated += dest.len(); - self.rng.fill_bytes(dest) - } -} - -impl, Rsdr: Reseeder + Default> - SeedableRng<(Rsdr, S)> for ReseedingRng { - fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { - self.rng.reseed(seed); - self.reseeder = rsdr; - self.bytes_generated = 0; - } - -/// Create a new `ReseedingRng` from the given reseeder and -/// seed. This uses a default value for `generation_threshold`. - fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { - ReseedingRng { - rng: SeedableRng::from_seed(seed), - generation_threshold: DEFAULT_GENERATION_THRESHOLD, - bytes_generated: 0, - reseeder: rsdr, - } - } -} - -impl fmt::Debug for ReseedingRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ReseedingRng") - .field("rng", &self.rng) - .field("generation_threshold", &self.generation_threshold) - .field("bytes_generated", &self.bytes_generated) - .field("reseeder", &self.reseeder) - .finish() - } -} - -/// Something that can be used to reseed an RNG via `ReseedingRng`. -pub trait Reseeder { - /// Reseed the given RNG. - fn reseed(&mut self, rng: &mut R); -} - -/// Reseed an RNG using a `Default` instance. This reseeds by -/// replacing the RNG with the result of a `Default::default` call. -#[derive(Copy, Clone, Debug)] -pub struct ReseedWithDefault; - -impl Reseeder for ReseedWithDefault { - fn reseed(&mut self, rng: &mut R) { - *rng = Default::default(); - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for ReseedWithDefault { - /// Creates an instance of `ReseedWithDefault`. - fn default() -> ReseedWithDefault { - ReseedWithDefault - } -} - -#[cfg(test)] -mod tests { - use std::prelude::v1::*; - - use super::{ReseedWithDefault, ReseedingRng}; - use {Rng, SeedableRng}; - - struct Counter { - i: u32, - } - - impl Rng for Counter { - fn next_u32(&mut self) -> u32 { - self.i += 1; - // very random - self.i - 1 - } - } - impl Default for Counter { - /// Constructs a `Counter` with initial value zero. - fn default() -> Counter { - Counter { i: 0 } - } - } - impl SeedableRng for Counter { - fn reseed(&mut self, seed: u32) { - self.i = seed; - } - fn from_seed(seed: u32) -> Counter { - Counter { i: seed } - } - } - type MyRng = ReseedingRng; - - #[test] - fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter { i: 0 }, 400, ReseedWithDefault); - - let mut i = 0; - for _ in 0..1000 { - assert_eq!(rs.next_u32(), i % 100); - i += 1; - } - } - - #[test] - fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - assert!(ra.gen_ascii_chars() - .take(100) - .eq(rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed((ReseedWithDefault, 3)); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); - } - - const FILL_BYTES_V_LEN: usize = 13579; - #[test] - fn test_rng_fill_bytes() { - let mut v = vec![0; FILL_BYTES_V_LEN]; - ::test::rng().fill_bytes(&mut v); - - // Sanity test: if we've gotten here, `fill_bytes` has not infinitely - // recursed. - assert_eq!(v.len(), FILL_BYTES_V_LEN); - - // To test that `fill_bytes` actually did something, check that the - // average of `v` is not 0. - let mut sum = 0.0; - for &x in &v { - sum += x as f64; - } - assert!(sum / v.len() as f64 != 0.0); - } -} diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 5dd094b587bdf..4222e81d2a3dd 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -16,6 +16,7 @@ graphviz = { path = "../libgraphviz" } jobserver = "0.1" log = "0.3" owning_ref = "0.3.3" +rustc_apfloat = { path = "../librustc_apfloat" } rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc/README.md b/src/librustc/README.md index 87de284d011b3..ddf71a06d607c 100644 --- a/src/librustc/README.md +++ b/src/librustc/README.md @@ -98,7 +98,7 @@ entire program, and each did a particular check of transformation. We are gradually replacing this pass-based code with an alternative setup based on on-demand **queries**. In the query-model, we work backwards, executing a *query* that expresses our ultimate goal (e.g., -"compiler this crate"). This query in turn may make other queries +"compile this crate"). This query in turn may make other queries (e.g., "get me a list of all modules in the crate"). Those queries make other queries that ultimately bottom out in the base operations, like parsing the input, running the type-checker, and so forth. This @@ -153,7 +153,7 @@ take: - LLVM then runs its various optimizations, which produces a number of `.o` files (one for each "codegen unit"). 6. **Linking** - - Finally, those `.o` files are linke together. + - Finally, those `.o` files are linked together. Glossary ======== @@ -162,7 +162,7 @@ The compiler uses a number of...idiosyncratic abbreviations and things. This glossary attempts to list them and give you a few pointers for understanding them better. -- AST -- the **abstract syntax tree** produced the `syntax` crate; reflects user syntax +- AST -- the **abstract syntax tree** produced by the `syntax` crate; reflects user syntax very closely. - codegen unit -- when we produce LLVM IR, we group the Rust code into a number of codegen units. Each of these units is processed by LLVM independently from one another, diff --git a/src/libcore/benches/mem.rs b/src/librustc/benches/dispatch.rs similarity index 65% rename from src/libcore/benches/mem.rs rename to src/librustc/benches/dispatch.rs index 8e541d92a7f17..63e74778fb92a 100644 --- a/src/libcore/benches/mem.rs +++ b/src/librustc/benches/dispatch.rs @@ -10,8 +10,6 @@ use test::Bencher; -// FIXME #13642 (these benchmarks should be in another place) -// Completely miscellaneous language-construct benchmarks. // Static/dynamic method dispatch struct Struct { @@ -44,27 +42,3 @@ fn trait_static_method_call(b: &mut Bencher) { s.method() }); } - -// Overhead of various match forms - -#[bench] -fn match_option_some(b: &mut Bencher) { - let x = Some(10); - b.iter(|| { - match x { - Some(y) => y, - None => 11 - } - }); -} - -#[bench] -fn match_vec_pattern(b: &mut Bencher) { - let x = [1,2,3,4,5,6]; - b.iter(|| { - match x { - [1,2,3,..] => 10, - _ => 11, - } - }); -} diff --git a/src/librustc/benches/lib.rs b/src/librustc/benches/lib.rs new file mode 100644 index 0000000000000..24294ec49ceed --- /dev/null +++ b/src/librustc/benches/lib.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(warnings)] + +#![feature(slice_patterns)] +#![feature(test)] + +extern crate test; + +mod dispatch; +mod pattern; diff --git a/src/librustc/benches/pattern.rs b/src/librustc/benches/pattern.rs new file mode 100644 index 0000000000000..638b1ce3f7530 --- /dev/null +++ b/src/librustc/benches/pattern.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use test::Bencher; + +// Overhead of various match forms + +#[bench] +fn option_some(b: &mut Bencher) { + let x = Some(10); + b.iter(|| { + match x { + Some(y) => y, + None => 11 + } + }); +} + +#[bench] +fn vec_pattern(b: &mut Bencher) { + let x = [1,2,3,4,5,6]; + b.iter(|| { + match x { + [1,2,3,..] => 10, + _ => 11, + } + }); +} diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 7a78765365db0..1fc3b04adb86d 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -65,8 +65,8 @@ use hir::map::DefPathHash; use hir::{HirId, ItemLocalId}; use ich::Fingerprint; -use ty::{TyCtxt, Instance, InstanceDef}; -use ty::fast_reject::SimplifiedType; +use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; +use ty::subst::Substs; use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use ich::StableHashingContext; use std::fmt; @@ -90,6 +90,11 @@ macro_rules! is_input_attr { ($attr:ident) => (false); } +macro_rules! is_eval_always_attr { + (eval_always) => (true); + ($attr:ident) => (false); +} + macro_rules! contains_anon_attr { ($($attr:ident),*) => ({$(is_anon_attr!($attr) | )* false}); } @@ -98,6 +103,10 @@ macro_rules! contains_input_attr { ($($attr:ident),*) => ({$(is_input_attr!($attr) | )* false}); } +macro_rules! contains_eval_always_attr { + ($($attr:ident),*) => ({$(is_eval_always_attr!($attr) | )* false}); +} + macro_rules! define_dep_nodes { (<$tcx:tt> $( @@ -160,6 +169,15 @@ macro_rules! define_dep_nodes { } } + #[inline] + pub fn is_eval_always(&self) -> bool { + match *self { + $( + DepKind :: $variant => { contains_eval_always_attr!($($attr), *) } + )* + } + } + #[allow(unreachable_code)] #[inline] pub fn has_params(&self) -> bool { @@ -339,6 +357,25 @@ macro_rules! define_dep_nodes { Ok(DepNode::new_no_params(kind)) } } + + /// Used in testing + pub fn has_label_string(label: &str) -> bool { + match label { + $( + stringify!($variant) => true, + )* + _ => false, + } + } + } + + /// Contains variant => str representations for constructing + /// DepNode groups for tests. + #[allow(dead_code, non_upper_case_globals)] + pub mod label_strs { + $( + pub const $variant: &'static str = stringify!($variant); + )* } ); } @@ -347,7 +384,7 @@ impl fmt::Debug for DepNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.kind)?; - if !self.kind.has_params() { + if !self.kind.has_params() && !self.kind.is_anon() { return Ok(()); } @@ -356,14 +393,14 @@ impl fmt::Debug for DepNode { ::ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { if let Some(def_id) = self.extract_def_id(tcx) { - write!(f, "{}", tcx.item_path_str(def_id))?; + write!(f, "{}", tcx.def_path_debug_str(def_id))?; } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) { write!(f, "{}", s)?; } else { - write!(f, "{:?}", self.hash)?; + write!(f, "{}", self.hash)?; } } else { - write!(f, "{:?}", self.hash)?; + write!(f, "{}", self.hash)?; } Ok(()) })?; @@ -422,21 +459,17 @@ define_dep_nodes!( <'tcx> // Represents metadata from an extern crate. [input] CrateMetadata(CrateNum), - // Represents some artifact that we save to disk. Note that these - // do not have a def-id as part of their identifier. - [] WorkProduct(WorkProductId), - // Represents different phases in the compiler. [] RegionScopeTree(DefId), - [] Coherence, - [] CoherenceInherentImplOverlapCheck, - [] Resolve, + [eval_always] Coherence, + [eval_always] CoherenceInherentImplOverlapCheck, [] CoherenceCheckTrait(DefId), - [] PrivacyAccessLevels(CrateNum), + [eval_always] PrivacyAccessLevels(CrateNum), // Represents the MIR for a fn; also used as the task node for // things read/modify that MIR. [] MirConstQualif(DefId), + [] MirBuilt(DefId), [] MirConst(DefId), [] MirValidated(DefId), [] MirOptimized(DefId), @@ -445,13 +478,12 @@ define_dep_nodes!( <'tcx> [] BorrowCheckKrate, [] BorrowCheck(DefId), [] MirBorrowCheck(DefId), - [] UnsafetyViolations(DefId), + [] UnsafetyCheckResult(DefId), + [] UnsafeDeriveOnReprPacked(DefId), - [] RvalueCheck(DefId), [] Reachability, [] MirKeys, - [] TransWriteMetadata, - [] CrateVariances, + [eval_always] CrateVariances, // Nodes representing bits of computed IR in the tcx. Each shared // table in the tcx (or elsewhere) maps to one of these @@ -460,15 +492,14 @@ define_dep_nodes!( <'tcx> [] TypeOfItem(DefId), [] GenericsOfItem(DefId), [] PredicatesOfItem(DefId), + [] InferredOutlivesOf(DefId), [] SuperPredicatesOfItem(DefId), [] TraitDefOfItem(DefId), [] AdtDefOfItem(DefId), - [] IsDefaultImpl(DefId), + [] IsAutoImpl(DefId), [] ImplTraitRef(DefId), [] ImplPolarity(DefId), - [] ClosureKind(DefId), [] FnSignature(DefId), - [] GenSignature(DefId), [] CoerceUnsizedInfo(DefId), [] ItemVarianceConstraints(DefId), @@ -480,74 +511,47 @@ define_dep_nodes!( <'tcx> [] DtorckConstraint(DefId), [] AdtDestructor(DefId), [] AssociatedItemDefIds(DefId), - [] InherentImpls(DefId), + [eval_always] InherentImpls(DefId), [] TypeckBodiesKrate, [] TypeckTables(DefId), + [] UsedTraitImports(DefId), [] HasTypeckTables(DefId), - [anon] ConstEval, + [] ConstEval { param_env: ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)> }, + [] CheckMatch(DefId), [] SymbolName(DefId), [] InstanceSymbolName { instance: Instance<'tcx> }, [] SpecializationGraph(DefId), [] ObjectSafety(DefId), + [] FulfillObligation { param_env: ParamEnv<'tcx>, trait_ref: PolyTraitRef<'tcx> }, + [] VtableMethods { trait_ref: PolyTraitRef<'tcx> }, - [anon] IsCopy, - [anon] IsSized, - [anon] IsFreeze, - [anon] NeedsDrop, - [anon] Layout, + [] IsCopy { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, + [] IsSized { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, + [] IsFreeze { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, + [] NeedsDrop { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, + [] Layout { param_env: ParamEnvAnd<'tcx, Ty<'tcx>> }, // The set of impls for a given trait. [] TraitImpls(DefId), - [] RelevantTraitImpls(DefId, SimplifiedType), - - [] AllLocalTraitImpls, - - // Nodes representing caches. To properly handle a true cache, we - // don't use a DepTrackingMap, but rather we push a task node. - // Otherwise the write into the map would be incorrectly - // attributed to the first task that happened to fill the cache, - // which would yield an overly conservative dep-graph. - [] TraitItems(DefId), - [] ReprHints(DefId), - - // Trait selection cache is a little funny. Given a trait - // reference like `Foo: SomeTrait`, there could be - // arbitrarily many def-ids to map on in there (e.g., `Foo`, - // `SomeTrait`, `Bar`). We could have a vector of them, but it - // requires heap-allocation, and trait sel in general can be a - // surprisingly hot path. So instead we pick two def-ids: the - // trait def-id, and the first def-id in the input types. If there - // is no def-id in the input types, then we use the trait def-id - // again. So for example: - // - // - `i32: Clone` -> `TraitSelect { trait_def_id: Clone, self_def_id: Clone }` - // - `u32: Clone` -> `TraitSelect { trait_def_id: Clone, self_def_id: Clone }` - // - `Clone: Clone` -> `TraitSelect { trait_def_id: Clone, self_def_id: Clone }` - // - `Vec: Clone` -> `TraitSelect { trait_def_id: Clone, self_def_id: Vec }` - // - `String: Clone` -> `TraitSelect { trait_def_id: Clone, self_def_id: String }` - // - `Foo: Trait` -> `TraitSelect { trait_def_id: Trait, self_def_id: Foo }` - // - `Foo: Trait` -> `TraitSelect { trait_def_id: Trait, self_def_id: Foo }` - // - `(Foo, Bar): Trait` -> `TraitSelect { trait_def_id: Trait, self_def_id: Foo }` - // - `i32: Trait` -> `TraitSelect { trait_def_id: Trait, self_def_id: Foo }` - // - // You can see that we map many trait refs to the same - // trait-select node. This is not a problem, it just means - // imprecision in our dep-graph tracking. The important thing is - // that for any given trait-ref, we always map to the **same** - // trait-select node. - [anon] TraitSelect, - // For proj. cache, we just keep a list of all def-ids, since it is - // not a hotspot. - [] ProjectionCache { def_ids: DefIdList }, + [input] AllLocalTraitImpls, + + [anon] TraitSelect, [] ParamEnv(DefId), [] DescribeDef(DefId), - [] DefSpan(DefId), + + // FIXME(mw): DefSpans are not really inputs since they are derived from + // HIR. But at the moment HIR hashing still contains some hacks that allow + // to make type debuginfo to be source location independent. Declaring + // DefSpan an input makes sure that changes to these are always detected + // regardless of HIR hashing. + [input] DefSpan(DefId), [] LookupStability(DefId), [] LookupDeprecationEntry(DefId), [] ItemBodyNestedBodies(DefId), [] ConstIsRvaluePromotableToStatic(DefId), + [] RvaluePromotableMap(DefId), [] ImplParent(DefId), [] TraitOfItem(DefId), [] IsExportedSymbol(DefId), @@ -558,11 +562,11 @@ define_dep_nodes!( <'tcx> [] IsPanicRuntime(CrateNum), [] IsCompilerBuiltins(CrateNum), [] HasGlobalAllocator(CrateNum), - [] ExternCrate(DefId), - [] LintLevels, + [input] ExternCrate(DefId), + [eval_always] LintLevels, [] Specializes { impl1: DefId, impl2: DefId }, [input] InScopeTraits(DefIndex), - [] ModuleExports(DefId), + [input] ModuleExports(DefId), [] IsSanitizerRuntime(CrateNum), [] IsProfilerRuntime(CrateNum), [] GetPanicStrategy(CrateNum), @@ -572,9 +576,9 @@ define_dep_nodes!( <'tcx> [] NativeLibraries(CrateNum), [] PluginRegistrarFn(CrateNum), [] DeriveRegistrarFn(CrateNum), - [] CrateDisambiguator(CrateNum), - [] CrateHash(CrateNum), - [] OriginalCrateName(CrateNum), + [input] CrateDisambiguator(CrateNum), + [input] CrateHash(CrateNum), + [input] OriginalCrateName(CrateNum), [] ImplementationsOfTrait { krate: CrateNum, trait_id: DefId }, [] AllTraitImplementations(CrateNum), @@ -582,42 +586,52 @@ define_dep_nodes!( <'tcx> [] IsDllimportForeignItem(DefId), [] IsStaticallyIncludedForeignItem(DefId), [] NativeLibraryKind(DefId), - [] LinkArgs, + [input] LinkArgs, - [] NamedRegion(DefIndex), - [] IsLateBound(DefIndex), - [] ObjectLifetimeDefaults(DefIndex), + [input] NamedRegion(DefIndex), + [input] IsLateBound(DefIndex), + [input] ObjectLifetimeDefaults(DefIndex), [] Visibility(DefId), [] DepKind(CrateNum), - [] CrateName(CrateNum), + [input] CrateName(CrateNum), [] ItemChildren(DefId), [] ExternModStmtCnum(DefId), - [] GetLangItems, + [input] GetLangItems, [] DefinedLangItems(CrateNum), [] MissingLangItems(CrateNum), [] ExternConstBody(DefId), [] VisibleParentMap, - [] IsDirectExternCrate(CrateNum), - [] MissingExternCrateItem(CrateNum), - [] UsedCrateSource(CrateNum), - [] PostorderCnums, - [] HasCloneClosures(CrateNum), - [] HasCopyClosures(CrateNum), - - [] Freevars(DefId), - [] MaybeUnusedTraitImport(DefId), - [] MaybeUnusedExternCrates, - [] StabilityIndex, - [] AllCrateNums, + [input] MissingExternCrateItem(CrateNum), + [input] UsedCrateSource(CrateNum), + [input] PostorderCnums, + [input] HasCloneClosures(CrateNum), + [input] HasCopyClosures(CrateNum), + + // This query is not expected to have inputs -- as a result, it's + // not a good candidate for "replay" because it's essentially a + // pure function of its input (and hence the expectation is that + // no caller would be green **apart** from just this + // query). Making it anonymous avoids hashing the result, which + // may save a bit of time. + [anon] EraseRegionsTy { ty: Ty<'tcx> }, + + [input] Freevars(DefId), + [input] MaybeUnusedTraitImport(DefId), + [input] MaybeUnusedExternCrates, + [eval_always] StabilityIndex, + [input] AllCrateNums, [] ExportedSymbols(CrateNum), - [] CollectAndPartitionTranslationItems, + [eval_always] CollectAndPartitionTranslationItems, [] ExportName(DefId), [] ContainsExternIndicator(DefId), [] IsTranslatedFunction(DefId), [] CodegenUnit(InternedString), [] CompileCodegenUnit(InternedString), - [] OutputFilenames, + [input] OutputFilenames, + [anon] NormalizeTy, + // We use this for most things when incr. comp. is turned off. + [] Null, ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { @@ -714,42 +728,8 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId, De let (def_id_0, def_id_1) = *self; format!("({}, {})", - tcx.def_path(def_id_0).to_string(tcx), - tcx.def_path(def_id_1).to_string(tcx)) - } -} - - -impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefIdList,) { - const CAN_RECONSTRUCT_QUERY_KEY: bool = false; - - // We actually would not need to specialize the implementation of this - // method but it's faster to combine the hashes than to instantiate a full - // hashing context and stable-hashing state. - fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint { - let mut fingerprint = Fingerprint::zero(); - - for &def_id in self.0.iter() { - let def_path_hash = tcx.def_path_hash(def_id); - fingerprint = fingerprint.combine(def_path_hash.0); - } - - fingerprint - } - - fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String { - use std::fmt::Write; - - let mut s = String::new(); - write!(&mut s, "[").unwrap(); - - for &def_id in self.0.iter() { - write!(&mut s, "{}", tcx.def_path(def_id).to_string(tcx)).unwrap(); - } - - write!(&mut s, "]").unwrap(); - - s + tcx.def_path_debug_str(def_id_0), + tcx.def_path_debug_str(def_id_1)) } } @@ -798,17 +778,8 @@ impl WorkProductId { hash: fingerprint } } - - pub fn to_dep_node(self) -> DepNode { - DepNode { - kind: DepKind::WorkProduct, - hash: self.hash, - } - } } impl_stable_hash_for!(struct ::dep_graph::WorkProductId { hash }); - -type DefIdList = Vec; diff --git a/src/librustc/dep_graph/edges.rs b/src/librustc/dep_graph/edges.rs deleted file mode 100644 index b12db11cb6af6..0000000000000 --- a/src/librustc/dep_graph/edges.rs +++ /dev/null @@ -1,266 +0,0 @@ -// 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 ich::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::StableHasher; -use std::env; -use std::hash::Hash; -use std::mem; -use super::{DepGraphQuery, DepKind, DepNode}; -use super::debug::EdgeFilter; - -pub(super) struct DepGraphEdges { - nodes: Vec, - indices: FxHashMap, - edges: FxHashSet<(DepNodeIndex, DepNodeIndex)>, - task_stack: Vec, - forbidden_edge: Option, - - // A set to help assert that no two tasks use the same DepNode. This is a - // temporary measure. Once we load the previous dep-graph as readonly, this - // check will fall out of the graph implementation naturally. - opened_once: FxHashSet, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub(super) struct DepNodeIndex { - index: u32, -} - -impl DepNodeIndex { - - pub const INVALID: DepNodeIndex = DepNodeIndex { index: ::std::u32::MAX }; - - fn new(v: usize) -> DepNodeIndex { - assert!((v & 0xFFFF_FFFF) == v); - DepNodeIndex { index: v as u32 } - } - - fn index(self) -> usize { - self.index as usize - } -} - -#[derive(Clone, Debug, PartialEq)] -enum OpenTask { - Regular { - node: DepNode, - reads: Vec, - read_set: FxHashSet, - }, - Anon { - reads: Vec, - read_set: FxHashSet, - }, - Ignore, -} - -impl DepGraphEdges { - pub fn new() -> DepGraphEdges { - let forbidden_edge = if cfg!(debug_assertions) { - match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { - Ok(s) => { - match EdgeFilter::new(&s) { - Ok(f) => Some(f), - Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), - } - } - Err(_) => None, - } - } else { - None - }; - - DepGraphEdges { - nodes: vec![], - indices: FxHashMap(), - edges: FxHashSet(), - task_stack: Vec::new(), - forbidden_edge, - opened_once: FxHashSet(), - } - } - - fn id(&self, index: DepNodeIndex) -> DepNode { - self.nodes[index.index()] - } - - pub fn push_ignore(&mut self) { - self.task_stack.push(OpenTask::Ignore); - } - - pub fn pop_ignore(&mut self) { - let popped_node = self.task_stack.pop().unwrap(); - debug_assert_eq!(popped_node, OpenTask::Ignore); - } - - pub fn push_task(&mut self, key: DepNode) { - if !self.opened_once.insert(key) { - bug!("Re-opened node {:?}", key) - } - - self.task_stack.push(OpenTask::Regular { - node: key, - reads: Vec::new(), - read_set: FxHashSet(), - }); - } - - pub fn pop_task(&mut self, key: DepNode) -> DepNodeIndex { - let popped_node = self.task_stack.pop().unwrap(); - - if let OpenTask::Regular { - node, - read_set: _, - reads - } = popped_node { - debug_assert_eq!(node, key); - debug_assert!(!node.kind.is_input() || reads.is_empty()); - - let target_id = self.get_or_create_node(node); - - for read in reads.into_iter() { - let source_id = self.get_or_create_node(read); - self.edges.insert((source_id, target_id)); - } - - target_id - } else { - bug!("pop_task() - Expected regular task to be popped") - } - } - - pub fn push_anon_task(&mut self) { - self.task_stack.push(OpenTask::Anon { - reads: Vec::new(), - read_set: FxHashSet(), - }); - } - - pub fn pop_anon_task(&mut self, kind: DepKind) -> DepNodeIndex { - let popped_node = self.task_stack.pop().unwrap(); - - if let OpenTask::Anon { - read_set: _, - reads - } = popped_node { - let mut fingerprint = Fingerprint::zero(); - let mut hasher = StableHasher::new(); - - for read in reads.iter() { - mem::discriminant(&read.kind).hash(&mut hasher); - - // Fingerprint::combine() is faster than sending Fingerprint - // through the StableHasher (at least as long as StableHasher - // is so slow). - fingerprint = fingerprint.combine(read.hash); - } - - fingerprint = fingerprint.combine(hasher.finish()); - - let target_dep_node = DepNode { - kind, - hash: fingerprint, - }; - - if let Some(&index) = self.indices.get(&target_dep_node) { - return index; - } - - let target_id = self.get_or_create_node(target_dep_node); - - for read in reads.into_iter() { - let source_id = self.get_or_create_node(read); - self.edges.insert((source_id, target_id)); - } - - target_id - } else { - bug!("pop_anon_task() - Expected anonymous task to be popped") - } - } - - /// Indicates that the current task `C` reads `v` by adding an - /// edge from `v` to `C`. If there is no current task, has no - /// effect. Note that *reading* from tracked state is harmless if - /// you are not in a task; what is bad is *writing* to tracked - /// state (and leaking data that you read into a tracked task). - pub fn read(&mut self, source: DepNode) { - match self.task_stack.last_mut() { - Some(&mut OpenTask::Regular { - node: target, - ref mut reads, - ref mut read_set, - }) => { - if read_set.insert(source) { - reads.push(source); - - if cfg!(debug_assertions) { - if let Some(ref forbidden_edge) = self.forbidden_edge { - if forbidden_edge.test(&source, &target) { - bug!("forbidden edge {:?} -> {:?} created", source, target) - } - } - } - } - } - Some(&mut OpenTask::Anon { - ref mut reads, - ref mut read_set, - }) => { - if read_set.insert(source) { - reads.push(source); - } - } - Some(&mut OpenTask::Ignore) | None => { - // ignore - } - } - } - - pub fn read_index(&mut self, source: DepNodeIndex) { - let dep_node = self.nodes[source.index()]; - self.read(dep_node); - } - - pub fn query(&self) -> DepGraphQuery { - let edges: Vec<_> = self.edges.iter() - .map(|&(i, j)| (self.id(i), self.id(j))) - .collect(); - DepGraphQuery::new(&self.nodes, &edges) - } - - #[inline] - pub fn add_edge(&mut self, source: DepNode, target: DepNode) { - let source = self.get_or_create_node(source); - let target = self.get_or_create_node(target); - self.edges.insert((source, target)); - } - - pub fn add_node(&mut self, node: DepNode) -> DepNodeIndex { - self.get_or_create_node(node) - } - - #[inline] - fn get_or_create_node(&mut self, dep_node: DepNode) -> DepNodeIndex { - let DepGraphEdges { - ref mut indices, - ref mut nodes, - .. - } = *self; - - *indices.entry(dep_node).or_insert_with(|| { - let next_id = nodes.len(); - nodes.push(dep_node); - DepNodeIndex::new(next_id) - }) - } -} diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 71a7ee84cd144..96d6b0f79cfff 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -8,23 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use errors::DiagnosticBuilder; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHashingContextProvider}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use session::config::OutputType; use std::cell::{Ref, RefCell}; +use std::env; use std::hash::Hash; use std::rc::Rc; +use ty::TyCtxt; use util::common::{ProfileQueriesMsg, profq_msg}; use ich::Fingerprint; +use super::debug::EdgeFilter; use super::dep_node::{DepNode, DepKind, WorkProductId}; use super::query::DepGraphQuery; use super::raii; use super::safe::DepGraphSafe; -use super::edges::{self, DepGraphEdges}; use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use super::prev::PreviousDepGraph; @@ -42,28 +44,29 @@ pub struct DepGraph { fingerprints: Rc>> } -/// As a temporary measure, while transitioning to the new DepGraph -/// implementation, we maintain the old and the new dep-graph encoding in -/// parallel, so a DepNodeIndex actually contains two indices, one for each -/// version. + +newtype_index!(DepNodeIndex); + +impl DepNodeIndex { + const INVALID: DepNodeIndex = DepNodeIndex(::std::u32::MAX); +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct DepNodeIndex { - legacy: edges::DepNodeIndex, - new: DepNodeIndexNew, +pub enum DepNodeColor { + Red, + Green(DepNodeIndex) } -impl DepNodeIndex { - pub const INVALID: DepNodeIndex = DepNodeIndex { - legacy: edges::DepNodeIndex::INVALID, - new: DepNodeIndexNew::INVALID, - }; +impl DepNodeColor { + pub fn is_green(self) -> bool { + match self { + DepNodeColor::Red => false, + DepNodeColor::Green(_) => true, + } + } } struct DepGraphData { - /// The old, initial encoding of the dependency graph. This will soon go - /// away. - edges: RefCell, - /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into @@ -74,6 +77,8 @@ struct DepGraphData { /// nodes and edges as well as all fingerprints of nodes that have them. previous: PreviousDepGraph, + colors: RefCell>, + /// When we load, there may be `.o` files, cached mir, or other such /// things available to us. If we find that they are not dirty, we /// load the path to the file storing those work-products here into @@ -84,6 +89,9 @@ struct DepGraphData { work_products: RefCell>, dep_node_debug: RefCell>, + + // Used for testing, only populated when -Zquery-dep-graph is specified. + loaded_from_cache: RefCell>, } impl DepGraph { @@ -93,10 +101,11 @@ impl DepGraph { data: Some(Rc::new(DepGraphData { previous_work_products: RefCell::new(FxHashMap()), work_products: RefCell::new(FxHashMap()), - edges: RefCell::new(DepGraphEdges::new()), dep_node_debug: RefCell::new(FxHashMap()), current: RefCell::new(CurrentDepGraph::new()), previous: prev_graph, + colors: RefCell::new(FxHashMap()), + loaded_from_cache: RefCell::new(FxHashMap()), })), fingerprints: Rc::new(RefCell::new(FxHashMap())), } @@ -116,12 +125,22 @@ impl DepGraph { } pub fn query(&self) -> DepGraphQuery { - self.data.as_ref().unwrap().edges.borrow().query() + let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); + let nodes: Vec<_> = current_dep_graph.nodes.iter().cloned().collect(); + let mut edges = Vec::new(); + for (index, edge_targets) in current_dep_graph.edges.iter_enumerated() { + let from = current_dep_graph.nodes[index]; + for &edge_target in edge_targets { + let to = current_dep_graph.nodes[edge_target]; + edges.push((from, to)); + } + } + + DepGraphQuery::new(&nodes[..], &edges[..]) } pub fn in_ignore<'graph>(&'graph self) -> Option> { - self.data.as_ref().map(|data| raii::IgnoreTask::new(&data.edges, - &data.current)) + self.data.as_ref().map(|data| raii::IgnoreTask::new(&data.current)) } pub fn with_ignore(&self, op: OP) -> R @@ -166,10 +185,27 @@ impl DepGraph { -> (R, DepNodeIndex) where C: DepGraphSafe + StableHashingContextProvider, R: HashStable, + { + self.with_task_impl(key, cx, arg, task, + |data, key| data.borrow_mut().push_task(key), + |data, key| data.borrow_mut().pop_task(key)) + } + + fn with_task_impl(&self, + key: DepNode, + cx: C, + arg: A, + task: fn(C, A) -> R, + push: fn(&RefCell, DepNode), + pop: fn(&RefCell, DepNode) -> DepNodeIndex) + -> (R, DepNodeIndex) + where C: DepGraphSafe + StableHashingContextProvider, + R: HashStable, { if let Some(ref data) = self.data { - data.edges.borrow_mut().push_task(key); - data.current.borrow_mut().push_task(key); + debug_assert!(!data.colors.borrow().contains_key(&key)); + + push(&data.current, key); if cfg!(debug_assertions) { profq_msg(ProfileQueriesMsg::TaskBegin(key.clone())) }; @@ -186,31 +222,52 @@ impl DepGraph { profq_msg(ProfileQueriesMsg::TaskEnd) }; - let dep_node_index_legacy = data.edges.borrow_mut().pop_task(key); - let dep_node_index_new = data.current.borrow_mut().pop_task(key); + let dep_node_index = pop(&data.current, key); let mut stable_hasher = StableHasher::new(); result.hash_stable(&mut hcx, &mut stable_hasher); - assert!(self.fingerprints - .borrow_mut() - .insert(key, stable_hasher.finish()) - .is_none()); + let current_fingerprint = stable_hasher.finish(); - (result, DepNodeIndex { - legacy: dep_node_index_legacy, - new: dep_node_index_new, - }) + // Store the current fingerprint + { + let old_value = self.fingerprints + .borrow_mut() + .insert(key, current_fingerprint); + debug_assert!(old_value.is_none(), + "DepGraph::with_task() - Duplicate fingerprint \ + insertion for {:?}", key); + } + + // Determine the color of the new DepNode. + { + let prev_fingerprint = data.previous.fingerprint_of(&key); + + let color = if Some(current_fingerprint) == prev_fingerprint { + DepNodeColor::Green(dep_node_index) + } else { + DepNodeColor::Red + }; + + let old_value = data.colors.borrow_mut().insert(key, color); + debug_assert!(old_value.is_none(), + "DepGraph::with_task() - Duplicate DepNodeColor \ + insertion for {:?}", key); + } + + (result, dep_node_index) } else { if key.kind.fingerprint_needed_for_crate_hash() { let mut hcx = cx.create_stable_hashing_context(); let result = task(cx, arg); let mut stable_hasher = StableHasher::new(); result.hash_stable(&mut hcx, &mut stable_hasher); - assert!(self.fingerprints - .borrow_mut() - .insert(key, stable_hasher.finish()) - .is_none()); + let old_value = self.fingerprints + .borrow_mut() + .insert(key, stable_hasher.finish()); + debug_assert!(old_value.is_none(), + "DepGraph::with_task() - Duplicate fingerprint \ + insertion for {:?}", key); (result, DepNodeIndex::INVALID) } else { (task(cx, arg), DepNodeIndex::INVALID) @@ -224,28 +281,39 @@ impl DepGraph { where OP: FnOnce() -> R { if let Some(ref data) = self.data { - data.edges.borrow_mut().push_anon_task(); data.current.borrow_mut().push_anon_task(); let result = op(); - let dep_node_index_legacy = data.edges.borrow_mut().pop_anon_task(dep_kind); - let dep_node_index_new = data.current.borrow_mut().pop_anon_task(dep_kind); - (result, DepNodeIndex { - legacy: dep_node_index_legacy, - new: dep_node_index_new, - }) + let dep_node_index = data.current + .borrow_mut() + .pop_anon_task(dep_kind); + (result, dep_node_index) } else { (op(), DepNodeIndex::INVALID) } } + /// Execute something within an "eval-always" task which is a task + // that runs whenever anything changes. + pub fn with_eval_always_task(&self, + key: DepNode, + cx: C, + arg: A, + task: fn(C, A) -> R) + -> (R, DepNodeIndex) + where C: DepGraphSafe + StableHashingContextProvider, + R: HashStable, + { + self.with_task_impl(key, cx, arg, task, + |data, key| data.borrow_mut().push_eval_always_task(key), + |data, key| data.borrow_mut().pop_eval_always_task(key)) + } + #[inline] pub fn read(&self, v: DepNode) { if let Some(ref data) = self.data { - data.edges.borrow_mut().read(v); - let mut current = data.current.borrow_mut(); - if let Some(&dep_node_index_new) = current.node_to_node_index.get(&v) { - current.read_index(dep_node_index_new); + if let Some(&dep_node_index) = current.node_to_node_index.get(&v) { + current.read_index(dep_node_index); } else { bug!("DepKind {:?} should be pre-allocated but isn't.", v.kind) } @@ -253,32 +321,31 @@ impl DepGraph { } #[inline] - pub fn read_index(&self, v: DepNodeIndex) { + pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { - data.edges.borrow_mut().read_index(v.legacy); - data.current.borrow_mut().read_index(v.new); + data.current.borrow_mut().read_index(dep_node_index); } } - /// Only to be used during graph loading #[inline] - pub fn add_edge_directly(&self, source: DepNode, target: DepNode) { - self.data.as_ref().unwrap().edges.borrow_mut().add_edge(source, target); - } - - /// Only to be used during graph loading - pub fn add_node_directly(&self, node: DepNode) { - self.data.as_ref().unwrap().edges.borrow_mut().add_node(node); - } - pub fn fingerprint_of(&self, dep_node: &DepNode) -> Fingerprint { - self.fingerprints.borrow()[dep_node] + match self.fingerprints.borrow().get(dep_node) { + Some(&fingerprint) => fingerprint, + None => { + bug!("Could not find current fingerprint for {:?}", dep_node) + } + } } - pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Fingerprint { + pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) } + #[inline] + pub fn prev_dep_node_index_of(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { + self.data.as_ref().unwrap().previous.node_to_index(dep_node) + } + /// Indicates that a previous work product exists for `v`. This is /// invoked during initial start-up based on what nodes are clean /// (and what files exist in the incr. directory). @@ -346,6 +413,12 @@ impl DepGraph { self.data.as_ref().and_then(|t| t.dep_node_debug.borrow().get(&dep_node).cloned()) } + pub fn edge_deduplication_data(&self) -> (u64, u64) { + let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); + + (current_dep_graph.total_read_count, current_dep_graph.total_duplicate_read_count) + } + pub fn serialize(&self) -> SerializedDepGraph { let fingerprints = self.fingerprints.borrow(); let current_dep_graph = self.data.as_ref().unwrap().current.borrow(); @@ -367,7 +440,7 @@ impl DepGraph { for (current_dep_node_index, edges) in current_dep_graph.edges.iter_enumerated() { let start = edge_list_data.len() as u32; // This should really just be a memcpy :/ - edge_list_data.extend(edges.iter().map(|i| SerializedDepNodeIndex(i.index))); + edge_list_data.extend(edges.iter().map(|i| SerializedDepNodeIndex::new(i.index()))); let end = edge_list_data.len() as u32; debug_assert_eq!(current_dep_node_index.index(), edge_list_indices.len()); @@ -383,6 +456,260 @@ impl DepGraph { edge_list_data, } } + + pub fn node_color(&self, dep_node: &DepNode) -> Option { + self.data.as_ref().and_then(|data| data.colors.borrow().get(dep_node).cloned()) + } + + pub fn try_mark_green<'tcx>(&self, + tcx: TyCtxt<'_, 'tcx, 'tcx>, + dep_node: &DepNode) + -> Option { + debug!("try_mark_green({:?}) - BEGIN", dep_node); + let data = self.data.as_ref().unwrap(); + + debug_assert!(!data.colors.borrow().contains_key(dep_node)); + debug_assert!(!data.current.borrow().node_to_node_index.contains_key(dep_node)); + + if dep_node.kind.is_input() { + // We should only hit try_mark_green() for inputs that do not exist + // anymore in the current compilation session. Existing inputs are + // eagerly marked as either red/green before any queries are + // executed. + debug_assert!(dep_node.extract_def_id(tcx).is_none()); + debug!("try_mark_green({:?}) - END - DepNode is deleted input", dep_node); + return None; + } + + let (prev_deps, prev_dep_node_index) = match data.previous.edges_from(dep_node) { + Some(prev) => { + // This DepNode and the corresponding query invocation existed + // in the previous compilation session too, so we can try to + // mark it as green by recursively marking all of its + // dependencies green. + prev + } + None => { + // This DepNode did not exist in the previous compilation session, + // so we cannot mark it as green. + debug!("try_mark_green({:?}) - END - DepNode does not exist in \ + current compilation session anymore", dep_node); + return None + } + }; + + let mut current_deps = Vec::new(); + + for &dep_dep_node_index in prev_deps { + let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index); + + let dep_dep_node_color = data.colors.borrow().get(dep_dep_node).cloned(); + match dep_dep_node_color { + Some(DepNodeColor::Green(node_index)) => { + // This dependency has been marked as green before, we are + // still fine and can continue with checking the other + // dependencies. + debug!("try_mark_green({:?}) --- found dependency {:?} to \ + be immediately green", dep_node, dep_dep_node); + current_deps.push(node_index); + } + Some(DepNodeColor::Red) => { + // We found a dependency the value of which has changed + // compared to the previous compilation session. We cannot + // mark the DepNode as green and also don't need to bother + // with checking any of the other dependencies. + debug!("try_mark_green({:?}) - END - dependency {:?} was \ + immediately red", dep_node, dep_dep_node); + return None + } + None => { + // We don't know the state of this dependency. If it isn't + // an input node, let's try to mark it green recursively. + if !dep_dep_node.kind.is_input() { + debug!("try_mark_green({:?}) --- state of dependency {:?} \ + is unknown, trying to mark it green", dep_node, + dep_dep_node); + + if let Some(node_index) = self.try_mark_green(tcx, dep_dep_node) { + debug!("try_mark_green({:?}) --- managed to MARK \ + dependency {:?} as green", dep_node, dep_dep_node); + current_deps.push(node_index); + continue; + } + } else { + match dep_dep_node.kind { + DepKind::Hir | + DepKind::HirBody | + DepKind::CrateMetadata => { + if dep_node.extract_def_id(tcx).is_none() { + // If the node does not exist anymore, we + // just fail to mark green. + return None + } else { + // If the node does exist, it should have + // been pre-allocated. + bug!("DepNode {:?} should have been \ + pre-allocated but wasn't.", + dep_dep_node) + } + } + _ => { + // For other kinds of inputs it's OK to be + // forced. + } + } + } + + // We failed to mark it green, so we try to force the query. + debug!("try_mark_green({:?}) --- trying to force \ + dependency {:?}", dep_node, dep_dep_node); + if ::ty::maps::force_from_dep_node(tcx, dep_dep_node) { + let dep_dep_node_color = data.colors + .borrow() + .get(dep_dep_node) + .cloned(); + match dep_dep_node_color { + Some(DepNodeColor::Green(node_index)) => { + debug!("try_mark_green({:?}) --- managed to \ + FORCE dependency {:?} to green", + dep_node, dep_dep_node); + current_deps.push(node_index); + } + Some(DepNodeColor::Red) => { + debug!("try_mark_green({:?}) - END - \ + dependency {:?} was red after forcing", + dep_node, + dep_dep_node); + return None + } + None => { + bug!("try_mark_green() - Forcing the DepNode \ + should have set its color") + } + } + } else { + // The DepNode could not be forced. + debug!("try_mark_green({:?}) - END - dependency {:?} \ + could not be forced", dep_node, dep_dep_node); + return None + } + } + } + } + + + // If we got here without hitting a `return` that means that all + // dependencies of this DepNode could be marked as green. Therefore we + // can also mark this DepNode as green. We do so by... + + // ... allocating an entry for it in the current dependency graph and + // adding all the appropriate edges imported from the previous graph ... + let dep_node_index = data.current + .borrow_mut() + .alloc_node(*dep_node, current_deps); + + // ... copying the fingerprint from the previous graph too, so we don't + // have to recompute it ... + let fingerprint = data.previous.fingerprint_by_index(prev_dep_node_index); + let old_fingerprint = self.fingerprints + .borrow_mut() + .insert(*dep_node, fingerprint); + debug_assert!(old_fingerprint.is_none(), + "DepGraph::try_mark_green() - Duplicate fingerprint \ + insertion for {:?}", dep_node); + + // ... emitting any stored diagnostic ... + { + let diagnostics = tcx.on_disk_query_result_cache + .load_diagnostics(tcx, prev_dep_node_index); + + if diagnostics.len() > 0 { + let handle = tcx.sess.diagnostic(); + + // Promote the previous diagnostics to the current session. + tcx.on_disk_query_result_cache + .store_diagnostics(dep_node_index, diagnostics.clone()); + + for diagnostic in diagnostics { + DiagnosticBuilder::new_diagnostic(handle, diagnostic).emit(); + } + } + } + + // ... and finally storing a "Green" entry in the color map. + let old_color = data.colors + .borrow_mut() + .insert(*dep_node, DepNodeColor::Green(dep_node_index)); + debug_assert!(old_color.is_none(), + "DepGraph::try_mark_green() - Duplicate DepNodeColor \ + insertion for {:?}", dep_node); + + debug!("try_mark_green({:?}) - END - successfully marked as green", dep_node); + Some(dep_node_index) + } + + // Used in various assertions + pub fn is_green(&self, dep_node_index: DepNodeIndex) -> bool { + let dep_node = self.data.as_ref().unwrap().current.borrow().nodes[dep_node_index]; + self.data.as_ref().unwrap().colors.borrow().get(&dep_node).map(|&color| { + match color { + DepNodeColor::Red => false, + DepNodeColor::Green(_) => true, + } + }).unwrap_or(false) + } + + // This method loads all on-disk cacheable query results into memory, so + // they can be written out to the new cache file again. Most query results + // will already be in memory but in the case where we marked something as + // green but then did not need the value, that value will never have been + // loaded from disk. + // + // This method will only load queries that will end up in the disk cache. + // Other queries will not be executed. + pub fn exec_cache_promotions<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) { + let green_nodes: Vec = { + let data = self.data.as_ref().unwrap(); + data.colors.borrow().iter().filter_map(|(dep_node, color)| match color { + DepNodeColor::Green(_) => { + if dep_node.cache_on_disk(tcx) { + Some(*dep_node) + } else { + None + } + } + DepNodeColor::Red => { + // We can skip red nodes because a node can only be marked + // as red if the query result was recomputed and thus is + // already in memory. + None + } + }).collect() + }; + + for dep_node in green_nodes { + dep_node.load_from_on_disk_cache(tcx); + } + } + + pub fn mark_loaded_from_cache(&self, dep_node_index: DepNodeIndex, state: bool) { + debug!("mark_loaded_from_cache({:?}, {})", + self.data.as_ref().unwrap().current.borrow().nodes[dep_node_index], + state); + + self.data + .as_ref() + .unwrap() + .loaded_from_cache + .borrow_mut() + .insert(dep_node_index, state); + } + + pub fn was_loaded_from_cache(&self, dep_node: &DepNode) -> Option { + let data = self.data.as_ref().unwrap(); + let dep_node_index = data.current.borrow().node_to_node_index[dep_node]; + data.loaded_from_cache.borrow().get(&dep_node_index).cloned() + } } /// A "work product" is an intermediate result that we save into the @@ -419,30 +746,74 @@ impl DepGraph { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct WorkProduct { pub cgu_name: String, - /// Extra hash used to decide if work-product is still suitable; - /// note that this is *not* a hash of the work-product itself. - /// See documentation on `WorkProduct` type for an example. - pub input_hash: u64, - /// Saved files associated with this CGU - pub saved_files: Vec<(OutputType, String)>, + pub saved_files: Vec<(WorkProductFileKind, String)>, } -pub(super) struct CurrentDepGraph { - nodes: IndexVec, - edges: IndexVec>, - node_to_node_index: FxHashMap, +#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable)] +pub enum WorkProductFileKind { + Object, + Bytecode, + BytecodeCompressed, +} +pub(super) struct CurrentDepGraph { + nodes: IndexVec, + edges: IndexVec>, + node_to_node_index: FxHashMap, task_stack: Vec, + forbidden_edge: Option, + + // Anonymous DepNodes are nodes the ID of which we compute from the list of + // their edges. This has the beneficial side-effect that multiple anonymous + // nodes can be coalesced into one without changing the semantics of the + // dependency graph. However, the merging of nodes can lead to a subtle + // problem during red-green marking: The color of an anonymous node from + // the current session might "shadow" the color of the node with the same + // ID from the previous session. In order to side-step this problem, we make + // sure that anon-node IDs allocated in different sessions don't overlap. + // This is implemented by mixing a session-key into the ID fingerprint of + // each anon node. The session-key is just a random number generated when + // the DepGraph is created. + anon_id_seed: Fingerprint, + + total_read_count: u64, + total_duplicate_read_count: u64, } impl CurrentDepGraph { fn new() -> CurrentDepGraph { + use std::time::{SystemTime, UNIX_EPOCH}; + + let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let nanos = duration.as_secs() * 1_000_000_000 + + duration.subsec_nanos() as u64; + let mut stable_hasher = StableHasher::new(); + nanos.hash(&mut stable_hasher); + + let forbidden_edge = if cfg!(debug_assertions) { + match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { + Ok(s) => { + match EdgeFilter::new(&s) { + Ok(f) => Some(f), + Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), + } + } + Err(_) => None, + } + } else { + None + }; + CurrentDepGraph { nodes: IndexVec::new(), edges: IndexVec::new(), node_to_node_index: FxHashMap(), + anon_id_seed: stable_hasher.finish(), task_stack: Vec::new(), + forbidden_edge, + total_read_count: 0, + total_duplicate_read_count: 0, } } @@ -463,7 +834,7 @@ impl CurrentDepGraph { }); } - pub(super) fn pop_task(&mut self, key: DepNode) -> DepNodeIndexNew { + pub(super) fn pop_task(&mut self, key: DepNode) -> DepNodeIndex { let popped_node = self.task_stack.pop().unwrap(); if let OpenTask::Regular { @@ -471,7 +842,30 @@ impl CurrentDepGraph { read_set: _, reads } = popped_node { - debug_assert_eq!(node, key); + assert_eq!(node, key); + + // If this is an input node, we expect that it either has no + // dependencies, or that it just depends on DepKind::CrateMetadata + // or DepKind::Krate. This happens for some "thin wrapper queries" + // like `crate_disambiguator` which sometimes have zero deps (for + // when called for LOCAL_CRATE) or they depend on a CrateMetadata + // node. + if cfg!(debug_assertions) { + if node.kind.is_input() && reads.len() > 0 && + // FIXME(mw): Special case for DefSpan until Spans are handled + // better in general. + node.kind != DepKind::DefSpan && + reads.iter().any(|&i| { + !(self.nodes[i].kind == DepKind::CrateMetadata || + self.nodes[i].kind == DepKind::Krate) + }) + { + bug!("Input node {:?} with unexpected reads: {:?}", + node, + reads.iter().map(|&i| self.nodes[i]).collect::>()) + } + } + self.alloc_node(node, reads) } else { bug!("pop_task() - Expected regular task to be popped") @@ -485,14 +879,16 @@ impl CurrentDepGraph { }); } - fn pop_anon_task(&mut self, kind: DepKind) -> DepNodeIndexNew { + fn pop_anon_task(&mut self, kind: DepKind) -> DepNodeIndex { let popped_node = self.task_stack.pop().unwrap(); if let OpenTask::Anon { read_set: _, reads } = popped_node { - let mut fingerprint = Fingerprint::zero(); + debug_assert!(!kind.is_input()); + + let mut fingerprint = self.anon_id_seed; let mut hasher = StableHasher::new(); for &read in reads.iter() { @@ -514,24 +910,56 @@ impl CurrentDepGraph { }; if let Some(&index) = self.node_to_node_index.get(&target_dep_node) { - return index; + index + } else { + self.alloc_node(target_dep_node, reads) } - - self.alloc_node(target_dep_node, reads) } else { bug!("pop_anon_task() - Expected anonymous task to be popped") } } - fn read_index(&mut self, source: DepNodeIndexNew) { + fn push_eval_always_task(&mut self, key: DepNode) { + self.task_stack.push(OpenTask::EvalAlways { node: key }); + } + + fn pop_eval_always_task(&mut self, key: DepNode) -> DepNodeIndex { + let popped_node = self.task_stack.pop().unwrap(); + + if let OpenTask::EvalAlways { + node, + } = popped_node { + debug_assert_eq!(node, key); + let krate_idx = self.node_to_node_index[&DepNode::new_no_params(DepKind::Krate)]; + self.alloc_node(node, vec![krate_idx]) + } else { + bug!("pop_eval_always_task() - Expected eval always task to be popped"); + } + } + + fn read_index(&mut self, source: DepNodeIndex) { match self.task_stack.last_mut() { Some(&mut OpenTask::Regular { ref mut reads, ref mut read_set, - node: _, + node: ref target, }) => { + self.total_read_count += 1; if read_set.insert(source) { reads.push(source); + + if cfg!(debug_assertions) { + if let Some(ref forbidden_edge) = self.forbidden_edge { + let source = self.nodes[source]; + if forbidden_edge.test(&source, &target) { + bug!("forbidden edge {:?} -> {:?} created", + source, + target) + } + } + } + } else { + self.total_duplicate_read_count += 1; } } Some(&mut OpenTask::Anon { @@ -542,7 +970,8 @@ impl CurrentDepGraph { reads.push(source); } } - Some(&mut OpenTask::Ignore) | None => { + Some(&mut OpenTask::Ignore) | + Some(&mut OpenTask::EvalAlways { .. }) | None => { // ignore } } @@ -550,12 +979,12 @@ impl CurrentDepGraph { fn alloc_node(&mut self, dep_node: DepNode, - edges: Vec) - -> DepNodeIndexNew { + edges: Vec) + -> DepNodeIndex { debug_assert_eq!(self.edges.len(), self.nodes.len()); debug_assert_eq!(self.node_to_node_index.len(), self.nodes.len()); debug_assert!(!self.node_to_node_index.contains_key(&dep_node)); - let dep_node_index = DepNodeIndexNew::new(self.nodes.len()); + let dep_node_index = DepNodeIndex::new(self.nodes.len()); self.nodes.push(dep_node); self.node_to_node_index.insert(dep_node, dep_node_index); self.edges.push(edges); @@ -563,46 +992,19 @@ impl CurrentDepGraph { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub(super) struct DepNodeIndexNew { - index: u32, -} - -impl Idx for DepNodeIndexNew { - fn new(idx: usize) -> Self { - DepNodeIndexNew::new(idx) - } - fn index(self) -> usize { - self.index() - } -} - -impl DepNodeIndexNew { - - const INVALID: DepNodeIndexNew = DepNodeIndexNew { - index: ::std::u32::MAX, - }; - - fn new(v: usize) -> DepNodeIndexNew { - assert!((v & 0xFFFF_FFFF) == v); - DepNodeIndexNew { index: v as u32 } - } - - fn index(self) -> usize { - self.index as usize - } -} - #[derive(Clone, Debug, PartialEq)] enum OpenTask { Regular { node: DepNode, - reads: Vec, - read_set: FxHashSet, + reads: Vec, + read_set: FxHashSet, }, Anon { - reads: Vec, - read_set: FxHashSet, + reads: Vec, + read_set: FxHashSet, }, Ignore, + EvalAlways { + node: DepNode, + }, } diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index cd77e06bdd6b0..a472183698abf 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -11,7 +11,6 @@ pub mod debug; mod dep_node; mod dep_tracking_map; -mod edges; mod graph; mod prev; mod query; @@ -20,10 +19,11 @@ mod safe; mod serialized; pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; -pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId}; -pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex}; +pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, label_strs}; +pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor}; +pub use self::graph::WorkProductFileKind; pub use self::prev::PreviousDepGraph; pub use self::query::DepGraphQuery; pub use self::safe::AssertDepGraphSafe; pub use self::safe::DepGraphSafe; -pub use self::serialized::SerializedDepGraph; +pub use self::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; diff --git a/src/librustc/dep_graph/prev.rs b/src/librustc/dep_graph/prev.rs index 882ca0414ccc9..6c43b5c5ff197 100644 --- a/src/librustc/dep_graph/prev.rs +++ b/src/librustc/dep_graph/prev.rs @@ -28,19 +28,38 @@ impl PreviousDepGraph { PreviousDepGraph { data, index } } - pub fn with_edges_from(&self, dep_node: &DepNode, mut f: F) - where - F: FnMut(&(DepNode, Fingerprint)), - { - let node_index = self.index[dep_node]; - self.data - .edge_targets_from(node_index) - .into_iter() - .for_each(|&index| f(&self.data.nodes[index])); + #[inline] + pub fn edges_from(&self, + dep_node: &DepNode) + -> Option<(&[SerializedDepNodeIndex], SerializedDepNodeIndex)> { + self.index + .get(dep_node) + .map(|&node_index| { + (self.data.edge_targets_from(node_index), node_index) + }) } - pub fn fingerprint_of(&self, dep_node: &DepNode) -> Fingerprint { - let node_index = self.index[dep_node]; - self.data.nodes[node_index].1 + #[inline] + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { + self.data.nodes[dep_node_index].0 + } + + #[inline] + pub fn node_to_index(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { + self.index[dep_node] + } + + #[inline] + pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option { + self.index + .get(dep_node) + .map(|&node_index| self.data.nodes[node_index].1) + } + + #[inline] + pub fn fingerprint_by_index(&self, + dep_node_index: SerializedDepNodeIndex) + -> Fingerprint { + self.data.nodes[dep_node_index].1 } } diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs index 6e9e4f4a18b15..5728bcc7d2771 100644 --- a/src/librustc/dep_graph/raii.rs +++ b/src/librustc/dep_graph/raii.rs @@ -8,33 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::edges::DepGraphEdges; use super::graph::CurrentDepGraph; use std::cell::RefCell; pub struct IgnoreTask<'graph> { - legacy_graph: &'graph RefCell, - new_graph: &'graph RefCell, + graph: &'graph RefCell, } impl<'graph> IgnoreTask<'graph> { - pub(super) fn new(legacy_graph: &'graph RefCell, - new_graph: &'graph RefCell) - -> IgnoreTask<'graph> { - legacy_graph.borrow_mut().push_ignore(); - new_graph.borrow_mut().push_ignore(); + pub(super) fn new(graph: &'graph RefCell) -> IgnoreTask<'graph> { + graph.borrow_mut().push_ignore(); IgnoreTask { - legacy_graph, - new_graph, + graph, } } } impl<'graph> Drop for IgnoreTask<'graph> { fn drop(&mut self) { - self.legacy_graph.borrow_mut().pop_ignore(); - self.new_graph.borrow_mut().pop_ignore(); + self.graph.borrow_mut().pop_ignore(); } } diff --git a/src/librustc/dep_graph/serialized.rs b/src/librustc/dep_graph/serialized.rs index 21beac9214eef..c96040ab9b6e3 100644 --- a/src/librustc/dep_graph/serialized.rs +++ b/src/librustc/dep_graph/serialized.rs @@ -14,31 +14,7 @@ use dep_graph::DepNode; use ich::Fingerprint; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -/// The index of a DepNode in the SerializedDepGraph::nodes array. -#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug, - RustcEncodable, RustcDecodable)] -pub struct SerializedDepNodeIndex(pub u32); - -impl SerializedDepNodeIndex { - #[inline] - pub fn new(idx: usize) -> SerializedDepNodeIndex { - assert!(idx <= ::std::u32::MAX as usize); - SerializedDepNodeIndex(idx as u32) - } -} - -impl Idx for SerializedDepNodeIndex { - #[inline] - fn new(idx: usize) -> Self { - assert!(idx <= ::std::u32::MAX as usize); - SerializedDepNodeIndex(idx as u32) - } - - #[inline] - fn index(self) -> usize { - self.0 as usize - } -} +newtype_index!(SerializedDepNodeIndex); /// Data for use when recompiling the **current crate**. #[derive(Debug, RustcEncodable, RustcDecodable)] @@ -64,7 +40,10 @@ impl SerializedDepGraph { } } - pub fn edge_targets_from(&self, source: SerializedDepNodeIndex) -> &[SerializedDepNodeIndex] { + #[inline] + pub fn edge_targets_from(&self, + source: SerializedDepNodeIndex) + -> &[SerializedDepNodeIndex] { let targets = self.edge_list_indices[source]; &self.edge_list_data[targets.0 as usize..targets.1 as usize] } diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 76fba1583f3bc..8089a88a9e8d4 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -401,16 +401,6 @@ fn bar(x: &str, y: &str) -> &str { } fn baz<'a>(x: &'a str, y: &str) -> &str { } ``` -Here's an example that is currently an error, but may work in a future version -of Rust: - -```compile_fail,E0106 -struct Foo<'a>(&'a str); - -trait Quux { } -impl Quux for Foo { } -``` - Lifetime elision in implementation headers was part of the lifetime elision RFC. It is, however, [currently unimplemented][iss15872]. @@ -1351,74 +1341,6 @@ struct Foo { ``` "##, -E0312: r##" -A lifetime of reference outlives lifetime of borrowed content. - -Erroneous code example: - -```compile_fail,E0312 -fn make_child<'tree, 'human>( - x: &'human i32, - y: &'tree i32 -) -> &'human i32 { - if x > y - { x } - else - { y } - // error: lifetime of reference outlives lifetime of borrowed content -} -``` - -The function declares that it returns a reference with the `'human` -lifetime, but it may return data with the `'tree` lifetime. As neither -lifetime is declared longer than the other, this results in an -error. Sometimes, this error is because the function *body* is -incorrect -- that is, maybe you did not *mean* to return data from -`y`. In that case, you should fix the function body. - -Often, however, the body is correct. In that case, the function -signature needs to be altered to match the body, so that the caller -understands that data from either `x` or `y` may be returned. The -simplest way to do this is to give both function parameters the *same* -named lifetime: - -``` -fn make_child<'human>( - x: &'human i32, - y: &'human i32 -) -> &'human i32 { - if x > y - { x } - else - { y } // ok! -} -``` - -However, in some cases, you may prefer to explicitly declare that one lifetime -outlives another using a `where` clause: - -``` -fn make_child<'tree, 'human>( - x: &'human i32, - y: &'tree i32 -) -> &'human i32 -where - 'tree: 'human -{ - if x > y - { x } - else - { y } // ok! -} -``` - -Here, the where clause `'tree: 'human` can be read as "the lifetime -'tree outlives the lifetime 'human" -- meaning, references with the -`'tree` lifetime live *at least as long as* references with the -`'human` lifetime. Therefore, it is safe to return data with lifetime -`'tree` when data with the lifetime `'human` is needed. -"##, - E0317: r##" This error occurs when an `if` expression without an `else` block is used in a context where a type other than `()` is expected, for example a `let` @@ -1848,6 +1770,46 @@ If you want to get command-line arguments, use `std::env::args`. To exit with a specified exit code, use `std::process::exit`. "##, +E0562: r##" +Abstract return types (written `impl Trait` for some trait `Trait`) are only +allowed as function return types. + +Erroneous code example: + +```compile_fail,E0562 +#![feature(conservative_impl_trait)] + +fn main() { + let count_to_ten: impl Iterator = 0..10; + // error: `impl Trait` not allowed outside of function and inherent method + // return types + for i in count_to_ten { + println!("{}", i); + } +} +``` + +Make sure `impl Trait` only appears in return-type position. + +``` +#![feature(conservative_impl_trait)] + +fn count_to_n(n: usize) -> impl Iterator { + 0..n +} + +fn main() { + for i in count_to_n(10) { // ok! + println!("{}", i); + } +} +``` + +See [RFC 1522] for more details. + +[RFC 1522]: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md +"##, + E0591: r##" Per [RFC 401][rfc401], if you have a function declaration `foo`: @@ -1943,7 +1905,7 @@ fn main() { "##, E0601: r##" -No `main` function was found in a binary crate. To fix this error, just add a +No `main` function was found in a binary crate. To fix this error, add a `main` function. For example: ``` @@ -2007,7 +1969,38 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { ``` "##, +E0644: r##" +A closure or generator was constructed that references its own type. + +Erroneous example: + +```compile-fail,E0644 +fn fix(f: &F) + where F: Fn(&F) +{ + f(&f); +} + +fn main() { + fix(&|y| { + // Here, when `x` is called, the parameter `y` is equal to `x`. + }); } +``` + +Rust does not permit a closure to directly reference its own type, +either through an argument (as in the example above) or by capturing +itself through its environment. This restriction helps keep closure +inference tractable. + +The easiest fix is to rewrite your closure into a top-level function, +or into a method. In some cases, you may also be able to have your +closure call itself by capturing a `&Fn()` object or `fn()` pointer +that refers to itself. That is permitting, since the closure would be +invoking itself via a virtual call, and hence does not directly +reference its own *type*. + +"##, } register_diagnostics! { @@ -2028,6 +2021,7 @@ register_diagnostics! { // E0304, // expected signed integer constant // E0305, // expected constant E0311, // thing may not live long enough + E0312, // lifetime of reference outlives lifetime of borrowed content E0313, // lifetime of borrowed pointer outlives lifetime of captured variable E0314, // closure outlives stack frame E0315, // cannot invoke closure outside of its lifetime @@ -2056,4 +2050,7 @@ register_diagnostics! { E0628, // generators cannot have explicit arguments E0631, // type mismatch in closure arguments E0637, // "'_" is not a valid lifetime bound + E0657, // `impl Trait` can only capture lifetimes bound at the fn level + E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax + E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders } diff --git a/src/librustc/hir/README.md b/src/librustc/hir/README.md index c832a897dee8b..e283fc40c50a3 100644 --- a/src/librustc/hir/README.md +++ b/src/librustc/hir/README.md @@ -57,7 +57,7 @@ carry around references into the HIR, but rather to carry around *identifier numbers* (or just "ids"). Right now, you will find four sorts of identifiers in active use: -- `DefId`, which primarily name "definitions" or top-level items. +- `DefId`, which primarily names "definitions" or top-level items. - You can think of a `DefId` as being shorthand for a very explicit and complete path, like `std::collections::HashMap`. However, these paths are able to name things that are not nameable in @@ -114,6 +114,6 @@ A **body** represents some kind of executable code, such as the body of a function/closure or the definition of a constant. Bodies are associated with an **owner**, which is typically some kind of item (e.g., a `fn()` or `const`), but could also be a closure expression -(e.g., `|x, y| x + y`). You can use the HIR map to find find the body +(e.g., `|x, y| x + y`). You can use the HIR map to find the body associated with a given def-id (`maybe_body_owned_by()`) or to find the owner of a body (`body_owner_def_id()`). diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 946cbb7960b67..003255f87966f 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -47,27 +47,27 @@ struct CheckAttrVisitor<'a> { impl<'a> CheckAttrVisitor<'a> { /// Check any attribute. - fn check_attribute(&self, attr: &ast::Attribute, target: Target) { + fn check_attribute(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) { if let Some(name) = attr.name() { match &*name.as_str() { - "inline" => self.check_inline(attr, target), - "repr" => self.check_repr(attr, target), + "inline" => self.check_inline(attr, item, target), + "repr" => self.check_repr(attr, item, target), _ => (), } } } /// Check if an `#[inline]` is applied to a function. - fn check_inline(&self, attr: &ast::Attribute, target: Target) { + fn check_inline(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) { if target != Target::Fn { struct_span_err!(self.sess, attr.span, E0518, "attribute should be applied to function") - .span_label(attr.span, "requires a function") + .span_label(item.span, "not a function") .emit(); } } /// Check if an `#[repr]` attr is valid. - fn check_repr(&self, attr: &ast::Attribute, target: Target) { + fn check_repr(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) { let words = match attr.meta_item_list() { Some(words) => words, None => { @@ -75,7 +75,9 @@ impl<'a> CheckAttrVisitor<'a> { } }; - let mut conflicting_reprs = 0; + let mut int_reprs = 0; + let mut is_c = false; + let mut is_simd = false; for word in words { @@ -86,7 +88,7 @@ impl<'a> CheckAttrVisitor<'a> { let (message, label) = match &*name.as_str() { "C" => { - conflicting_reprs += 1; + is_c = true; if target != Target::Struct && target != Target::Union && target != Target::Enum { @@ -108,7 +110,7 @@ impl<'a> CheckAttrVisitor<'a> { } } "simd" => { - conflicting_reprs += 1; + is_simd = true; if target != Target::Struct { ("attribute should be applied to struct", "a struct") @@ -128,7 +130,7 @@ impl<'a> CheckAttrVisitor<'a> { "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "isize" | "usize" => { - conflicting_reprs += 1; + int_reprs += 1; if target != Target::Enum { ("attribute should be applied to enum", "an enum") @@ -139,10 +141,14 @@ impl<'a> CheckAttrVisitor<'a> { _ => continue, }; struct_span_err!(self.sess, attr.span, E0517, "{}", message) - .span_label(attr.span, format!("requires {}", label)) + .span_label(item.span, format!("not {}", label)) .emit(); } - if conflicting_reprs > 1 { + + // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) + if (int_reprs > 1) + || (is_simd && is_c) + || (int_reprs == 1 && is_c && is_c_like_enum(item)) { span_warn!(self.sess, attr.span, E0566, "conflicting representation hints"); } @@ -153,7 +159,7 @@ impl<'a> Visitor<'a> for CheckAttrVisitor<'a> { fn visit_item(&mut self, item: &'a ast::Item) { let target = Target::from_item(item); for attr in &item.attrs { - self.check_attribute(attr, target); + self.check_attribute(attr, item, target); } visit::walk_item(self, item); } @@ -162,3 +168,17 @@ impl<'a> Visitor<'a> for CheckAttrVisitor<'a> { pub fn check_crate(sess: &Session, krate: &ast::Crate) { visit::walk_crate(&mut CheckAttrVisitor { sess: sess }, krate); } + +fn is_c_like_enum(item: &ast::Item) -> bool { + if let ast::ItemKind::Enum(ref def, _) = item.node { + for variant in &def.variants { + match variant.node.data { + ast::VariantData::Unit(_) => { /* continue */ } + _ => { return false; } + } + } + true + } else { + false + } +} diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index 4e0c6479abf14..64bcdc7920a01 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -35,6 +35,7 @@ pub enum Def { Variant(DefId), Trait(DefId), TyAlias(DefId), + TyForeign(DefId), AssociatedTy(DefId), PrimTy(hir::PrimTy), TyParam(DefId), @@ -152,7 +153,7 @@ impl Def { Def::AssociatedTy(id) | Def::TyParam(id) | Def::Struct(id) | Def::StructCtor(id, ..) | Def::Union(id) | Def::Trait(id) | Def::Method(id) | Def::Const(id) | Def::AssociatedConst(id) | Def::Macro(id, ..) | - Def::GlobalAsm(id) => { + Def::GlobalAsm(id) | Def::TyForeign(id) => { id } @@ -186,6 +187,7 @@ impl Def { Def::StructCtor(.., CtorKind::Fictive) => bug!("impossible struct constructor"), Def::Union(..) => "union", Def::Trait(..) => "trait", + Def::TyForeign(..) => "foreign type", Def::Method(..) => "method", Def::Const(..) => "constant", Def::AssociatedConst(..) => "associated constant", diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index 8e48352007b37..8858023ec1d7b 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -11,35 +11,31 @@ use ty; use rustc_data_structures::indexed_vec::Idx; -use serialize::{self, Encoder, Decoder}; - +use serialize; use std::fmt; use std::u32; -#[derive(Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, Debug)] -pub struct CrateNum(u32); - -impl Idx for CrateNum { - fn new(value: usize) -> Self { - assert!(value < (u32::MAX) as usize); - CrateNum(value as u32) - } +newtype_index!(CrateNum + { + ENCODABLE = custom + DEBUG_FORMAT = "crate{}", - fn index(self) -> usize { - self.0 as usize - } -} + /// Item definitions in the currently-compiled crate would have the CrateNum + /// LOCAL_CRATE in their DefId. + const LOCAL_CRATE = 0, -/// Item definitions in the currently-compiled crate would have the CrateNum -/// LOCAL_CRATE in their DefId. -pub const LOCAL_CRATE: CrateNum = CrateNum(0); + /// Virtual crate for builtin macros + // FIXME(jseyfried): this is also used for custom derives until proc-macro crates get + // `CrateNum`s. + const BUILTIN_MACROS_CRATE = u32::MAX, -/// Virtual crate for builtin macros -// FIXME(jseyfried): this is also used for custom derives until proc-macro crates get `CrateNum`s. -pub const BUILTIN_MACROS_CRATE: CrateNum = CrateNum(u32::MAX); + /// A CrateNum value that indicates that something is wrong. + const INVALID_CRATE = u32::MAX - 1, -/// A CrateNum value that indicates that something is wrong. -pub const INVALID_CRATE: CrateNum = CrateNum(u32::MAX - 1); + /// A special CrateNum that we use for the tcx.rcache when decoding from + /// the incr. comp. cache. + const RESERVED_FOR_INCR_COMP_CACHE = u32::MAX - 2, + }); impl CrateNum { pub fn new(x: usize) -> CrateNum { @@ -68,17 +64,8 @@ impl fmt::Display for CrateNum { } } -impl serialize::UseSpecializedEncodable for CrateNum { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(self.0) - } -} - -impl serialize::UseSpecializedDecodable for CrateNum { - fn default_decode(d: &mut D) -> Result { - d.read_u32().map(CrateNum) - } -} +impl serialize::UseSpecializedEncodable for CrateNum {} +impl serialize::UseSpecializedDecodable for CrateNum {} /// A DefIndex is an index into the hir-map for a crate, identifying a /// particular definition. It should really be considered an interned @@ -93,20 +80,18 @@ impl serialize::UseSpecializedDecodable for CrateNum { /// /// Since the DefIndex is mostly treated as an opaque ID, you probably /// don't have to care about these ranges. -#[derive(Clone, Eq, Ord, PartialOrd, PartialEq, RustcEncodable, - RustcDecodable, Hash, Copy)] -pub struct DefIndex(u32); +newtype_index!(DefIndex + { + ENCODABLE = custom + DEBUG_FORMAT = custom, -impl Idx for DefIndex { - fn new(value: usize) -> Self { - assert!(value < (u32::MAX) as usize); - DefIndex(value as u32) - } + /// The start of the "high" range of DefIndexes. + const DEF_INDEX_HI_START = 1 << 31, - fn index(self) -> usize { - self.0 as usize - } -} + /// The crate root is always assigned index 0 by the AST Map code, + /// thanks to `NodeCollector::new`. + const CRATE_DEF_INDEX = 0, + }); impl fmt::Debug for DefIndex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -118,12 +103,6 @@ impl fmt::Debug for DefIndex { } impl DefIndex { - #[inline] - pub fn new(x: usize) -> DefIndex { - assert!(x < (u32::MAX as usize)); - DefIndex(x as u32) - } - #[inline] pub fn from_u32(x: u32) -> DefIndex { DefIndex(x) @@ -162,12 +141,8 @@ impl DefIndex { } } -/// The start of the "high" range of DefIndexes. -const DEF_INDEX_HI_START: DefIndex = DefIndex(1 << 31); - -/// The crate root is always assigned index 0 by the AST Map code, -/// thanks to `NodeCollector::new`. -pub const CRATE_DEF_INDEX: DefIndex = DefIndex(0); +impl serialize::UseSpecializedEncodable for DefIndex {} +impl serialize::UseSpecializedDecodable for DefIndex {} #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum DefIndexAddressSpace { @@ -189,7 +164,7 @@ impl DefIndexAddressSpace { /// A DefId identifies a particular *definition*, by combining a crate /// index and a def index. -#[derive(Clone, Eq, Ord, PartialOrd, PartialEq, RustcEncodable, RustcDecodable, Hash, Copy)] +#[derive(Clone, Eq, Ord, PartialOrd, PartialEq, Hash, Copy)] pub struct DefId { pub krate: CrateNum, pub index: DefIndex, @@ -197,28 +172,74 @@ pub struct DefId { impl fmt::Debug for DefId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DefId {{ krate: {:?}, node: {:?}", - self.krate, self.index)?; + write!(f, "DefId({:?}/{}:{}", + self.krate.index(), + self.index.address_space().index(), + self.index.as_array_index())?; ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { - write!(f, " => {}", tcx.def_path(*self).to_string(tcx))?; + write!(f, " ~ {}", tcx.def_path_debug_str(*self))?; } Ok(()) })?; - write!(f, " }}") + write!(f, ")") } } - impl DefId { /// Make a local `DefId` with the given index. + #[inline] pub fn local(index: DefIndex) -> DefId { DefId { krate: LOCAL_CRATE, index: index } } - pub fn is_local(&self) -> bool { + #[inline] + pub fn is_local(self) -> bool { self.krate == LOCAL_CRATE } + + #[inline] + pub fn to_local(self) -> LocalDefId { + LocalDefId::from_def_id(self) + } } + +impl serialize::UseSpecializedEncodable for DefId {} +impl serialize::UseSpecializedDecodable for DefId {} + +/// A LocalDefId is equivalent to a DefId with `krate == LOCAL_CRATE`. Since +/// we encode this information in the type, we can ensure at compile time that +/// no DefIds from upstream crates get thrown into the mix. There are quite a +/// few cases where we know that only DefIds from the local crate are expected +/// and a DefId from a different crate would signify a bug somewhere. This +/// is when LocalDefId comes in handy. +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct LocalDefId(DefIndex); + +impl LocalDefId { + + #[inline] + pub fn from_def_id(def_id: DefId) -> LocalDefId { + assert!(def_id.is_local()); + LocalDefId(def_id.index) + } + + #[inline] + pub fn to_def_id(self) -> DefId { + DefId { + krate: LOCAL_CRATE, + index: self.0 + } + } +} + +impl fmt::Debug for LocalDefId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.to_def_id().fmt(f) + } +} + +impl serialize::UseSpecializedEncodable for LocalDefId {} +impl serialize::UseSpecializedDecodable for LocalDefId {} diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 1755b3bca0572..c9d35158ed002 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -503,7 +503,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { // visit_enum_def() takes care of visiting the Item's NodeId visitor.visit_enum_def(enum_definition, type_parameters, item.id, item.span) } - ItemDefaultImpl(_, ref trait_ref) => { + ItemAutoImpl(_, ref trait_ref) => { visitor.visit_id(item.id); visitor.visit_trait_ref(trait_ref) } @@ -520,7 +520,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { visitor.visit_id(item.id); visitor.visit_variant_data(struct_definition, item.name, generics, item.id, item.span); } - ItemTrait(_, ref generics, ref bounds, ref trait_item_refs) => { + ItemTrait(.., ref generics, ref bounds, ref trait_item_refs) => { visitor.visit_id(item.id); visitor.visit_generics(generics); walk_list!(visitor, visit_ty_param_bound, bounds); @@ -591,7 +591,13 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { } visitor.visit_lifetime(lifetime); } - TyImplTrait(ref bounds) => { + TyImplTraitExistential(ref existty, ref lifetimes) => { + let ExistTy { ref generics, ref bounds } = *existty; + walk_generics(visitor, generics); + walk_list!(visitor, visit_ty_param_bound, bounds); + walk_list!(visitor, visit_lifetime, lifetimes); + } + TyImplTraitUniversal(_, ref bounds) => { walk_list!(visitor, visit_ty_param_bound, bounds); } TyTypeof(expression) => { @@ -704,6 +710,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v } } ForeignItemStatic(ref typ, _) => visitor.visit_ty(typ), + ForeignItemType => (), } walk_list!(visitor, visit_attribute, &foreign_item.attrs); @@ -780,9 +787,7 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' FnKind::ItemFn(_, generics, ..) => { visitor.visit_generics(generics); } - FnKind::Method(_, sig, ..) => { - visitor.visit_generics(&sig.generics); - } + FnKind::Method(..) | FnKind::Closure(_) => {} } } @@ -802,6 +807,7 @@ pub fn walk_fn<'v, V: Visitor<'v>>(visitor: &mut V, pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem) { visitor.visit_name(trait_item.span, trait_item.name); walk_list!(visitor, visit_attribute, &trait_item.attrs); + visitor.visit_generics(&trait_item.generics); match trait_item.node { TraitItemKind::Const(ref ty, default) => { visitor.visit_id(trait_item.id); @@ -810,7 +816,6 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai } TraitItemKind::Method(ref sig, TraitMethod::Required(ref names)) => { visitor.visit_id(trait_item.id); - visitor.visit_generics(&sig.generics); visitor.visit_fn_decl(&sig.decl); for name in names { visitor.visit_name(name.span, name.node); @@ -852,6 +857,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt ref vis, ref defaultness, ref attrs, + ref generics, ref node, span } = *impl_item; @@ -860,6 +866,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt visitor.visit_vis(vis); visitor.visit_defaultness(defaultness); walk_list!(visitor, visit_attribute, attrs); + visitor.visit_generics(generics); match *node { ImplItemKind::Const(ref ty, body) => { visitor.visit_id(impl_item.id); diff --git a/src/librustc/hir/itemlikevisit.rs b/src/librustc/hir/itemlikevisit.rs index ce1a34faf5ee8..2221ecf07b434 100644 --- a/src/librustc/hir/itemlikevisit.rs +++ b/src/librustc/hir/itemlikevisit.rs @@ -41,7 +41,7 @@ use super::intravisit::Visitor; /// - Example: Lifetime resolution, which wants to bring lifetimes declared on the /// impl into scope while visiting the impl-items, and then back out again. /// - How: Implement `intravisit::Visitor` and override the -/// `visit_nested_map()` methods to return +/// `nested_visit_map()` methods to return /// `NestedVisitorMap::All`. Walk your crate with /// `intravisit::walk_crate()` invoked on `tcx.hir.krate()`. /// - Pro: Visitor methods for any kind of HIR node, not just item-like things. diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 411cdde3190b2..63b80a3f3c1e2 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -42,8 +42,9 @@ use dep_graph::DepGraph; use hir; -use hir::map::{Definitions, DefKey}; -use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX}; +use hir::HirVec; +use hir::map::{Definitions, DefKey, DefPathData}; +use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace}; use hir::def::{Def, PathResolution}; use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES; use middle::cstore::CrateStore; @@ -52,7 +53,7 @@ use session::Session; use util::common::FN_OUTPUT_NAME; use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use std::iter; use std::mem; @@ -96,13 +97,34 @@ pub struct LoweringContext<'a> { exported_macros: Vec, trait_impls: BTreeMap>, - trait_default_impl: BTreeMap, + trait_auto_impl: BTreeMap, is_generator: bool, catch_scopes: Vec, loop_scopes: Vec, is_in_loop_condition: bool, + is_in_trait_impl: bool, + + // Used to create lifetime definitions from in-band lifetime usages. + // e.g. `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8` + // When a named lifetime is encountered in a function or impl header and + // has not been defined + // (i.e. it doesn't appear in the in_scope_lifetimes list), it is added + // to this list. The results of this list are then added to the list of + // lifetime definitions in the corresponding impl or function generics. + lifetimes_to_define: Vec<(Span, Name)>, + // Whether or not in-band lifetimes are being collected. This is used to + // indicate whether or not we're in a place where new lifetimes will result + // in in-band lifetime definitions, such a function or an impl header. + // This will always be false unless the `in_band_lifetimes` feature is + // enabled. + is_collecting_in_band_lifetimes: bool, + // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. + // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked + // against this list to see if it is already in-scope, or if a definition + // needs to be created for it. + in_scope_lifetimes: Vec, type_def_lifetime_params: DefIdMap, @@ -123,6 +145,24 @@ pub trait Resolver { fn definitions(&mut self) -> &mut Definitions; } +#[derive(Clone, Copy, Debug)] +enum ImplTraitContext { + /// Treat `impl Trait` as shorthand for a new universal generic parameter. + /// Example: `fn foo(x: impl Debug)`, where `impl Debug` is conceptually + /// equivalent to a fresh universal parameter like `fn foo(x: T)`. + /// + /// We store a DefId here so we can look up necessary information later + Universal(DefId), + + /// Treat `impl Trait` as shorthand for a new universal existential parameter. + /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually + /// equivalent to a fresh existential parameter like `abstract type T; fn foo() -> T`. + Existential, + + /// `impl Trait` is not accepted in this position. + Disallowed, +} + pub fn lower_crate(sess: &Session, cstore: &CrateStore, dep_graph: &DepGraph, @@ -146,7 +186,7 @@ pub fn lower_crate(sess: &Session, impl_items: BTreeMap::new(), bodies: BTreeMap::new(), trait_impls: BTreeMap::new(), - trait_default_impl: BTreeMap::new(), + trait_auto_impl: BTreeMap::new(), exported_macros: Vec::new(), catch_scopes: Vec::new(), loop_scopes: Vec::new(), @@ -156,6 +196,10 @@ pub fn lower_crate(sess: &Session, item_local_id_counters: NodeMap(), node_id_to_hir_id: IndexVec::new(), is_generator: false, + is_in_trait_impl: false, + lifetimes_to_define: Vec::new(), + is_collecting_in_band_lifetimes: false, + in_scope_lifetimes: Vec::new(), }.lower_crate(krate) } @@ -198,7 +242,7 @@ impl<'a> LoweringContext<'a> { ItemKind::Union(_, ref generics) | ItemKind::Enum(_, ref generics) | ItemKind::Ty(_, ref generics) | - ItemKind::Trait(_, ref generics, ..) => { + ItemKind::Trait(_, _, ref generics, ..) => { let def_id = self.lctx.resolver.definitions().local_def_id(item.id); let count = generics.lifetimes.len(); self.lctx.type_def_lifetime_params.insert(def_id, count); @@ -223,6 +267,21 @@ impl<'a> LoweringContext<'a> { lctx: &'lcx mut LoweringContext<'interner>, } + impl<'lcx, 'interner> ItemLowerer<'lcx, 'interner> { + fn with_trait_impl_ref(&mut self, trait_impl_ref: &Option, f: F) + where F: FnOnce(&mut Self) + { + let old = self.lctx.is_in_trait_impl; + self.lctx.is_in_trait_impl = if let &None = trait_impl_ref { + false + } else { + true + }; + f(self); + self.lctx.is_in_trait_impl = old; + } + } + impl<'lcx, 'interner> Visitor<'lcx> for ItemLowerer<'lcx, 'interner> { fn visit_item(&mut self, item: &'lcx Item) { let mut item_lowered = true; @@ -235,7 +294,23 @@ impl<'a> LoweringContext<'a> { }); if item_lowered { - visit::walk_item(self, item); + let item_lifetimes = match self.lctx.items.get(&item.id).unwrap().node { + hir::Item_::ItemImpl(_,_,_,ref generics,..) | + hir::Item_::ItemTrait(_,_,ref generics,..) => + generics.lifetimes.clone(), + _ => Vec::new().into(), + }; + + self.lctx.with_parent_impl_lifetime_defs(&item_lifetimes, |this| { + let this = &mut ItemLowerer { lctx: this }; + if let ItemKind::Impl(_,_,_,_,ref opt_trait_ref,_,_) = item.node { + this.with_trait_impl_ref(opt_trait_ref, |this| { + visit::walk_item(this, item) + }); + } else { + visit::walk_item(this, item); + } + }); } } @@ -284,7 +359,7 @@ impl<'a> LoweringContext<'a> { bodies: self.bodies, body_ids, trait_impls: self.trait_impls, - trait_default_impl: self.trait_default_impl, + trait_auto_impl: self.trait_auto_impl, } } @@ -448,6 +523,124 @@ impl<'a> LoweringContext<'a> { span.with_ctxt(SyntaxContext::empty().apply_mark(mark)) } + // Creates a new hir::LifetimeDef for every new lifetime encountered + // while evaluating `f`. Definitions are created with the parent provided. + // If no `parent_id` is provided, no definitions will be returned. + fn collect_in_band_lifetime_defs( + &mut self, + parent_id: Option, + f: F + ) -> (Vec, T) where F: FnOnce(&mut LoweringContext) -> T + { + assert!(!self.is_collecting_in_band_lifetimes); + assert!(self.lifetimes_to_define.is_empty()); + self.is_collecting_in_band_lifetimes = self.sess.features.borrow().in_band_lifetimes; + + let res = f(self); + + self.is_collecting_in_band_lifetimes = false; + + let lifetimes_to_define = self.lifetimes_to_define.split_off(0); + + let lifetime_defs = match parent_id { + Some(parent_id) => lifetimes_to_define.into_iter().map(|(span, name)| { + let def_node_id = self.next_id().node_id; + + // Add a definition for the in-band lifetime def + self.resolver.definitions().create_def_with_parent( + parent_id.index, + def_node_id, + DefPathData::LifetimeDef(name.as_str()), + DefIndexAddressSpace::High, + Mark::root() + ); + + hir::LifetimeDef { + lifetime: hir::Lifetime { + id: def_node_id, + span, + name: hir::LifetimeName::Name(name), + }, + bounds: Vec::new().into(), + pure_wrt_drop: false, + in_band: true, + } + }).collect(), + None => Vec::new(), + }; + + (lifetime_defs, res) + } + + // Evaluates `f` with the lifetimes in `lt_defs` in-scope. + // This is used to track which lifetimes have already been defined, and + // which are new in-band lifetimes that need to have a definition created + // for them. + fn with_in_scope_lifetime_defs( + &mut self, + lt_defs: &[LifetimeDef], + f: F + ) -> T where F: FnOnce(&mut LoweringContext) -> T + { + let old_len = self.in_scope_lifetimes.len(); + let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.ident.name); + self.in_scope_lifetimes.extend(lt_def_names); + + let res = f(self); + + self.in_scope_lifetimes.truncate(old_len); + res + } + + // Same as the method above, but accepts `hir::LifetimeDef`s + // instead of `ast::LifetimeDef`s. + // This should only be used with generics that have already had their + // in-band lifetimes added. In practice, this means that this function is + // only used when lowering a child item of a trait or impl. + fn with_parent_impl_lifetime_defs( + &mut self, + lt_defs: &[hir::LifetimeDef], + f: F + ) -> T where F: FnOnce(&mut LoweringContext) -> T + { + let old_len = self.in_scope_lifetimes.len(); + let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.name.name()); + self.in_scope_lifetimes.extend(lt_def_names); + + let res = f(self); + + self.in_scope_lifetimes.truncate(old_len); + res + } + + // Appends in-band lifetime defs to the existing set of out-of-band lifetime defs. + // Evaluates all within the context of the out-of-band defs. + // If provided, `impl_item_id` is used to find the parent impls of impl items so + // that their generics are not duplicated. + fn add_in_band_lifetime_defs( + &mut self, + generics: &Generics, + parent_id: Option, + f: F + ) -> (hir::Generics, T) + where F: FnOnce(&mut LoweringContext) -> T + { + let (in_band_defs, (mut lowered_generics, res)) = + self.with_in_scope_lifetime_defs(&generics.lifetimes, |this| { + this.collect_in_band_lifetime_defs(parent_id, |this| { + (this.lower_generics(generics), f(this)) + }) + }); + + lowered_generics.lifetimes = + lowered_generics.lifetimes + .iter().cloned() + .chain(in_band_defs.into_iter()) + .collect(); + + (lowered_generics, res) + } + fn with_catch_scope(&mut self, catch_id: NodeId, f: F) -> T where F: FnOnce(&mut LoweringContext) -> T { @@ -644,47 +837,49 @@ impl<'a> LoweringContext<'a> { } } - fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding { + fn lower_ty_binding(&mut self, b: &TypeBinding, itctx: ImplTraitContext) -> hir::TypeBinding { hir::TypeBinding { id: self.lower_node_id(b.id).node_id, name: self.lower_ident(b.ident), - ty: self.lower_ty(&b.ty), + ty: self.lower_ty(&b.ty, itctx), span: b.span, } } - fn lower_ty(&mut self, t: &Ty) -> P { + fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> P { let kind = match t.node { TyKind::Infer => hir::TyInfer, TyKind::Err => hir::TyErr, - TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)), - TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)), + TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty, itctx)), + TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt, itctx)), TyKind::Rptr(ref region, ref mt) => { let span = t.span.with_hi(t.span.lo()); let lifetime = match *region { Some(ref lt) => self.lower_lifetime(lt), None => self.elided_lifetime(span) }; - hir::TyRptr(lifetime, self.lower_mt(mt)) + hir::TyRptr(lifetime, self.lower_mt(mt, itctx)) } TyKind::BareFn(ref f) => { - hir::TyBareFn(P(hir::BareFnTy { - lifetimes: self.lower_lifetime_defs(&f.lifetimes), - unsafety: self.lower_unsafety(f.unsafety), - abi: f.abi, - decl: self.lower_fn_decl(&f.decl), - })) + self.with_in_scope_lifetime_defs(&f.lifetimes, |this| + hir::TyBareFn(P(hir::BareFnTy { + lifetimes: this.lower_lifetime_defs(&f.lifetimes), + unsafety: this.lower_unsafety(f.unsafety), + abi: f.abi, + decl: this.lower_fn_decl(&f.decl, None, false), + arg_names: this.lower_fn_args_to_names(&f.decl), + }))) } TyKind::Never => hir::TyNever, TyKind::Tup(ref tys) => { - hir::TyTup(tys.iter().map(|ty| self.lower_ty(ty)).collect()) + hir::TyTup(tys.iter().map(|ty| self.lower_ty(ty, itctx)).collect()) } TyKind::Paren(ref ty) => { - return self.lower_ty(ty); + return self.lower_ty(ty, itctx); } TyKind::Path(ref qself, ref path) => { let id = self.lower_node_id(t.id); - let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit); + let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit, itctx); return self.ty_path(id, t.span, qpath); } TyKind::ImplicitSelf => { @@ -698,18 +893,18 @@ impl<'a> LoweringContext<'a> { } TyKind::Array(ref ty, ref length) => { let length = self.lower_body(None, |this| this.lower_expr(length)); - hir::TyArray(self.lower_ty(ty), length) + hir::TyArray(self.lower_ty(ty, itctx), length) } TyKind::Typeof(ref expr) => { let expr = self.lower_body(None, |this| this.lower_expr(expr)); hir::TyTypeof(expr) } - TyKind::TraitObject(ref bounds) => { + TyKind::TraitObject(ref bounds, ..) => { let mut lifetime_bound = None; let bounds = bounds.iter().filter_map(|bound| { match *bound { TraitTyParamBound(ref ty, TraitBoundModifier::None) => { - Some(self.lower_poly_trait_ref(ty)) + Some(self.lower_poly_trait_ref(ty, itctx)) } TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, RegionTyParamBound(ref lifetime) => { @@ -726,7 +921,50 @@ impl<'a> LoweringContext<'a> { hir::TyTraitObject(bounds, lifetime_bound) } TyKind::ImplTrait(ref bounds) => { - hir::TyImplTrait(self.lower_bounds(bounds)) + use syntax::feature_gate::{emit_feature_err, GateIssue}; + match itctx { + ImplTraitContext::Existential => { + let has_feature = self.sess.features.borrow().conservative_impl_trait; + if !t.span.allows_unstable() && !has_feature { + emit_feature_err(&self.sess.parse_sess, "conservative_impl_trait", + t.span, GateIssue::Language, + "`impl Trait` in return position is experimental"); + } + let def_index = self.resolver.definitions().opt_def_index(t.id).unwrap(); + let hir_bounds = self.lower_bounds(bounds, itctx); + let (lifetimes, lifetime_defs) = + self.lifetimes_from_impl_trait_bounds(def_index, &hir_bounds); + + hir::TyImplTraitExistential(hir::ExistTy { + generics: hir::Generics { + lifetimes: lifetime_defs, + // Type parameters are taken from environment: + ty_params: Vec::new().into(), + where_clause: hir::WhereClause { + id: self.next_id().node_id, + predicates: Vec::new().into(), + }, + span: t.span, + }, + bounds: hir_bounds, + }, lifetimes) + }, + ImplTraitContext::Universal(def_id) => { + let has_feature = self.sess.features.borrow().universal_impl_trait; + if !t.span.allows_unstable() && !has_feature { + emit_feature_err(&self.sess.parse_sess, "universal_impl_trait", + t.span, GateIssue::Language, + "`impl Trait` in argument position is experimental"); + } + hir::TyImplTraitUniversal(def_id, self.lower_bounds(bounds, itctx)) + }, + ImplTraitContext::Disallowed => { + span_err!(self.sess, t.span, E0562, + "`impl Trait` not allowed outside of function \ + and inherent method return types"); + hir::TyErr + } + } } TyKind::Mac(_) => panic!("TyMac should have been expanded by now."), }; @@ -740,6 +978,112 @@ impl<'a> LoweringContext<'a> { }) } + fn lifetimes_from_impl_trait_bounds( + &mut self, + parent_index: DefIndex, + bounds: &hir::TyParamBounds + ) -> (HirVec, HirVec) { + + // This visitor walks over impl trait bounds and creates defs for all lifetimes which + // appear in the bounds, excluding lifetimes that are created within the bounds. + // e.g. 'a, 'b, but not 'c in `impl for<'c> SomeTrait<'a, 'b, 'c>` + struct ImplTraitLifetimeCollector<'r, 'a: 'r> { + context: &'r mut LoweringContext<'a>, + parent: DefIndex, + currently_bound_lifetimes: Vec, + already_defined_lifetimes: HashSet, + output_lifetimes: Vec, + output_lifetime_defs: Vec, + } + + impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a> { + fn nested_visit_map<'this>(&'this mut self) + -> hir::intravisit::NestedVisitorMap<'this, 'v> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_poly_trait_ref(&mut self, + polytr: &'v hir::PolyTraitRef, + _: hir::TraitBoundModifier) { + let old_len = self.currently_bound_lifetimes.len(); + + // Record the introduction of 'a in `for<'a> ...` + for lt_def in &polytr.bound_lifetimes { + // Introduce lifetimes one at a time so that we can handle + // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...` + if let hir::LifetimeName::Name(name) = lt_def.lifetime.name { + self.currently_bound_lifetimes.push(name); + } + + // Visit the lifetime bounds + for lt_bound in <_def.bounds { + self.visit_lifetime(<_bound); + } + } + + hir::intravisit::walk_trait_ref(self, &polytr.trait_ref); + + self.currently_bound_lifetimes.truncate(old_len); + } + + fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { + // Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes) + if let hir::LifetimeName::Name(lifetime_name) = lifetime.name { + if !self.currently_bound_lifetimes.contains(&lifetime_name) && + !self.already_defined_lifetimes.contains(&lifetime_name) + { + self.already_defined_lifetimes.insert(lifetime_name); + let name = hir::LifetimeName::Name(lifetime_name); + + self.output_lifetimes.push(hir::Lifetime { + id: self.context.next_id().node_id, + span: lifetime.span, + name, + }); + + let def_node_id = self.context.next_id().node_id; + self.context.resolver.definitions().create_def_with_parent( + self.parent, + def_node_id, + DefPathData::LifetimeDef(lifetime_name.as_str()), + DefIndexAddressSpace::High, + Mark::root() + ); + let def_lifetime = hir::Lifetime { + id: def_node_id, + span: lifetime.span, + name, + }; + self.output_lifetime_defs.push(hir::LifetimeDef { + lifetime: def_lifetime, + bounds: Vec::new().into(), + pure_wrt_drop: false, + in_band: false, + }); + } + } + } + } + + let mut lifetime_collector = ImplTraitLifetimeCollector { + context: self, + parent: parent_index, + currently_bound_lifetimes: Vec::new(), + already_defined_lifetimes: HashSet::new(), + output_lifetimes: Vec::new(), + output_lifetime_defs: Vec::new(), + }; + + for bound in bounds { + hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound); + } + + ( + lifetime_collector.output_lifetimes.into(), + lifetime_collector.output_lifetime_defs.into() + ) + } + fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod { hir::ForeignMod { abi: fm.abi, @@ -772,10 +1116,11 @@ impl<'a> LoweringContext<'a> { id: NodeId, qself: &Option, p: &Path, - param_mode: ParamMode) + param_mode: ParamMode, + itctx: ImplTraitContext) -> hir::QPath { let qself_position = qself.as_ref().map(|q| q.position); - let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty)); + let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx)); let resolution = self.resolver.get_resolution(id) .unwrap_or(PathResolution::new(Def::Err)); @@ -837,12 +1182,15 @@ impl<'a> LoweringContext<'a> { return n; } assert!(!def_id.is_local()); - let n = self.cstore.item_generics_cloned_untracked(def_id).regions.len(); + let n = self.cstore + .item_generics_cloned_untracked(def_id, self.sess) + .regions + .len(); self.type_def_lifetime_params.insert(def_id, n); n }); self.lower_path_segment(p.span, segment, param_mode, num_lifetimes, - parenthesized_generic_args) + parenthesized_generic_args, itctx) }).collect(), span: p.span, }); @@ -878,7 +1226,8 @@ impl<'a> LoweringContext<'a> { // * final path is `<<>::IntoIter>::Item>::clone` for (i, segment) in p.segments.iter().enumerate().skip(proj_start) { let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0, - ParenthesizedGenericArgs::Warn)); + ParenthesizedGenericArgs::Warn, + itctx)); let qpath = hir::QPath::TypeRelative(ty, segment); // It's finished, return the extension of the right node type. @@ -912,7 +1261,8 @@ impl<'a> LoweringContext<'a> { def: self.expect_full_def(id), segments: segments.map(|segment| { self.lower_path_segment(p.span, segment, param_mode, 0, - ParenthesizedGenericArgs::Err) + ParenthesizedGenericArgs::Err, + ImplTraitContext::Disallowed) }).chain(name.map(|name| hir::PathSegment::from_name(name))) .collect(), span: p.span, @@ -933,16 +1283,18 @@ impl<'a> LoweringContext<'a> { segment: &PathSegment, param_mode: ParamMode, expected_lifetimes: usize, - parenthesized_generic_args: ParenthesizedGenericArgs) + parenthesized_generic_args: ParenthesizedGenericArgs, + itctx: ImplTraitContext) -> hir::PathSegment { let (mut parameters, infer_types) = if let Some(ref parameters) = segment.parameters { let msg = "parenthesized parameters may only be used with a trait"; match **parameters { PathParameters::AngleBracketed(ref data) => { - self.lower_angle_bracketed_parameter_data(data, param_mode) + self.lower_angle_bracketed_parameter_data(data, param_mode, itctx) } PathParameters::Parenthesized(ref data) => match parenthesized_generic_args { - ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data), + ParenthesizedGenericArgs::Ok => + self.lower_parenthesized_parameter_data(data), ParenthesizedGenericArgs::Warn => { self.sess.buffer_lint(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, CRATE_NODE_ID, data.span, msg.into()); @@ -956,7 +1308,7 @@ impl<'a> LoweringContext<'a> { } } } else { - self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode) + self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode, itctx) }; if !parameters.parenthesized && parameters.lifetimes.is_empty() { @@ -974,13 +1326,14 @@ impl<'a> LoweringContext<'a> { fn lower_angle_bracketed_parameter_data(&mut self, data: &AngleBracketedParameterData, - param_mode: ParamMode) + param_mode: ParamMode, + itctx: ImplTraitContext) -> (hir::PathParameters, bool) { let &AngleBracketedParameterData { ref lifetimes, ref types, ref bindings, .. } = data; (hir::PathParameters { lifetimes: self.lower_lifetimes(lifetimes), - types: types.iter().map(|ty| self.lower_ty(ty)).collect(), - bindings: bindings.iter().map(|b| self.lower_ty_binding(b)).collect(), + types: types.iter().map(|ty| self.lower_ty(ty, itctx)).collect(), + bindings: bindings.iter().map(|b| self.lower_ty_binding(b, itctx)).collect(), parenthesized: false, }, types.is_empty() && param_mode == ParamMode::Optional) } @@ -988,8 +1341,9 @@ impl<'a> LoweringContext<'a> { fn lower_parenthesized_parameter_data(&mut self, data: &ParenthesizedParameterData) -> (hir::PathParameters, bool) { + const DISALLOWED: ImplTraitContext = ImplTraitContext::Disallowed; let &ParenthesizedParameterData { ref inputs, ref output, span } = data; - let inputs = inputs.iter().map(|ty| self.lower_ty(ty)).collect(); + let inputs = inputs.iter().map(|ty| self.lower_ty(ty, DISALLOWED)).collect(); let mk_tup = |this: &mut Self, tys, span| { let LoweredNodeId { node_id, hir_id } = this.next_id(); P(hir::Ty { node: hir::TyTup(tys), id: node_id, hir_id, span }) @@ -1001,7 +1355,7 @@ impl<'a> LoweringContext<'a> { bindings: hir_vec![hir::TypeBinding { id: self.next_id().node_id, name: Symbol::intern(FN_OUTPUT_NAME), - ty: output.as_ref().map(|ty| self.lower_ty(&ty)) + ty: output.as_ref().map(|ty| self.lower_ty(&ty, DISALLOWED)) .unwrap_or_else(|| mk_tup(self, hir::HirVec::new(), span)), span: output.as_ref().map_or(span, |ty| ty.span), }], @@ -1014,7 +1368,7 @@ impl<'a> LoweringContext<'a> { P(hir::Local { id: node_id, hir_id, - ty: l.ty.as_ref().map(|t| self.lower_ty(t)), + ty: l.ty.as_ref().map(|t| self.lower_ty(t, ImplTraitContext::Disallowed)), pat: self.lower_pat(&l.pat), init: l.init.as_ref().map(|e| P(self.lower_expr(e))), span: l.span, @@ -1051,11 +1405,32 @@ impl<'a> LoweringContext<'a> { }).collect() } - fn lower_fn_decl(&mut self, decl: &FnDecl) -> P { + + fn lower_fn_decl(&mut self, + decl: &FnDecl, + fn_def_id: Option, + impl_trait_return_allow: bool) + -> P { + // NOTE: The two last paramters here have to do with impl Trait. If fn_def_id is Some, + // then impl Trait arguments are lowered into generic paramters on the given + // fn_def_id, otherwise impl Trait is disallowed. (for now) + // + // Furthermore, if impl_trait_return_allow is true, then impl Trait may be used in + // return positions as well. This guards against trait declarations and their impls + // where impl Trait is disallowed. (again for now) P(hir::FnDecl { - inputs: decl.inputs.iter().map(|arg| self.lower_ty(&arg.ty)).collect(), + inputs: decl.inputs.iter() + .map(|arg| if let Some(def_id) = fn_def_id { + self.lower_ty(&arg.ty, ImplTraitContext::Universal(def_id)) + } else { + self.lower_ty(&arg.ty, ImplTraitContext::Disallowed) + }).collect(), output: match decl.output { - FunctionRetTy::Ty(ref ty) => hir::Return(self.lower_ty(ty)), + FunctionRetTy::Ty(ref ty) => match fn_def_id { + Some(_) if impl_trait_return_allow => + hir::Return(self.lower_ty(ty, ImplTraitContext::Existential)), + _ => hir::Return(self.lower_ty(ty, ImplTraitContext::Disallowed)), + }, FunctionRetTy::Default(span) => hir::DefaultReturn(span), }, variadic: decl.variadic, @@ -1069,10 +1444,11 @@ impl<'a> LoweringContext<'a> { }) } - fn lower_ty_param_bound(&mut self, tpb: &TyParamBound) -> hir::TyParamBound { + fn lower_ty_param_bound(&mut self, tpb: &TyParamBound, itctx: ImplTraitContext) + -> hir::TyParamBound { match *tpb { TraitTyParamBound(ref ty, modifier) => { - hir::TraitTyParamBound(self.lower_poly_trait_ref(ty), + hir::TraitTyParamBound(self.lower_poly_trait_ref(ty, itctx), self.lower_trait_bound_modifier(modifier)) } RegionTyParamBound(ref lifetime) => { @@ -1091,18 +1467,25 @@ impl<'a> LoweringContext<'a> { name = Symbol::gensym("Self"); } - let mut bounds = self.lower_bounds(&tp.bounds); + let itctx = ImplTraitContext::Universal(self.resolver.definitions().local_def_id(tp.id)); + let mut bounds = self.lower_bounds(&tp.bounds, itctx); if !add_bounds.is_empty() { - bounds = bounds.into_iter().chain(self.lower_bounds(add_bounds).into_iter()).collect(); + bounds = bounds.into_iter().chain( + self.lower_bounds(add_bounds, itctx).into_iter() + ).collect(); } hir::TyParam { id: self.lower_node_id(tp.id).node_id, name, bounds, - default: tp.default.as_ref().map(|x| self.lower_ty(x)), + default: tp.default.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::Disallowed)), span: tp.span, pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")), + synthetic: tp.attrs.iter() + .filter(|attr| attr.check_name("rustc_synthetic")) + .map(|_| hir::SyntheticTyParamKind::ImplTrait) + .nth(0), } } @@ -1114,23 +1497,44 @@ impl<'a> LoweringContext<'a> { } fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { + let name = match self.lower_ident(l.ident) { + x if x == "'_" => hir::LifetimeName::Underscore, + x if x == "'static" => hir::LifetimeName::Static, + name => { + if self.is_collecting_in_band_lifetimes && + !self.in_scope_lifetimes.contains(&name) && + self.lifetimes_to_define.iter() + .find(|&&(_, lt_name)| lt_name == name) + .is_none() + { + self.lifetimes_to_define.push((l.span, name)); + } + + hir::LifetimeName::Name(name) + } + }; + hir::Lifetime { id: self.lower_node_id(l.id).node_id, - name: match self.lower_ident(l.ident) { - x if x == "'_" => hir::LifetimeName::Underscore, - x if x == "'static" => hir::LifetimeName::Static, - name => hir::LifetimeName::Name(name), - }, + name, span: l.span, } } fn lower_lifetime_def(&mut self, l: &LifetimeDef) -> hir::LifetimeDef { - hir::LifetimeDef { + let was_collecting_in_band = self.is_collecting_in_band_lifetimes; + self.is_collecting_in_band_lifetimes = false; + + let def = hir::LifetimeDef { lifetime: self.lower_lifetime(&l.lifetime), bounds: self.lower_lifetimes(&l.bounds), pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")), - } + in_band: false, + }; + + self.is_collecting_in_band_lifetimes = was_collecting_in_band; + + def } fn lower_lifetimes(&mut self, lts: &Vec) -> hir::HirVec { @@ -1205,15 +1609,19 @@ impl<'a> LoweringContext<'a> { ref bounded_ty, ref bounds, span}) => { - hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_lifetimes: self.lower_lifetime_defs(bound_lifetimes), - bounded_ty: self.lower_ty(bounded_ty), - bounds: bounds.iter().filter_map(|bound| match *bound { - // Ignore `?Trait` bounds, they were copied into type parameters already. - TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, - _ => Some(self.lower_ty_param_bound(bound)) - }).collect(), - span, + self.with_in_scope_lifetime_defs(bound_lifetimes, |this| { + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bound_lifetimes: this.lower_lifetime_defs(bound_lifetimes), + bounded_ty: this.lower_ty(bounded_ty, ImplTraitContext::Disallowed), + bounds: bounds.iter().filter_map(|bound| match *bound { + // Ignore `?Trait` bounds. + // Tthey were copied into type parameters already. + TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, + _ => Some(this.lower_ty_param_bound( + bound, ImplTraitContext::Disallowed)) + }).collect(), + span, + }) }) } WherePredicate::RegionPredicate(WhereRegionPredicate{ ref lifetime, @@ -1231,8 +1639,8 @@ impl<'a> LoweringContext<'a> { span}) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { id: self.lower_node_id(id).node_id, - lhs_ty: self.lower_ty(lhs_ty), - rhs_ty: self.lower_ty(rhs_ty), + lhs_ty: self.lower_ty(lhs_ty, ImplTraitContext::Disallowed), + rhs_ty: self.lower_ty(rhs_ty, ImplTraitContext::Disallowed), span, }) } @@ -1259,8 +1667,8 @@ impl<'a> LoweringContext<'a> { } } - fn lower_trait_ref(&mut self, p: &TraitRef) -> hir::TraitRef { - let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit) { + fn lower_trait_ref(&mut self, p: &TraitRef, itctx: ImplTraitContext) -> hir::TraitRef { + let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit, itctx) { hir::QPath::Resolved(None, path) => path.and_then(|path| path), qpath => bug!("lower_trait_ref: unexpected QPath `{:?}`", qpath) }; @@ -1270,10 +1678,17 @@ impl<'a> LoweringContext<'a> { } } - fn lower_poly_trait_ref(&mut self, p: &PolyTraitRef) -> hir::PolyTraitRef { + fn lower_poly_trait_ref(&mut self, + p: &PolyTraitRef, + itctx: ImplTraitContext) + -> hir::PolyTraitRef { + let bound_lifetimes = self.lower_lifetime_defs(&p.bound_lifetimes); + let trait_ref = self.with_parent_impl_lifetime_defs(&bound_lifetimes, + |this| this.lower_trait_ref(&p.trait_ref, itctx)); + hir::PolyTraitRef { - bound_lifetimes: self.lower_lifetime_defs(&p.bound_lifetimes), - trait_ref: self.lower_trait_ref(&p.trait_ref), + bound_lifetimes, + trait_ref, span: p.span, } } @@ -1288,7 +1703,7 @@ impl<'a> LoweringContext<'a> { None => Ident { name: Symbol::intern(&index.to_string()), ctxt: f.span.ctxt() }, }), vis: self.lower_visibility(&f.vis, None), - ty: self.lower_ty(&f.ty), + ty: self.lower_ty(&f.ty, ImplTraitContext::Disallowed), attrs: self.lower_attrs(&f.attrs), } } @@ -1302,15 +1717,16 @@ impl<'a> LoweringContext<'a> { } } - fn lower_mt(&mut self, mt: &MutTy) -> hir::MutTy { + fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy { hir::MutTy { - ty: self.lower_ty(&mt.ty), + ty: self.lower_ty(&mt.ty, itctx), mutbl: self.lower_mutability(mt.mutbl), } } - fn lower_bounds(&mut self, bounds: &[TyParamBound]) -> hir::TyParamBounds { - bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect() + fn lower_bounds(&mut self, bounds: &[TyParamBound], itctx: ImplTraitContext) + -> hir::TyParamBounds { + bounds.iter().map(|bound| self.lower_ty_param_bound(bound, itctx)).collect() } fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> P { @@ -1352,110 +1768,50 @@ impl<'a> LoweringContext<'a> { -> hir::Item_ { match *i { ItemKind::ExternCrate(string) => hir::ItemExternCrate(string), - ItemKind::Use(ref view_path) => { - let path = match view_path.node { - ViewPathSimple(_, ref path) => path, - ViewPathGlob(ref path) => path, - ViewPathList(ref path, ref path_list_idents) => { - for &Spanned { node: ref import, span } in path_list_idents { - // `use a::{self as x, b as y};` lowers to - // `use a as x; use a::b as y;` - let mut ident = import.name; - let suffix = if ident.name == keywords::SelfValue.name() { - if let Some(last) = path.segments.last() { - ident = last.identifier; - } - None - } else { - Some(ident.name) - }; - - let mut path = self.lower_path_extra(import.id, path, suffix, - ParamMode::Explicit, true); - path.span = span; - - self.allocate_hir_id_counter(import.id, import); - let LoweredNodeId { - node_id: import_node_id, - hir_id: import_hir_id, - } = self.lower_node_id(import.id); - - self.with_hir_id_owner(import_node_id, |this| { - let vis = match *vis { - hir::Visibility::Public => hir::Visibility::Public, - hir::Visibility::Crate => hir::Visibility::Crate, - hir::Visibility::Inherited => hir::Visibility::Inherited, - hir::Visibility::Restricted { ref path, id: _ } => { - hir::Visibility::Restricted { - path: path.clone(), - // We are allocating a new NodeId here - id: this.next_id().node_id, - } - } - }; - - this.items.insert(import_node_id, hir::Item { - id: import_node_id, - hir_id: import_hir_id, - name: import.rename.unwrap_or(ident).name, - attrs: attrs.clone(), - node: hir::ItemUse(P(path), hir::UseKind::Single), - vis, - span, - }); - }); - } - path - } + ItemKind::Use(ref use_tree) => { + // Start with an empty prefix + let prefix = Path { + segments: vec![], + span: use_tree.span, }; - let path = P(self.lower_path(id, path, ParamMode::Explicit, true)); - let kind = match view_path.node { - ViewPathSimple(ident, _) => { - *name = ident.name; - hir::UseKind::Single - } - ViewPathGlob(_) => { - hir::UseKind::Glob - } - ViewPathList(..) => { - // Privatize the degenerate import base, used only to check - // the stability of `use a::{};`, to avoid it showing up as - // a reexport by accident when `pub`, e.g. in documentation. - *vis = hir::Inherited; - hir::UseKind::ListStem - } - }; - hir::ItemUse(path, kind) + + self.lower_use_tree(use_tree, &prefix, id, vis, name, attrs) } ItemKind::Static(ref t, m, ref e) => { let value = self.lower_body(None, |this| this.lower_expr(e)); - hir::ItemStatic(self.lower_ty(t), + hir::ItemStatic(self.lower_ty(t, ImplTraitContext::Disallowed), self.lower_mutability(m), value) } ItemKind::Const(ref t, ref e) => { let value = self.lower_body(None, |this| this.lower_expr(e)); - hir::ItemConst(self.lower_ty(t), value) + hir::ItemConst(self.lower_ty(t, ImplTraitContext::Disallowed), value) } ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => { + let fn_def_id = self.resolver.definitions().opt_local_def_id(id); self.with_new_scopes(|this| { let body_id = this.lower_body(Some(decl), |this| { let body = this.lower_block(body, false); this.expr_block(body, ThinVec::new()) }); - hir::ItemFn(this.lower_fn_decl(decl), - this.lower_unsafety(unsafety), - this.lower_constness(constness), - abi, - this.lower_generics(generics), - body_id) + let (generics, fn_decl) = + this.add_in_band_lifetime_defs(generics, fn_def_id, |this| + this.lower_fn_decl(decl, fn_def_id, true)); + + hir::ItemFn(fn_decl, + this.lower_unsafety(unsafety), + this.lower_constness(constness), + abi, + generics, + body_id) }) } ItemKind::Mod(ref m) => hir::ItemMod(self.lower_mod(m)), ItemKind::ForeignMod(ref nm) => hir::ItemForeignMod(self.lower_foreign_mod(nm)), ItemKind::GlobalAsm(ref ga) => hir::ItemGlobalAsm(self.lower_global_asm(ga)), ItemKind::Ty(ref t, ref generics) => { - hir::ItemTy(self.lower_ty(t), self.lower_generics(generics)) + hir::ItemTy(self.lower_ty(t, ImplTraitContext::Disallowed), + self.lower_generics(generics)) } ItemKind::Enum(ref enum_definition, ref generics) => { hir::ItemEnum(hir::EnumDef { @@ -1474,92 +1830,235 @@ impl<'a> LoweringContext<'a> { let vdata = self.lower_variant_data(vdata); hir::ItemUnion(vdata, self.lower_generics(generics)) } - ItemKind::DefaultImpl(unsafety, ref trait_ref) => { - let trait_ref = self.lower_trait_ref(trait_ref); + ItemKind::AutoImpl(unsafety, ref trait_ref) => { + let trait_ref = self.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed); if let Def::Trait(def_id) = trait_ref.path.def { - self.trait_default_impl.insert(def_id, id); + self.trait_auto_impl.insert(def_id, id); } - hir::ItemDefaultImpl(self.lower_unsafety(unsafety), + hir::ItemAutoImpl(self.lower_unsafety(unsafety), trait_ref) } ItemKind::Impl(unsafety, polarity, defaultness, - ref generics, + ref ast_generics, ref ifce, ref ty, ref impl_items) => { - let new_impl_items = impl_items.iter() - .map(|item| self.lower_impl_item_ref(item)) - .collect(); - let ifce = ifce.as_ref().map(|trait_ref| self.lower_trait_ref(trait_ref)); - - if let Some(ref trait_ref) = ifce { - if let Def::Trait(def_id) = trait_ref.path.def { - self.trait_impls.entry(def_id).or_insert(vec![]).push(id); - } - } + let def_id = self.resolver.definitions().opt_local_def_id(id); + let (generics, (ifce, lowered_ty)) = + self.add_in_band_lifetime_defs(ast_generics, def_id, |this| { + let ifce = ifce.as_ref().map(|trait_ref| { + this.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed) + }); + + if let Some(ref trait_ref) = ifce { + if let Def::Trait(def_id) = trait_ref.path.def { + this.trait_impls.entry(def_id).or_insert(vec![]).push(id); + } + } + + let lowered_ty = this.lower_ty(ty, ImplTraitContext::Disallowed); + + (ifce, lowered_ty) + }); + + let new_impl_items = self.with_in_scope_lifetime_defs( + &ast_generics.lifetimes, |this| { + impl_items.iter() + .map(|item| this.lower_impl_item_ref(item)) + .collect() + }); + hir::ItemImpl(self.lower_unsafety(unsafety), self.lower_impl_polarity(polarity), self.lower_defaultness(defaultness, true /* [1] */), - self.lower_generics(generics), + generics, ifce, - self.lower_ty(ty), + lowered_ty, new_impl_items) } - ItemKind::Trait(unsafety, ref generics, ref bounds, ref items) => { - let bounds = self.lower_bounds(bounds); + ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { + let bounds = self.lower_bounds(bounds, ImplTraitContext::Disallowed); let items = items.iter().map(|item| self.lower_trait_item_ref(item)).collect(); - hir::ItemTrait(self.lower_unsafety(unsafety), + hir::ItemTrait(self.lower_is_auto(is_auto), + self.lower_unsafety(unsafety), self.lower_generics(generics), bounds, items) } - ItemKind::MacroDef(..) | ItemKind::Mac(..) => panic!("Shouldn't still be around"), + ItemKind::MacroDef(..) | ItemKind::Mac(..) => { + panic!("Shouldn't still be around") + } } // [1] `defaultness.has_value()` is never called for an `impl`, always `true` in order to // not cause an assertion failure inside the `lower_defaultness` function } + fn lower_use_tree(&mut self, + tree: &UseTree, + prefix: &Path, + id: NodeId, + vis: &mut hir::Visibility, + name: &mut Name, + attrs: &hir::HirVec) + -> hir::Item_ { + let path = &tree.prefix; + + match tree.kind { + UseTreeKind::Simple(ident) => { + *name = ident.name; + + // First apply the prefix to the path + let mut path = Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span.to(prefix.span), + }; + + // Correctly resolve `self` imports + if path.segments.last().unwrap().identifier.name == keywords::SelfValue.name() { + let _ = path.segments.pop(); + if ident.name == keywords::SelfValue.name() { + *name = path.segments.last().unwrap().identifier.name; + } + } + + let path = P(self.lower_path(id, &path, ParamMode::Explicit, true)); + hir::ItemUse(path, hir::UseKind::Single) + } + UseTreeKind::Glob => { + let path = P(self.lower_path(id, &Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }, ParamMode::Explicit, true)); + hir::ItemUse(path, hir::UseKind::Glob) + } + UseTreeKind::Nested(ref trees) => { + let prefix = Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: prefix.span.to(path.span), + }; + + // Add all the nested PathListItems in the HIR + for &(ref use_tree, id) in trees { + self.allocate_hir_id_counter(id, &use_tree); + let LoweredNodeId { + node_id: new_id, + hir_id: new_hir_id, + } = self.lower_node_id(id); + + let mut vis = vis.clone(); + let mut name = name.clone(); + let item = self.lower_use_tree( + use_tree, &prefix, new_id, &mut vis, &mut name, &attrs, + ); + + self.with_hir_id_owner(new_id, |this| { + let vis = match vis { + hir::Visibility::Public => hir::Visibility::Public, + hir::Visibility::Crate => hir::Visibility::Crate, + hir::Visibility::Inherited => hir::Visibility::Inherited, + hir::Visibility::Restricted { ref path, id: _ } => { + hir::Visibility::Restricted { + path: path.clone(), + // We are allocating a new NodeId here + id: this.next_id().node_id, + } + } + }; + + this.items.insert(new_id, hir::Item { + id: new_id, + hir_id: new_hir_id, + name: name, + attrs: attrs.clone(), + node: item, + vis, + span: use_tree.span, + }); + }); + } + + // Privatize the degenerate import base, used only to check + // the stability of `use a::{};`, to avoid it showing up as + // a reexport by accident when `pub`, e.g. in documentation. + let path = P(self.lower_path(id, &prefix, ParamMode::Explicit, true)); + *vis = hir::Inherited; + hir::ItemUse(path, hir::UseKind::ListStem) + } + } + } + fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { self.with_parent_def(i.id, |this| { let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id); + let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id); + + let (generics, node) = match i.node { + TraitItemKind::Const(ref ty, ref default) => { + ( + this.lower_generics(&i.generics), + hir::TraitItemKind::Const( + this.lower_ty(ty, ImplTraitContext::Disallowed), + default.as_ref().map(|x| { + this.lower_body(None, |this| this.lower_expr(x)) + })) + ) + } + TraitItemKind::Method(ref sig, None) => { + let names = this.lower_fn_args_to_names(&sig.decl); + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::TraitItemKind::Method( + this.lower_method_sig(sig, fn_def_id, false), + hir::TraitMethod::Required(names))) + } + TraitItemKind::Method(ref sig, Some(ref body)) => { + let body_id = this.lower_body(Some(&sig.decl), |this| { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + }); + + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::TraitItemKind::Method( + this.lower_method_sig(sig, fn_def_id, false), + hir::TraitMethod::Provided(body_id))) + } + TraitItemKind::Type(ref bounds, ref default) => { + ( + this.lower_generics(&i.generics), + hir::TraitItemKind::Type( + this.lower_bounds(bounds, ImplTraitContext::Disallowed), + default.as_ref().map(|x| { + this.lower_ty(x, ImplTraitContext::Disallowed) + })) + ) + } + TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"), + }; hir::TraitItem { id: node_id, hir_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), - node: match i.node { - TraitItemKind::Const(ref ty, ref default) => { - hir::TraitItemKind::Const(this.lower_ty(ty), - default.as_ref().map(|x| { - this.lower_body(None, |this| this.lower_expr(x)) - })) - } - TraitItemKind::Method(ref sig, None) => { - let names = this.lower_fn_args_to_names(&sig.decl); - hir::TraitItemKind::Method(this.lower_method_sig(sig), - hir::TraitMethod::Required(names)) - } - TraitItemKind::Method(ref sig, Some(ref body)) => { - let body_id = this.lower_body(Some(&sig.decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) - }); - hir::TraitItemKind::Method(this.lower_method_sig(sig), - hir::TraitMethod::Provided(body_id)) - } - TraitItemKind::Type(ref bounds, ref default) => { - hir::TraitItemKind::Type(this.lower_bounds(bounds), - default.as_ref().map(|x| this.lower_ty(x))) - } - TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"), - }, + generics, + node, span: i.span, } }) @@ -1592,29 +2091,48 @@ impl<'a> LoweringContext<'a> { fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { self.with_parent_def(i.id, |this| { let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id); + let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id); + + let (generics, node) = match i.node { + ImplItemKind::Const(ref ty, ref expr) => { + let body_id = this.lower_body(None, |this| this.lower_expr(expr)); + ( + this.lower_generics(&i.generics), + hir::ImplItemKind::Const( + this.lower_ty(ty, ImplTraitContext::Disallowed), + body_id + ) + ) + } + ImplItemKind::Method(ref sig, ref body) => { + let body_id = this.lower_body(Some(&sig.decl), |this| { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + }); + let impl_trait_return_allow = !this.is_in_trait_impl; + + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::ImplItemKind::Method( + this.lower_method_sig(sig, fn_def_id, impl_trait_return_allow), + body_id)) + } + ImplItemKind::Type(ref ty) => ( + this.lower_generics(&i.generics), + hir::ImplItemKind::Type( + this.lower_ty(ty, ImplTraitContext::Disallowed)), + ), + ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"), + }; hir::ImplItem { id: node_id, hir_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), + generics, vis: this.lower_visibility(&i.vis, None), defaultness: this.lower_defaultness(i.defaultness, true /* [1] */), - node: match i.node { - ImplItemKind::Const(ref ty, ref expr) => { - let body_id = this.lower_body(None, |this| this.lower_expr(expr)); - hir::ImplItemKind::Const(this.lower_ty(ty), body_id) - } - ImplItemKind::Method(ref sig, ref body) => { - let body_id = this.lower_body(Some(&sig.decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) - }); - hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id) - } - ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(this.lower_ty(ty)), - ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"), - }, + node, span: i.span, } }) @@ -1632,8 +2150,10 @@ impl<'a> LoweringContext<'a> { kind: match i.node { ImplItemKind::Const(..) => hir::AssociatedItemKind::Const, ImplItemKind::Type(..) => hir::AssociatedItemKind::Type, - ImplItemKind::Method(ref sig, _) => hir::AssociatedItemKind::Method { - has_self: sig.decl.has_self(), + ImplItemKind::Method(ref sig, _) => { + hir::AssociatedItemKind::Method { + has_self: sig.decl.has_self(), + } }, ImplItemKind::Macro(..) => unimplemented!(), }, @@ -1651,11 +2171,10 @@ impl<'a> LoweringContext<'a> { fn lower_item_id(&mut self, i: &Item) -> SmallVector { match i.node { - ItemKind::Use(ref view_path) => { - if let ViewPathList(_, ref imports) = view_path.node { - return iter::once(i.id).chain(imports.iter().map(|import| import.node.id)) - .map(|id| hir::ItemId { id: id }).collect(); - } + ItemKind::Use(ref use_tree) => { + let mut vec = SmallVector::one(hir::ItemId { id: i.id }); + self.lower_item_id_use_tree(use_tree, &mut vec); + return vec; } ItemKind::MacroDef(..) => return SmallVector::new(), _ => {} @@ -1663,6 +2182,19 @@ impl<'a> LoweringContext<'a> { SmallVector::one(hir::ItemId { id: i.id }) } + fn lower_item_id_use_tree(&self, tree: &UseTree, vec: &mut SmallVector) { + match tree.kind { + UseTreeKind::Nested(ref nested_vec) => { + for &(ref nested, id) in nested_vec { + vec.push(hir::ItemId { id, }); + self.lower_item_id_use_tree(nested, vec); + } + } + UseTreeKind::Glob => {} + UseTreeKind::Simple(..) => {} + } + } + pub fn lower_item(&mut self, i: &Item) -> Option { let mut name = i.ident.name; let mut vis = self.lower_visibility(&i.vis, None); @@ -1702,18 +2234,32 @@ impl<'a> LoweringContext<'a> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem { self.with_parent_def(i.id, |this| { + let node_id = this.lower_node_id(i.id).node_id; + let def_id = this.resolver.definitions().local_def_id(node_id); hir::ForeignItem { - id: this.lower_node_id(i.id).node_id, + id: node_id, name: i.ident.name, attrs: this.lower_attrs(&i.attrs), node: match i.node { ForeignItemKind::Fn(ref fdec, ref generics) => { - hir::ForeignItemFn(this.lower_fn_decl(fdec), - this.lower_fn_args_to_names(fdec), - this.lower_generics(generics)) + // Disallow impl Trait in foreign items + let (generics, (fn_dec, fn_args)) = + this.add_in_band_lifetime_defs( + generics, + Some(def_id), + |this| ( + this.lower_fn_decl(fdec, None, false), + this.lower_fn_args_to_names(fdec) + ) + ); + + hir::ForeignItemFn(fn_dec, fn_args, generics) } ForeignItemKind::Static(ref t, m) => { - hir::ForeignItemStatic(this.lower_ty(t), m) + hir::ForeignItemStatic(this.lower_ty(t, ImplTraitContext::Disallowed), m) + } + ForeignItemKind::Ty => { + hir::ForeignItemType } }, vis: this.lower_visibility(&i.vis, None), @@ -1722,13 +2268,23 @@ impl<'a> LoweringContext<'a> { }) } - fn lower_method_sig(&mut self, sig: &MethodSig) -> hir::MethodSig { + fn lower_method_sig(&mut self, + sig: &MethodSig, + fn_def_id: Option, + impl_trait_return_allow: bool) + -> hir::MethodSig { hir::MethodSig { - generics: self.lower_generics(&sig.generics), abi: sig.abi, unsafety: self.lower_unsafety(sig.unsafety), constness: self.lower_constness(sig.constness), - decl: self.lower_fn_decl(&sig.decl), + decl: self.lower_fn_decl(&sig.decl, fn_def_id, impl_trait_return_allow), + } + } + + fn lower_is_auto(&mut self, a: IsAuto) -> hir::IsAuto { + match a { + IsAuto::Yes => hir::IsAuto::Yes, + IsAuto::No => hir::IsAuto::No, } } @@ -1814,16 +2370,19 @@ impl<'a> LoweringContext<'a> { } PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))), PatKind::TupleStruct(ref path, ref pats, ddpos) => { - let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional); + let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional, + ImplTraitContext::Disallowed); hir::PatKind::TupleStruct(qpath, pats.iter().map(|x| self.lower_pat(x)).collect(), ddpos) } PatKind::Path(ref qself, ref path) => { - hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional)) + hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional, + ImplTraitContext::Disallowed)) } PatKind::Struct(ref path, ref fields, etc) => { - let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional); + let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional, + ImplTraitContext::Disallowed); let fs = fields.iter() .map(|f| { @@ -2000,7 +2559,8 @@ impl<'a> LoweringContext<'a> { } ExprKind::MethodCall(ref seg, ref args) => { let hir_seg = self.lower_path_segment(e.span, seg, ParamMode::Optional, 0, - ParenthesizedGenericArgs::Err); + ParenthesizedGenericArgs::Err, + ImplTraitContext::Disallowed); let args = args.iter().map(|x| self.lower_expr(x)).collect(); hir::ExprMethodCall(hir_seg, seg.span, args) } @@ -2018,11 +2578,11 @@ impl<'a> LoweringContext<'a> { ExprKind::Lit(ref l) => hir::ExprLit(P((**l).clone())), ExprKind::Cast(ref expr, ref ty) => { let expr = P(self.lower_expr(expr)); - hir::ExprCast(expr, self.lower_ty(ty)) + hir::ExprCast(expr, self.lower_ty(ty, ImplTraitContext::Disallowed)) } ExprKind::Type(ref expr, ref ty) => { let expr = P(self.lower_expr(expr)); - hir::ExprType(expr, self.lower_ty(ty)) + hir::ExprType(expr, self.lower_ty(ty, ImplTraitContext::Disallowed)) } ExprKind::AddrOf(m, ref ohs) => { let m = self.lower_mutability(m); @@ -2099,7 +2659,7 @@ impl<'a> LoweringContext<'a> { this.sess.abort_if_errors(); } hir::ExprClosure(this.lower_capture_clause(capture_clause), - this.lower_fn_decl(decl), + this.lower_fn_decl(decl, None, false), body_id, fn_decl_span, is_generator) @@ -2173,7 +2733,8 @@ impl<'a> LoweringContext<'a> { }; } ExprKind::Path(ref qself, ref path) => { - hir::ExprPath(self.lower_qpath(e.id, qself, path, ParamMode::Optional)) + hir::ExprPath(self.lower_qpath(e.id, qself, path, ParamMode::Optional, + ImplTraitContext::Disallowed)) } ExprKind::Break(opt_ident, ref opt_expr) => { let label_result = if self.is_in_loop_condition && opt_ident.is_none() { @@ -2226,7 +2787,8 @@ impl<'a> LoweringContext<'a> { hir::ExprInlineAsm(P(hir_asm), outputs, inputs) } ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - hir::ExprStruct(self.lower_qpath(e.id, &None, path, ParamMode::Optional), + hir::ExprStruct(self.lower_qpath(e.id, &None, path, ParamMode::Optional, + ImplTraitContext::Disallowed), fields.iter().map(|x| self.lower_field(x)).collect(), maybe_expr.as_ref().map(|x| P(self.lower_expr(x)))) } @@ -2542,7 +3104,7 @@ impl<'a> LoweringContext<'a> { }; // Err(err) => #[allow(unreachable_code)] - // return Carrier::from_error(From::from(err)), + // return Try::from_error(From::from(err)), let err_arm = { let err_ident = self.str_to_ident("err"); let err_local = self.pat_ident(e.span, err_ident); @@ -2661,7 +3223,7 @@ impl<'a> LoweringContext<'a> { -> hir::Visibility { match *v { Visibility::Public => hir::Public, - Visibility::Crate(_) => hir::Visibility::Crate, + Visibility::Crate(..) => hir::Visibility::Crate, Visibility::Restricted { ref path, id } => { hir::Visibility::Restricted { path: P(self.lower_path(id, path, ParamMode::Explicit, true)), diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index 80fadcda2775d..02a1e33eeb918 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -12,6 +12,8 @@ use super::*; use dep_graph::{DepGraph, DepKind, DepNodeIndex}; use hir::intravisit::{Visitor, NestedVisitorMap}; +use middle::cstore::CrateStore; +use session::CrateDisambiguator; use std::iter::repeat; use syntax::ast::{NodeId, CRATE_NODE_ID}; use syntax_pos::Span; @@ -70,7 +72,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { impl_items: _, bodies: _, trait_impls: _, - trait_default_impl: _, + trait_auto_impl: _, body_ids: _, } = *krate; @@ -118,7 +120,9 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { } pub(super) fn finalize_and_compute_crate_hash(self, - crate_disambiguator: &str) + crate_disambiguator: CrateDisambiguator, + cstore: &CrateStore, + commandline_args_hash: u64) -> Vec> { let mut node_hashes: Vec<_> = self .hir_body_nodes @@ -131,9 +135,23 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { node_hashes.sort_unstable_by(|&(ref d1, _), &(ref d2, _)| d1.cmp(d2)); + let mut upstream_crates: Vec<_> = cstore.crates_untracked().iter().map(|&cnum| { + let name = cstore.crate_name_untracked(cnum).as_str(); + let disambiguator = cstore.crate_disambiguator_untracked(cnum) + .to_fingerprint(); + let hash = cstore.crate_hash_untracked(cnum); + (name, disambiguator, hash) + }).collect(); + + upstream_crates.sort_unstable_by(|&(name1, dis1, _), &(name2, dis2, _)| { + (name1, dis1).cmp(&(name2, dis2)) + }); + self.dep_graph.with_task(DepNode::new_no_params(DepKind::Krate), &self.hcx, - (node_hashes, crate_disambiguator), + ((node_hashes, upstream_crates), + (commandline_args_hash, + crate_disambiguator.to_fingerprint())), identity_fn); self.map } @@ -218,7 +236,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { f: F) { let prev_owner = self.current_dep_node_owner; let prev_signature_dep_index = self.current_signature_dep_index; - let prev_full_dep_index = self.current_signature_dep_index; + let prev_full_dep_index = self.current_full_dep_index; let prev_in_body = self.currently_in_body; let def_path_hash = self.definitions.def_path_hash(dep_node_owner); diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 673e6d3bbfbae..17a4c66edb9c9 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -10,12 +10,14 @@ use hir::map::definitions::*; use hir::def_id::{CRATE_DEF_INDEX, DefIndex, DefIndexAddressSpace}; +use session::CrateDisambiguator; use syntax::ast::*; use syntax::ext::hygiene::Mark; use syntax::visit; use syntax::symbol::keywords; use syntax::symbol::Symbol; +use syntax::parse::token::{self, Token}; use hir::map::{ITEM_LIKE_SPACE, REGULAR_SPACE}; @@ -43,7 +45,9 @@ impl<'a> DefCollector<'a> { } } - pub fn collect_root(&mut self, crate_name: &str, crate_disambiguator: &str) { + pub fn collect_root(&mut self, + crate_name: &str, + crate_disambiguator: CrateDisambiguator) { let root = self.definitions.create_root_def(crate_name, crate_disambiguator); assert_eq!(root, CRATE_DEF_INDEX); @@ -100,7 +104,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { // Pick the def data. This need not be unique, but the more // information we encapsulate into let def_data = match i.node { - ItemKind::DefaultImpl(..) | ItemKind::Impl(..) => + ItemKind::AutoImpl(..) | ItemKind::Impl(..) => DefPathData::Impl, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) | ItemKind::Trait(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) | ItemKind::Ty(..) => @@ -114,21 +118,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()), ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false), ItemKind::GlobalAsm(..) => DefPathData::Misc, - ItemKind::Use(ref view_path) => { - match view_path.node { - ViewPathGlob(..) => {} - - // FIXME(eddyb) Should use the real name. Which namespace? - ViewPathSimple(..) => {} - ViewPathList(_, ref imports) => { - for import in imports { - self.create_def(import.node.id, - DefPathData::Misc, - ITEM_LIKE_SPACE); - } - } - } - DefPathData::Misc + ItemKind::Use(..) => { + return visit::walk_item(self, i); } }; let def = self.create_def(i.id, def_data, ITEM_LIKE_SPACE); @@ -176,6 +167,11 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { }); } + fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { + self.create_def(id, DefPathData::Misc, ITEM_LIKE_SPACE); + visit::walk_use_tree(self, use_tree, id); + } + fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) { let def = self.create_def(foreign_item.id, DefPathData::ValueNs(foreign_item.ident.name.as_str()), @@ -283,4 +279,17 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { _ => visit::walk_stmt(self, stmt), } } + + fn visit_token(&mut self, t: Token) { + if let Token::Interpolated(nt) = t { + match nt.0 { + token::NtExpr(ref expr) => { + if let ExprKind::Mac(..) = expr.node { + self.visit_macro_invoc(expr.id, false); + } + } + _ => {} + } + } + } } diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index bd80b613e77f5..7c2f0bc3cef84 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -19,15 +19,15 @@ use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, DefIndexAddressSpace, CRATE_DEF_INDEX}; use ich::Fingerprint; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::stable_hasher::StableHasher; use serialize::{Encodable, Decodable, Encoder, Decoder}; +use session::CrateDisambiguator; use std::fmt::Write; use std::hash::Hash; use syntax::ast; use syntax::ext::hygiene::Mark; use syntax::symbol::{Symbol, InternedString}; -use ty::TyCtxt; use util::nodemap::NodeMap; /// The DefPathTable maps DefIndexes to DefKeys and vice versa. @@ -232,7 +232,9 @@ impl DefKey { DefPathHash(hasher.finish()) } - fn root_parent_stable_hash(crate_name: &str, crate_disambiguator: &str) -> DefPathHash { + fn root_parent_stable_hash(crate_name: &str, + crate_disambiguator: CrateDisambiguator) + -> DefPathHash { let mut hasher = StableHasher::new(); // Disambiguate this from a regular DefPath hash, // see compute_stable_hash() above. @@ -296,15 +298,12 @@ impl DefPath { DefPath { data: data, krate: krate } } - pub fn to_string(&self, tcx: TyCtxt) -> String { + /// Returns a string representation of the DefPath without + /// the crate-prefix. This method is useful if you don't have + /// a TyCtxt available. + pub fn to_string_no_crate(&self) -> String { let mut s = String::with_capacity(self.data.len() * 16); - s.push_str(&tcx.original_crate_name(self.krate).as_str()); - s.push_str("/"); - // Don't print the whole crate disambiguator. That's just annoying in - // debug output. - s.push_str(&tcx.crate_disambiguator(self.krate).as_str()[..7]); - for component in &self.data { write!(s, "::{}[{}]", @@ -316,20 +315,26 @@ impl DefPath { s } - /// Returns a string representation of the DefPath without + /// Return filename friendly string of the DefPah without /// the crate-prefix. This method is useful if you don't have /// a TyCtxt available. - pub fn to_string_no_crate(&self) -> String { + pub fn to_filename_friendly_no_crate(&self) -> String { let mut s = String::with_capacity(self.data.len() * 16); + let mut opt_delimiter = None; for component in &self.data { - write!(s, - "::{}[{}]", - component.data.as_interned_str(), - component.disambiguator) - .unwrap(); + opt_delimiter.map(|d| s.push(d)); + opt_delimiter = Some('-'); + if component.disambiguator == 0 { + write!(s, "{}", component.data.as_interned_str()).unwrap(); + } else { + write!(s, + "{}[{}]", + component.data.as_interned_str(), + component.disambiguator) + .unwrap(); + } } - s } } @@ -488,7 +493,7 @@ impl Definitions { /// Add a definition with a parent definition. pub fn create_root_def(&mut self, crate_name: &str, - crate_disambiguator: &str) + crate_disambiguator: CrateDisambiguator) -> DefIndex { let key = DefKey { parent: None, @@ -570,7 +575,8 @@ impl Definitions { self.node_to_def_index.insert(node_id, index); } - if expansion.is_modern() { + let expansion = expansion.modern(); + if expansion != Mark::root() { self.expansions.insert(index, expansion); } diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 8ce2feab06cef..28527b6f0bc64 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -17,7 +17,7 @@ pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, use dep_graph::{DepGraph, DepNode, DepKind, DepNodeIndex}; -use hir::def_id::{CRATE_DEF_INDEX, DefId, DefIndexAddressSpace}; +use hir::def_id::{CRATE_DEF_INDEX, DefId, LocalDefId, DefIndexAddressSpace}; use syntax::abi::Abi; use syntax::ast::{self, Name, NodeId, CRATE_NODE_ID}; @@ -359,6 +359,16 @@ impl<'hir> Map<'hir> { self.definitions.as_local_node_id(DefId::local(def_index)).unwrap() } + #[inline] + pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { + self.definitions.def_index_to_hir_id(def_id.to_def_id().index) + } + + #[inline] + pub fn local_def_id_to_node_id(&self, def_id: LocalDefId) -> NodeId { + self.definitions.as_local_node_id(def_id.to_def_id()).unwrap() + } + fn entry_count(&self) -> usize { self.map.len() } @@ -416,6 +426,12 @@ impl<'hir> Map<'hir> { /// if the node is a body owner, otherwise returns `None`. pub fn maybe_body_owned_by(&self, id: NodeId) -> Option { if let Some(entry) = self.find_entry(id) { + if self.dep_graph.is_fully_enabled() { + let hir_id_owner = self.node_to_hir_id(id).owner; + let def_path_hash = self.definitions.def_path_hash(hir_id_owner); + self.dep_graph.read(def_path_hash.to_dep_node(DepKind::HirBody)); + } + if let Some(body_id) = entry.associated_body() { // For item-like things and closures, the associated // body has its own distinct id, and that is returned @@ -442,6 +458,28 @@ impl<'hir> Map<'hir> { }) } + pub fn body_owner_kind(&self, id: NodeId) -> BodyOwnerKind { + // Handle constants in enum discriminants, types, and repeat expressions. + let def_id = self.local_def_id(id); + let def_key = self.def_key(def_id); + if def_key.disambiguated_data.data == DefPathData::Initializer { + return BodyOwnerKind::Const; + } + + match self.get(id) { + NodeItem(&Item { node: ItemConst(..), .. }) | + NodeTraitItem(&TraitItem { node: TraitItemKind::Const(..), .. }) | + NodeImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) => { + BodyOwnerKind::Const + } + NodeItem(&Item { node: ItemStatic(_, m, _), .. }) => { + BodyOwnerKind::Static(m) + } + // Default to function if it's not a constant or static. + _ => BodyOwnerKind::Fn + } + } + pub fn ty_param_owner(&self, id: NodeId) -> NodeId { match self.get(id) { NodeItem(&Item { node: ItemTrait(..), .. }) => id, @@ -474,16 +512,16 @@ impl<'hir> Map<'hir> { self.forest.krate.trait_impls.get(&trait_did).map_or(&[], |xs| &xs[..]) } - pub fn trait_default_impl(&self, trait_did: DefId) -> Option { + pub fn trait_auto_impl(&self, trait_did: DefId) -> Option { self.dep_graph.read(DepNode::new_no_params(DepKind::AllLocalTraitImpls)); // NB: intentionally bypass `self.forest.krate()` so that we // do not trigger a read of the whole krate here - self.forest.krate.trait_default_impl.get(&trait_did).cloned() + self.forest.krate.trait_auto_impl.get(&trait_did).cloned() } pub fn trait_is_auto(&self, trait_did: DefId) -> bool { - self.trait_default_impl(trait_did).is_some() + self.trait_auto_impl(trait_did).is_some() } /// Get the attributes on the krate. This is preferable to @@ -530,6 +568,12 @@ impl<'hir> Map<'hir> { /// from a node to the root of the ast (unless you get the same id back here /// that can happen if the id is not in the map itself or is just weird). pub fn get_parent_node(&self, id: NodeId) -> NodeId { + if self.dep_graph.is_fully_enabled() { + let hir_id_owner = self.node_to_hir_id(id).owner; + let def_path_hash = self.definitions.def_path_hash(hir_id_owner); + self.dep_graph.read(def_path_hash.to_dep_node(DepKind::HirBody)); + } + self.find_entry(id).and_then(|x| x.parent_node()).unwrap_or(id) } @@ -1014,8 +1058,11 @@ pub fn map_crate<'hir>(sess: &::session::Session, hcx); intravisit::walk_crate(&mut collector, &forest.krate); - let crate_disambiguator = sess.local_crate_disambiguator().as_str(); - collector.finalize_and_compute_crate_hash(&crate_disambiguator) + let crate_disambiguator = sess.local_crate_disambiguator(); + let cmdline_args = sess.opts.dep_tracking_hash(); + collector.finalize_and_compute_crate_hash(crate_disambiguator, + cstore, + cmdline_args) }; if log_enabled!(::log::LogLevel::Debug) { @@ -1140,7 +1187,7 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { ItemUnion(..) => "union", ItemTrait(..) => "trait", ItemImpl(..) => "impl", - ItemDefaultImpl(..) => "default impl", + ItemAutoImpl(..) => "default impl", }; format!("{} {}{}", item_str, path_str(), id_str) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index bff71155440a3..39ec33eef1fec 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -45,6 +45,7 @@ use ty::AdtKind; use rustc_data_structures::indexed_vec; +use serialize::{self, Encoder, Encodable, Decoder, Decodable}; use std::collections::BTreeMap; use std::fmt; @@ -85,13 +86,37 @@ pub mod svh; /// the local_id part of the HirId changing, which is a very useful property in /// incremental compilation where we have to persist things through changes to /// the code base. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, - RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct HirId { pub owner: DefIndex, pub local_id: ItemLocalId, } +impl serialize::UseSpecializedEncodable for HirId { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + let HirId { + owner, + local_id, + } = *self; + + owner.encode(s)?; + local_id.encode(s) + } +} + +impl serialize::UseSpecializedDecodable for HirId { + fn default_decode(d: &mut D) -> Result { + let owner = DefIndex::decode(d)?; + let local_id = ItemLocalId::decode(d)?; + + Ok(HirId { + owner, + local_id + }) + } +} + + /// An `ItemLocalId` uniquely identifies something within a given "item-like", /// that is within a hir::Item, hir::TraitItem, or hir::ImplItem. There is no /// guarantee that the numerical value of a given `ItemLocalId` corresponds to @@ -197,6 +222,10 @@ pub struct LifetimeDef { pub lifetime: Lifetime, pub bounds: HirVec, pub pure_wrt_drop: bool, + // Indicates that the lifetime definition was synthetically added + // as a result of an in-band lifetime usage like + // `fn foo(x: &'a u8) -> &'a u8 { x }` + pub in_band: bool, } /// A "Path" is essentially Rust's notion of a name; for instance: @@ -351,6 +380,7 @@ pub struct TyParam { pub default: Option>, pub span: Span, pub pure_wrt_drop: bool, + pub synthetic: Option, } /// Represents lifetimes and type parameters attached to a declaration @@ -419,6 +449,13 @@ impl Generics { } } +/// Synthetic Type Parameters are converted to an other form during lowering, this allows +/// to track the original form they had. Usefull for error messages. +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum SyntheticTyParamKind { + ImplTrait +} + /// A `where` clause in a definition #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct WhereClause { @@ -491,7 +528,7 @@ pub struct Crate { pub impl_items: BTreeMap, pub bodies: BTreeMap, pub trait_impls: BTreeMap>, - pub trait_default_impl: BTreeMap, + pub trait_auto_impl: BTreeMap, /// A list of the body ids written out in the order in which they /// appear in the crate. If you're going to process all the bodies @@ -908,8 +945,6 @@ impl Stmt_ { } } -// FIXME (pending discussion of #1697, #2178...): local should really be -// a refinement on pat. /// Local represents a `let` statement, e.g., `let : = ;` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct Local { @@ -1022,6 +1057,18 @@ impl Body { } } +#[derive(Copy, Clone, Debug)] +pub enum BodyOwnerKind { + /// Functions and methods. + Fn, + + /// Constants and associated constants. + Const, + + /// Initializer of a `static` item. + Static(Mutability), +} + /// An expression #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub struct Expr { @@ -1048,7 +1095,9 @@ pub enum Expr_ { /// A function call /// /// The first field resolves to the function itself (usually an `ExprPath`), - /// and the second field is the list of arguments + /// and the second field is the list of arguments. + /// This also represents calling the constructor of + /// tuple-like ADTs such as tuple structs and enum variants. ExprCall(P, HirVec), /// A method call (`x.foo::<'static, Bar, Baz>(a, b, c, d)`) /// @@ -1289,7 +1338,6 @@ pub struct MethodSig { pub constness: Constness, pub abi: Abi, pub decl: P, - pub generics: Generics, } // The bodies for items are stored "out of line", in a separate @@ -1310,6 +1358,7 @@ pub struct TraitItem { pub name: Name, pub hir_id: HirId, pub attrs: HirVec, + pub generics: Generics, pub node: TraitItemKind, pub span: Span, } @@ -1354,6 +1403,7 @@ pub struct ImplItem { pub vis: Visibility, pub defaultness: Defaultness, pub attrs: HirVec, + pub generics: Generics, pub node: ImplItemKind, pub span: Span, } @@ -1412,6 +1462,13 @@ pub struct BareFnTy { pub abi: Abi, pub lifetimes: HirVec, pub decl: P, + pub arg_names: HirVec>, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct ExistTy { + pub generics: Generics, + pub bounds: TyParamBounds, } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] @@ -1439,9 +1496,21 @@ pub enum Ty_ { /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TyTraitObject(HirVec, Lifetime), - /// An `impl Bound1 + Bound2 + Bound3` type - /// where `Bound` is a trait or a lifetime. - TyImplTrait(TyParamBounds), + /// An exsitentially quantified (there exists a type satisfying) `impl + /// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime. + /// + /// The `ExistTy` structure emulates an + /// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`. + /// + /// The `HirVec` is the list of lifetimes applied as parameters + /// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`. + /// This list is only a list of lifetimes and not type parameters + /// because all in-scope type parameters are captured by `impl Trait`, + /// so they are resolved directly through the parent `Generics`. + TyImplTraitExistential(ExistTy, HirVec), + /// An universally quantified (for all types satisfying) `impl + /// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime. + TyImplTraitUniversal(DefId, TyParamBounds), /// Unused for now TyTypeof(BodyId), /// TyInfer means the type should be inferred instead of it having been @@ -1490,6 +1559,13 @@ pub struct FnDecl { pub has_implicit_self: bool, } +/// Is the trait definition an auto trait? +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum IsAuto { + Yes, + No +} + #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Unsafety { Unsafe, @@ -1801,12 +1877,12 @@ pub enum Item_ { /// A union definition, e.g. `union Foo {x: A, y: B}` ItemUnion(VariantData, Generics), /// Represents a Trait Declaration - ItemTrait(Unsafety, Generics, TyParamBounds, HirVec), + ItemTrait(IsAuto, Unsafety, Generics, TyParamBounds, HirVec), - // Default trait implementations + /// Auto trait implementations /// /// `impl Trait for .. {}` - ItemDefaultImpl(Unsafety, TraitRef), + ItemAutoImpl(Unsafety, TraitRef), /// An implementation, eg `impl Trait for Foo { .. }` ItemImpl(Unsafety, ImplPolarity, @@ -1834,7 +1910,7 @@ impl Item_ { ItemUnion(..) => "union", ItemTrait(..) => "trait", ItemImpl(..) | - ItemDefaultImpl(..) => "item", + ItemAutoImpl(..) => "item", } } @@ -1846,6 +1922,19 @@ impl Item_ { _ => None, } } + + pub fn generics(&self) -> Option<&Generics> { + Some(match *self { + ItemFn(_, _, _, _, ref generics, _) | + ItemTy(_, ref generics) | + ItemEnum(_, ref generics) | + ItemStruct(_, ref generics) | + ItemUnion(_, ref generics) | + ItemTrait(_, _, ref generics, _, _) | + ItemImpl(_, _, _, ref generics, _, _, _)=> generics, + _ => return None + }) + } } /// A reference from an trait to one of its associated items. This @@ -1904,6 +1993,8 @@ pub enum ForeignItem_ { /// A foreign static item (`static ext: u8`), with optional mutability /// (the boolean is true when mutable) ForeignItemStatic(P, bool), + /// A foreign type + ForeignItemType, } impl ForeignItem_ { @@ -1911,12 +2002,13 @@ impl ForeignItem_ { match *self { ForeignItemFn(..) => "foreign function", ForeignItemStatic(..) => "foreign static item", + ForeignItemType => "foreign type", } } } /// A free variable referred to in a function. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] +#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)] pub struct Freevar { /// The variable being accessed free. pub def: Def, diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 144cb34ee356e..2bec224362ea6 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs @@ -160,11 +160,13 @@ impl hir::Pat { variants } - /// Checks if the pattern contains any `ref` or `ref mut` bindings, - /// and if yes whether it contains mutable or just immutables ones. + /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if + /// yes whether it contains mutable or just immutables ones. /// - /// FIXME(tschottdorf): this is problematic as the HIR is being scraped, - /// but ref bindings may be implicit after #42640. + /// FIXME(tschottdorf): this is problematic as the HIR is being scraped, but + /// ref bindings are be implicit after #42640 (default match binding modes). + /// + /// See #44848. pub fn contains_explicit_ref_binding(&self) -> Option { let mut result = None; self.each_binding(|annotation, _, _, _| { @@ -188,7 +190,9 @@ impl hir::Arm { /// bindings, and if yes whether its containing mutable ones or just immutables ones. pub fn contains_explicit_ref_binding(&self) -> Option { // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed - // for #42640. + // for #42640 (default match binding modes). + // + // See #44848. self.pats.iter() .filter_map(|pat| pat.contains_explicit_ref_binding()) .max_by_key(|m| match *m { diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 5daffe667fde5..d94dd24af3edc 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -399,7 +399,8 @@ impl<'a> State<'a> { }, span: syntax_pos::DUMMY_SP, }; - self.print_ty_fn(f.abi, f.unsafety, &f.decl, None, &generics)?; + self.print_ty_fn(f.abi, f.unsafety, &f.decl, None, &generics, + &f.arg_names[..])?; } hir::TyPath(ref qpath) => { self.print_qpath(qpath, false)? @@ -420,8 +421,11 @@ impl<'a> State<'a> { self.print_lifetime(lifetime)?; } } - hir::TyImplTrait(ref bounds) => { - self.print_bounds("impl ", &bounds[..])?; + hir::TyImplTraitExistential(ref existty, ref _lifetimes) => { + self.print_bounds("impl", &existty.bounds[..])?; + } + hir::TyImplTraitUniversal(_, ref bounds) => { + self.print_bounds("impl", &bounds[..])?; } hir::TyArray(ref ty, v) => { self.s.word("[")?; @@ -477,6 +481,13 @@ impl<'a> State<'a> { self.end()?; // end the head-ibox self.end() // end the outer cbox } + hir::ForeignItemType => { + self.head(&visibility_qualified(&item.vis, "type"))?; + self.print_name(item.name)?; + self.s.word(";")?; + self.end()?; // end the head-ibox + self.end() // end the outer cbox + } } } @@ -652,7 +663,7 @@ impl<'a> State<'a> { self.head(&visibility_qualified(&item.vis, "union"))?; self.print_struct(struct_def, generics, item.name, item.span, true)?; } - hir::ItemDefaultImpl(unsafety, ref trait_ref) => { + hir::ItemAutoImpl(unsafety, ref trait_ref) => { self.head("")?; self.print_visibility(&item.vis)?; self.print_unsafety(unsafety)?; @@ -709,9 +720,10 @@ impl<'a> State<'a> { } self.bclose(item.span)?; } - hir::ItemTrait(unsafety, ref generics, ref bounds, ref trait_items) => { + hir::ItemTrait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => { self.head("")?; self.print_visibility(&item.vis)?; + self.print_is_auto(is_auto)?; self.print_unsafety(unsafety)?; self.word_nbsp("trait")?; self.print_name(item.name)?; @@ -879,6 +891,7 @@ impl<'a> State<'a> { pub fn print_method_sig(&mut self, name: ast::Name, m: &hir::MethodSig, + generics: &hir::Generics, vis: &hir::Visibility, arg_names: &[Spanned], body_id: Option) @@ -888,7 +901,7 @@ impl<'a> State<'a> { m.constness, m.abi, Some(name), - &m.generics, + generics, vis, arg_names, body_id) @@ -904,12 +917,14 @@ impl<'a> State<'a> { self.print_associated_const(ti.name, &ty, default, &hir::Inherited)?; } hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(ref arg_names)) => { - self.print_method_sig(ti.name, sig, &hir::Inherited, arg_names, None)?; + self.print_method_sig(ti.name, sig, &ti.generics, &hir::Inherited, arg_names, + None)?; self.s.word(";")?; } hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => { self.head("")?; - self.print_method_sig(ti.name, sig, &hir::Inherited, &[], Some(body))?; + self.print_method_sig(ti.name, sig, &ti.generics, &hir::Inherited, &[], + Some(body))?; self.nbsp()?; self.end()?; // need to close a box self.end()?; // need to close a box @@ -937,7 +952,7 @@ impl<'a> State<'a> { } hir::ImplItemKind::Method(ref sig, body) => { self.head("")?; - self.print_method_sig(ii.name, sig, &ii.vis, &[], Some(body))?; + self.print_method_sig(ii.name, sig, &ii.generics, &ii.vis, &[], Some(body))?; self.nbsp()?; self.end()?; // need to close a box self.end()?; // need to close a box @@ -1242,6 +1257,15 @@ impl<'a> State<'a> { Fixity::None => (prec + 1, prec + 1), }; + let left_prec = match (&lhs.node, op.node) { + // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is + // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead + // of `(x as i32) < ...`. We need to convince it _not_ to do that. + (&hir::ExprCast { .. }, hir::BinOp_::BiLt) | + (&hir::ExprCast { .. }, hir::BinOp_::BiShl) => parser::PREC_FORCE_PAREN, + _ => left_prec, + }; + self.print_expr_maybe_paren(lhs, left_prec)?; self.s.space()?; self.word_space(op.node.as_str())?; @@ -2140,7 +2164,8 @@ impl<'a> State<'a> { unsafety: hir::Unsafety, decl: &hir::FnDecl, name: Option, - generics: &hir::Generics) + generics: &hir::Generics, + arg_names: &[Spanned]) -> io::Result<()> { self.ibox(indent_unit)?; if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { @@ -2163,7 +2188,7 @@ impl<'a> State<'a> { name, &generics, &hir::Inherited, - &[], + arg_names, None)?; self.end() } @@ -2262,6 +2287,13 @@ impl<'a> State<'a> { hir::Unsafety::Unsafe => self.word_nbsp("unsafe"), } } + + pub fn print_is_auto(&mut self, s: hir::IsAuto) -> io::Result<()> { + match s { + hir::IsAuto::Yes => self.word_nbsp("auto"), + hir::IsAuto::No => Ok(()), + } + } } // Dup'ed from parse::classify, but adapted for the HIR. diff --git a/src/librustc/ich/fingerprint.rs b/src/librustc/ich/fingerprint.rs index 2391b61253aa9..3d089d8e75fb1 100644 --- a/src/librustc/ich/fingerprint.rs +++ b/src/librustc/ich/fingerprint.rs @@ -9,8 +9,6 @@ // except according to those terms. use rustc_data_structures::stable_hasher; -use std::mem; -use std::slice; #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] pub struct Fingerprint(u64, u64); @@ -31,6 +29,11 @@ impl Fingerprint { self.0 } + #[inline] + pub fn as_value(&self) -> (u64, u64) { + (self.0, self.1) + } + #[inline] pub fn combine(self, other: Fingerprint) -> Fingerprint { // See https://stackoverflow.com/a/27952689 on why this function is @@ -54,16 +57,9 @@ impl ::std::fmt::Display for Fingerprint { } impl stable_hasher::StableHasherResult for Fingerprint { - fn finish(mut hasher: stable_hasher::StableHasher) -> Self { - let hash_bytes: &[u8] = hasher.finalize(); - - assert!(hash_bytes.len() >= mem::size_of::() * 2); - let hash_bytes: &[u64] = unsafe { - slice::from_raw_parts(hash_bytes.as_ptr() as *const u64, 2) - }; - - // The bytes returned bytes the Blake2B hasher are always little-endian. - Fingerprint(u64::from_le(hash_bytes[0]), u64::from_le(hash_bytes[1])) + fn finish(hasher: stable_hasher::StableHasher) -> Self { + let (_0, _1) = hasher.finalize(); + Fingerprint(_0, _1) } } diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index e7a26e14db5bb..d95b825b9e562 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -28,7 +28,7 @@ use syntax::attr; use syntax::codemap::CodeMap; use syntax::ext::hygiene::SyntaxContext; use syntax::symbol::Symbol; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; use rustc_data_structures::stable_hasher::{HashStable, StableHashingContextProvider, StableHasher, StableHasherResult, @@ -206,9 +206,10 @@ impl<'gcx> StableHashingContext<'gcx> { pub fn hash_hir_item_like(&mut self, item_attrs: &[ast::Attribute], + is_const: bool, f: F) { let prev_overflow_checks = self.overflow_checks_enabled; - if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") { + if is_const || attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") { self.overflow_checks_enabled = true; } let prev_hash_node_ids = self.node_id_hashing_mode; @@ -226,6 +227,8 @@ impl<'gcx> StableHashingContext<'gcx> { match binop { hir::BiAdd | hir::BiSub | + hir::BiShl | + hir::BiShr | hir::BiMul => self.overflow_checks_enabled, hir::BiDiv | @@ -236,8 +239,6 @@ impl<'gcx> StableHashingContext<'gcx> { hir::BiBitXor | hir::BiBitAnd | hir::BiBitOr | - hir::BiShl | - hir::BiShr | hir::BiEq | hir::BiLt | hir::BiLe | @@ -361,63 +362,53 @@ impl<'gcx> HashStable> for Span { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { - use syntax_pos::Pos; + const TAG_VALID_SPAN: u8 = 0; + const TAG_INVALID_SPAN: u8 = 1; + const TAG_EXPANSION: u8 = 0; + const TAG_NO_EXPANSION: u8 = 1; if !hcx.hash_spans { return } + if *self == DUMMY_SP { + return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + } + // If this is not an empty or invalid span, we want to hash the last // position that belongs to it, as opposed to hashing the first // position past it. - let span_hi = if self.hi() > self.lo() { - // We might end up in the middle of a multibyte character here, - // but that's OK, since we are not trying to decode anything at - // this position. - self.hi() - ::syntax_pos::BytePos(1) - } else { - self.hi() - }; + let span = self.data(); + + if span.hi < span.lo { + return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + } - { - let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo()); - let loc1 = loc1.as_ref() - .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize())) - .unwrap_or(("???", 0, 0)); - - let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi); - let loc2 = loc2.as_ref() - .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize())) - .unwrap_or(("???", 0, 0)); - - if loc1.0 == loc2.0 { - std_hash::Hash::hash(&0u8, hasher); - - std_hash::Hash::hash(loc1.0, hasher); - std_hash::Hash::hash(&loc1.1, hasher); - std_hash::Hash::hash(&loc1.2, hasher); - - // Do not hash the file name twice - std_hash::Hash::hash(&loc2.1, hasher); - std_hash::Hash::hash(&loc2.2, hasher); - } else { - std_hash::Hash::hash(&1u8, hasher); - - std_hash::Hash::hash(loc1.0, hasher); - std_hash::Hash::hash(&loc1.1, hasher); - std_hash::Hash::hash(&loc1.2, hasher); - - std_hash::Hash::hash(loc2.0, hasher); - std_hash::Hash::hash(&loc2.1, hasher); - std_hash::Hash::hash(&loc2.2, hasher); + let (file_lo, line_lo, col_lo) = match hcx.codemap() + .byte_pos_to_line_and_col(span.lo) { + Some(pos) => pos, + None => { + return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher); } + }; + + if !file_lo.contains(span.hi) { + return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher); } - if self.ctxt() == SyntaxContext::empty() { - 0u8.hash_stable(hcx, hasher); + let len = span.hi - span.lo; + + std_hash::Hash::hash(&TAG_VALID_SPAN, hasher); + std_hash::Hash::hash(&file_lo.name, hasher); + std_hash::Hash::hash(&line_lo, hasher); + std_hash::Hash::hash(&col_lo, hasher); + std_hash::Hash::hash(&len, hasher); + + if span.ctxt == SyntaxContext::empty() { + TAG_NO_EXPANSION.hash_stable(hcx, hasher); } else { - 1u8.hash_stable(hcx, hasher); - self.source_callsite().hash_stable(hcx, hasher); + TAG_EXPANSION.hash_stable(hcx, hasher); + span.ctxt.outer().expn_info().hash_stable(hcx, hasher); } } } diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 96d5940caf6a4..77bf3da679dd7 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -13,7 +13,7 @@ use hir; use hir::map::DefPathHash; -use hir::def_id::{DefId, CrateNum, CRATE_DEF_INDEX}; +use hir::def_id::{DefId, LocalDefId, CrateNum, CRATE_DEF_INDEX}; use ich::{StableHashingContext, NodeIdHashingMode}; use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher, StableHasherResult}; @@ -38,6 +38,24 @@ impl<'gcx> ToStableHashKey> for DefId { } } +impl<'gcx> HashStable> for LocalDefId { + #[inline] + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + hcx.def_path_hash(self.to_def_id()).hash_stable(hcx, hasher); + } +} + +impl<'gcx> ToStableHashKey> for LocalDefId { + type KeyType = DefPathHash; + + #[inline] + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'gcx>) -> DefPathHash { + hcx.def_path_hash(self.to_def_id()) + } +} + impl<'gcx> HashStable> for CrateNum { #[inline] fn hash_stable(&self, @@ -139,7 +157,8 @@ impl_stable_hash_for!(struct hir::Lifetime { impl_stable_hash_for!(struct hir::LifetimeDef { lifetime, bounds, - pure_wrt_drop + pure_wrt_drop, + in_band }); impl_stable_hash_for!(struct hir::Path { @@ -177,7 +196,8 @@ impl_stable_hash_for!(struct hir::TyParam { bounds, default, span, - pure_wrt_drop + pure_wrt_drop, + synthetic }); impl_stable_hash_for!(struct hir::Generics { @@ -187,6 +207,10 @@ impl_stable_hash_for!(struct hir::Generics { span }); +impl_stable_hash_for!(enum hir::SyntheticTyParamKind { + ImplTrait +}); + impl_stable_hash_for!(struct hir::WhereClause { id, predicates @@ -227,8 +251,7 @@ impl_stable_hash_for!(struct hir::MethodSig { unsafety, constness, abi, - decl, - generics + decl }); impl_stable_hash_for!(struct hir::TypeBinding { @@ -269,7 +292,13 @@ impl_stable_hash_for!(struct hir::BareFnTy { unsafety, abi, lifetimes, - decl + decl, + arg_names +}); + +impl_stable_hash_for!(struct hir::ExistTy { + generics, + bounds }); impl_stable_hash_for!(enum hir::Ty_ { @@ -282,7 +311,8 @@ impl_stable_hash_for!(enum hir::Ty_ { TyTup(ts), TyPath(qpath), TyTraitObject(trait_refs, lifetime), - TyImplTrait(bounds), + TyImplTraitExistential(existty, lifetimes), + TyImplTraitUniversal(def_id, bounds), TyTypeof(body_id), TyErr, TyInfer @@ -351,33 +381,7 @@ impl<'gcx> HashStable> for hir::Block { targeted_by_break, } = *self; - let non_item_stmts = || stmts.iter().filter(|stmt| { - match stmt.node { - hir::StmtDecl(ref decl, _) => { - match decl.node { - // If this is a declaration of a nested item, we don't - // want to leave any trace of it in the hash value, not - // even that it exists. Otherwise changing the position - // of nested items would invalidate the containing item - // even though that does not constitute a semantic - // change. - hir::DeclItem(_) => false, - hir::DeclLocal(_) => true - } - } - hir::StmtExpr(..) | - hir::StmtSemi(..) => true - } - }); - - let count = non_item_stmts().count(); - - count.hash_stable(hcx, hasher); - - for stmt in non_item_stmts() { - stmt.hash_stable(hcx, hasher); - } - + stmts.hash_stable(hcx, hasher); expr.hash_stable(hcx, hasher); rules.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); @@ -703,13 +707,23 @@ impl<'gcx> HashStable> for hir::TraitItem { hir_id: _, name, ref attrs, + ref generics, ref node, span } = *self; - hcx.hash_hir_item_like(attrs, |hcx| { + let is_const = match *node { + hir::TraitItemKind::Const(..) | + hir::TraitItemKind::Type(..) => true, + hir::TraitItemKind::Method(hir::MethodSig { constness, .. }, _) => { + constness == hir::Constness::Const + } + }; + + hcx.hash_hir_item_like(attrs, is_const, |hcx| { name.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); + generics.hash_stable(hcx, hasher); node.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }); @@ -738,15 +752,25 @@ impl<'gcx> HashStable> for hir::ImplItem { ref vis, defaultness, ref attrs, + ref generics, ref node, span } = *self; - hcx.hash_hir_item_like(attrs, |hcx| { + let is_const = match *node { + hir::ImplItemKind::Const(..) | + hir::ImplItemKind::Type(..) => true, + hir::ImplItemKind::Method(hir::MethodSig { constness, .. }, _) => { + constness == hir::Constness::Const + } + }; + + hcx.hash_hir_item_like(attrs, is_const, |hcx| { name.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); defaultness.hash_stable(hcx, hasher); attrs.hash_stable(hcx, hasher); + generics.hash_stable(hcx, hasher); node.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); }); @@ -860,18 +884,20 @@ impl<'gcx> HashStable> for hir::Item { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { - let hash_spans = match self.node { + let is_const = match self.node { hir::ItemStatic(..) | - hir::ItemConst(..) | - hir::ItemFn(..) => { - hcx.hash_spans() + hir::ItemConst(..) => { + true + } + hir::ItemFn(_, _, constness, ..) => { + constness == hir::Constness::Const } hir::ItemUse(..) | hir::ItemExternCrate(..) | hir::ItemForeignMod(..) | hir::ItemGlobalAsm(..) | hir::ItemMod(..) | - hir::ItemDefaultImpl(..) | + hir::ItemAutoImpl(..) | hir::ItemTrait(..) | hir::ItemImpl(..) | hir::ItemTy(..) | @@ -892,14 +918,12 @@ impl<'gcx> HashStable> for hir::Item { span } = *self; - hcx.hash_hir_item_like(attrs, |hcx| { - hcx.while_hashing_spans(hash_spans, |hcx| { - name.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); - vis.hash_stable(hcx, hasher); - span.hash_stable(hcx, hasher); - }); + hcx.hash_hir_item_like(attrs, is_const, |hcx| { + name.hash_stable(hcx, hasher); + attrs.hash_stable(hcx, hasher); + node.hash_stable(hcx, hasher); + vis.hash_stable(hcx, hasher); + span.hash_stable(hcx, hasher); }); } } @@ -917,8 +941,8 @@ impl_stable_hash_for!(enum hir::Item_ { ItemEnum(enum_def, generics), ItemStruct(variant_data, generics), ItemUnion(variant_data, generics), - ItemTrait(unsafety, generics, bounds, item_refs), - ItemDefaultImpl(unsafety, trait_ref), + ItemTrait(is_auto, unsafety, generics, bounds, item_refs), + ItemAutoImpl(unsafety, trait_ref), ItemImpl(unsafety, impl_polarity, impl_defaultness, generics, trait_ref, ty, impl_item_refs) }); @@ -968,7 +992,8 @@ impl_stable_hash_for!(struct hir::ForeignItem { impl_stable_hash_for!(enum hir::ForeignItem_ { ForeignItemFn(fn_decl, arg_names, generics), - ForeignItemStatic(ty, is_mutbl) + ForeignItemStatic(ty, is_mutbl), + ForeignItemType }); impl_stable_hash_for!(enum hir::Stmt_ { @@ -1077,6 +1102,7 @@ impl_stable_hash_for!(enum hir::def::Def { PrimTy(prim_ty), TyParam(def_id), SelfTy(trait_def_id, impl_def_id), + TyForeign(def_id), Fn(def_id), Const(def_id), Static(def_id, is_mutbl), @@ -1097,6 +1123,10 @@ impl_stable_hash_for!(enum hir::Mutability { MutImmutable }); +impl_stable_hash_for!(enum hir::IsAuto { + Yes, + No +}); impl_stable_hash_for!(enum hir::Unsafety { Unsafe, diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 4bda89690b7a9..331b44ac119c6 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -31,10 +31,30 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> { lexical_scope, is_user_variable }); -impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref }); +impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref, mutability }); impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, is_cleanup }); -impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, lint_node_id }); +impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, kind }); +impl_stable_hash_for!(struct mir::UnsafetyCheckResult { violations, unsafe_blocks }); +impl<'gcx> HashStable> +for mir::UnsafetyViolationKind { + #[inline] + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + mir::UnsafetyViolationKind::General => {} + mir::UnsafetyViolationKind::ExternStatic(lint_node_id) | + mir::UnsafetyViolationKind::BorrowPacked(lint_node_id) => { + lint_node_id.hash_stable(hcx, hasher); + } + + } + } +} impl<'gcx> HashStable> for mir::Terminator<'gcx> { #[inline] @@ -62,7 +82,8 @@ for mir::Terminator<'gcx> { mir::TerminatorKind::Drop { .. } | mir::TerminatorKind::DropAndReplace { .. } | mir::TerminatorKind::Yield { .. } | - mir::TerminatorKind::Call { .. } => false, + mir::TerminatorKind::Call { .. } | + mir::TerminatorKind::FalseEdges { .. } => false, }; if hash_spans_unconditionally { @@ -77,7 +98,7 @@ for mir::Terminator<'gcx> { } } -impl<'gcx, T> HashStable> for mir::ClearOnDecode +impl<'gcx, T> HashStable> for mir::ClearCrossCrate where T: HashStable> { #[inline] @@ -86,8 +107,8 @@ impl<'gcx, T> HashStable> for mir::ClearOnDecode hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - mir::ClearOnDecode::Clear => {} - mir::ClearOnDecode::Set(ref value) => { + mir::ClearCrossCrate::Clear => {} + mir::ClearCrossCrate::Set(ref value) => { value.hash_stable(hcx, hasher); } } @@ -210,6 +231,12 @@ for mir::TerminatorKind<'gcx> { target.hash_stable(hcx, hasher); cleanup.hash_stable(hcx, hasher); } + mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => { + real_target.hash_stable(hcx, hasher); + for target in imaginary_targets { + target.hash_stable(hcx, hasher); + } + } } } } @@ -245,24 +272,24 @@ for mir::StatementKind<'gcx> { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - lvalue.hash_stable(hcx, hasher); + mir::StatementKind::Assign(ref place, ref rvalue) => { + place.hash_stable(hcx, hasher); rvalue.hash_stable(hcx, hasher); } - mir::StatementKind::SetDiscriminant { ref lvalue, variant_index } => { - lvalue.hash_stable(hcx, hasher); + mir::StatementKind::SetDiscriminant { ref place, variant_index } => { + place.hash_stable(hcx, hasher); variant_index.hash_stable(hcx, hasher); } - mir::StatementKind::StorageLive(ref lvalue) | - mir::StatementKind::StorageDead(ref lvalue) => { - lvalue.hash_stable(hcx, hasher); + mir::StatementKind::StorageLive(ref place) | + mir::StatementKind::StorageDead(ref place) => { + place.hash_stable(hcx, hasher); } mir::StatementKind::EndRegion(ref region_scope) => { region_scope.hash_stable(hcx, hasher); } - mir::StatementKind::Validate(ref op, ref lvalues) => { + mir::StatementKind::Validate(ref op, ref places) => { op.hash_stable(hcx, hasher); - lvalues.hash_stable(hcx, hasher); + places.hash_stable(hcx, hasher); } mir::StatementKind::Nop => {} mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { @@ -282,7 +309,7 @@ impl<'gcx, T> HashStable> hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { - self.lval.hash_stable(hcx, hasher); + self.place.hash_stable(hcx, hasher); self.ty.hash_stable(hcx, hasher); self.re.hash_stable(hcx, hasher); self.mutbl.hash_stable(hcx, hasher); @@ -291,20 +318,20 @@ impl<'gcx, T> HashStable> impl_stable_hash_for!(enum mir::ValidationOp { Acquire, Release, Suspend(region_scope) }); -impl<'gcx> HashStable> for mir::Lvalue<'gcx> { +impl<'gcx> HashStable> for mir::Place<'gcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - mir::Lvalue::Local(ref local) => { + mir::Place::Local(ref local) => { local.hash_stable(hcx, hasher); } - mir::Lvalue::Static(ref statik) => { + mir::Place::Static(ref statik) => { statik.hash_stable(hcx, hasher); } - mir::Lvalue::Projection(ref lvalue_projection) => { - lvalue_projection.hash_stable(hcx, hasher); + mir::Place::Projection(ref place_projection) => { + place_projection.hash_stable(hcx, hasher); } } } @@ -393,8 +420,11 @@ impl<'gcx> HashStable> for mir::Operand<'gcx> { mem::discriminant(self).hash_stable(hcx, hasher); match *self { - mir::Operand::Consume(ref lvalue) => { - lvalue.hash_stable(hcx, hasher); + mir::Operand::Copy(ref place) => { + place.hash_stable(hcx, hasher); + } + mir::Operand::Move(ref place) => { + place.hash_stable(hcx, hasher); } mir::Operand::Constant(ref constant) => { constant.hash_stable(hcx, hasher); @@ -417,13 +447,13 @@ impl<'gcx> HashStable> for mir::Rvalue<'gcx> { operand.hash_stable(hcx, hasher); val.hash_stable(hcx, hasher); } - mir::Rvalue::Ref(region, borrow_kind, ref lvalue) => { + mir::Rvalue::Ref(region, borrow_kind, ref place) => { region.hash_stable(hcx, hasher); borrow_kind.hash_stable(hcx, hasher); - lvalue.hash_stable(hcx, hasher); + place.hash_stable(hcx, hasher); } - mir::Rvalue::Len(ref lvalue) => { - lvalue.hash_stable(hcx, hasher); + mir::Rvalue::Len(ref place) => { + place.hash_stable(hcx, hasher); } mir::Rvalue::Cast(cast_kind, ref operand, ty) => { cast_kind.hash_stable(hcx, hasher); @@ -440,8 +470,8 @@ impl<'gcx> HashStable> for mir::Rvalue<'gcx> { op.hash_stable(hcx, hasher); operand.hash_stable(hcx, hasher); } - mir::Rvalue::Discriminant(ref lvalue) => { - lvalue.hash_stable(hcx, hasher); + mir::Rvalue::Discriminant(ref place) => { + place.hash_stable(hcx, hasher); } mir::Rvalue::NullaryOp(op, ty) => { op.hash_stable(hcx, hasher); diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 669e1ba773e23..c414349c8ffd6 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -347,6 +347,30 @@ impl_stable_hash_for!(enum ::syntax::ast::MetaItemKind { NameValue(lit) }); +impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnInfo { + call_site, + callee +}); + +impl_stable_hash_for!(struct ::syntax_pos::hygiene::NameAndSpan { + format, + allow_internal_unstable, + allow_internal_unsafe, + span +}); + +impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat { + MacroAttribute(sym), + MacroBang(sym), + CompilerDesugaring(kind) +}); + +impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind { + BackArrow, + DotFill, + QuestionMark +}); + impl<'gcx> HashStable> for FileMap { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, @@ -354,6 +378,7 @@ impl<'gcx> HashStable> for FileMap { let FileMap { ref name, name_was_remapped, + unmapped_path: _, crate_of_origin, // Do not hash the source as it is not encoded src: _, @@ -363,6 +388,7 @@ impl<'gcx> HashStable> for FileMap { end_pos: _, ref lines, ref multibyte_chars, + ref non_narrow_chars, } = *self; name.hash_stable(hcx, hasher); @@ -388,6 +414,12 @@ impl<'gcx> HashStable> for FileMap { for &char_pos in multibyte_chars.iter() { stable_multibyte_char(char_pos, start_pos).hash_stable(hcx, hasher); } + + let non_narrow_chars = non_narrow_chars.borrow(); + non_narrow_chars.len().hash_stable(hcx, hasher); + for &char_pos in non_narrow_chars.iter() { + stable_non_narrow_char(char_pos, start_pos).hash_stable(hcx, hasher); + } } } @@ -407,3 +439,12 @@ fn stable_multibyte_char(mbc: ::syntax_pos::MultiByteChar, (pos.0 - filemap_start.0, bytes as u32) } + +fn stable_non_narrow_char(swc: ::syntax_pos::NonNarrowChar, + filemap_start: ::syntax_pos::BytePos) + -> (u32, u32) { + let pos = swc.pos(); + let width = swc.width(); + + (pos.0 - filemap_start.0, width as u32) +} diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 2bbf807807bad..9609ae5a0beb8 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -61,6 +61,9 @@ for ty::RegionKind { def_id.hash_stable(hcx, hasher); name.hash_stable(hcx, hasher); } + ty::ReLateBound(db, ty::BrEnv) => { + db.depth.hash_stable(hcx, hasher); + } ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => { def_id.hash_stable(hcx, hasher); index.hash_stable(hcx, hasher); @@ -233,8 +236,9 @@ impl<'gcx> HashStable> for ty::Predicate<'gcx> { ty::Predicate::ObjectSafe(def_id) => { def_id.hash_stable(hcx, hasher); } - ty::Predicate::ClosureKind(def_id, closure_kind) => { + ty::Predicate::ClosureKind(def_id, closure_substs, closure_kind) => { def_id.hash_stable(hcx, hasher); + closure_substs.hash_stable(hcx, hasher); closure_kind.hash_stable(hcx, hasher); } ty::Predicate::ConstEvaluatable(def_id, substs) => { @@ -367,7 +371,8 @@ for ::middle::const_val::ErrKind<'gcx> { MiscBinaryOp | MiscCatchAll | IndexOpFeatureGated | - TypeckError => { + TypeckError | + CheckMatchError => { // nothing to do } UnimplementedConstVal(s) => { @@ -463,7 +468,8 @@ impl_stable_hash_for!(struct ty::TypeParameterDef { index, has_default, object_lifetime_default, - pure_wrt_drop + pure_wrt_drop, + synthetic }); impl<'gcx, T> HashStable> @@ -488,10 +494,15 @@ for ::middle::resolve_lifetime::Set1 } } +impl_stable_hash_for!(enum ::middle::resolve_lifetime::LifetimeDefOrigin { + Explicit, + InBand +}); + impl_stable_hash_for!(enum ::middle::resolve_lifetime::Region { Static, - EarlyBound(index, decl), - LateBound(db_index, decl), + EarlyBound(index, decl, is_in_band), + LateBound(db_index, decl, is_in_band), LateBoundAnon(db_index, anon_index), Free(call_site_scope_data, decl) }); @@ -514,7 +525,7 @@ impl_stable_hash_for!(enum ty::cast::CastKind { FnPtrAddrCast }); -impl_stable_hash_for!(struct ::middle::region::FirstStatementIndex { idx }); +impl_stable_hash_for!(tuple_struct ::middle::region::FirstStatementIndex { idx }); impl_stable_hash_for!(struct ::middle::region::Scope { id, code }); impl<'gcx> ToStableHashKey> for region::Scope { @@ -606,8 +617,7 @@ for ty::TypeVariants<'gcx> def_id.hash_stable(hcx, hasher); closure_substs.hash_stable(hcx, hasher); } - TyGenerator(def_id, closure_substs, interior) - => { + TyGenerator(def_id, closure_substs, interior) => { def_id.hash_stable(hcx, hasher); closure_substs.hash_stable(hcx, hasher); interior.hash_stable(hcx, hasher); @@ -626,6 +636,9 @@ for ty::TypeVariants<'gcx> TyParam(param_ty) => { param_ty.hash_stable(hcx, hasher); } + TyForeign(def_id) => { + def_id.hash_stable(hcx, hasher); + } TyInfer(..) => { bug!("ty::TypeVariants::hash_stable() - Unexpected variant {:?}.", *self) } @@ -725,13 +738,13 @@ impl<'gcx> HashStable> for ty::TraitDef { def_id: _, unsafety, paren_sugar, - has_default_impl, + has_auto_impl, def_path_hash, } = *self; unsafety.hash_stable(hcx, hasher); paren_sugar.hash_stable(hcx, hasher); - has_default_impl.hash_stable(hcx, hasher); + has_auto_impl.hash_stable(hcx, hasher); def_path_hash.hash_stable(hcx, hasher); } } @@ -751,13 +764,11 @@ impl<'gcx> HashStable> for ty::CrateVariancesMap { hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { let ty::CrateVariancesMap { - ref dependencies, ref variances, // This is just an irrelevant helper value. empty_variance: _, } = *self; - dependencies.hash_stable(hcx, hasher); variances.hash_stable(hcx, hasher); } } @@ -840,3 +851,129 @@ impl_stable_hash_for!(struct ::util::common::ErrorReported {}); impl_stable_hash_for!(tuple_struct ::middle::reachable::ReachableSet { reachable_set }); + +impl<'gcx, N> HashStable> +for traits::Vtable<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use traits::Vtable::*; + + mem::discriminant(self).hash_stable(hcx, hasher); + + match self { + &VtableImpl(ref table_impl) => table_impl.hash_stable(hcx, hasher), + &VtableAutoImpl(ref table_def_impl) => table_def_impl.hash_stable(hcx, hasher), + &VtableParam(ref table_param) => table_param.hash_stable(hcx, hasher), + &VtableObject(ref table_obj) => table_obj.hash_stable(hcx, hasher), + &VtableBuiltin(ref table_builtin) => table_builtin.hash_stable(hcx, hasher), + &VtableClosure(ref table_closure) => table_closure.hash_stable(hcx, hasher), + &VtableFnPointer(ref table_fn_pointer) => table_fn_pointer.hash_stable(hcx, hasher), + &VtableGenerator(ref table_generator) => table_generator.hash_stable(hcx, hasher), + } + } +} + +impl<'gcx, N> HashStable> +for traits::VtableImplData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableImplData { + impl_def_id, + substs, + ref nested, + } = *self; + impl_def_id.hash_stable(hcx, hasher); + substs.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableAutoImplData where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableAutoImplData { + trait_def_id, + ref nested, + } = *self; + trait_def_id.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableObjectData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableObjectData { + upcast_trait_ref, + vtable_base, + ref nested, + } = *self; + upcast_trait_ref.hash_stable(hcx, hasher); + vtable_base.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableBuiltinData where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableBuiltinData { + ref nested, + } = *self; + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableClosureData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableClosureData { + closure_def_id, + substs, + ref nested, + } = *self; + closure_def_id.hash_stable(hcx, hasher); + substs.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableFnPointerData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableFnPointerData { + fn_ty, + ref nested, + } = *self; + fn_ty.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} + +impl<'gcx, N> HashStable> +for traits::VtableGeneratorData<'gcx, N> where N: HashStable> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let traits::VtableGeneratorData { + closure_def_id, + substs, + ref nested, + } = *self; + closure_def_id.hash_stable(hcx, hasher); + substs.hash_stable(hcx, hasher); + nested.hash_stable(hcx, hasher); + } +} diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs index cd0749a686511..cbd76ee14db38 100644 --- a/src/librustc/ich/mod.rs +++ b/src/librustc/ich/mod.rs @@ -28,8 +28,6 @@ mod impls_syntax; pub const ATTR_DIRTY: &'static str = "rustc_dirty"; pub const ATTR_CLEAN: &'static str = "rustc_clean"; -pub const ATTR_DIRTY_METADATA: &'static str = "rustc_metadata_dirty"; -pub const ATTR_CLEAN_METADATA: &'static str = "rustc_metadata_clean"; pub const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed"; pub const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; pub const ATTR_PARTITION_REUSED: &'static str = "rustc_partition_reused"; @@ -41,8 +39,6 @@ pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ ATTR_THEN_THIS_WOULD_NEED, ATTR_DIRTY, ATTR_CLEAN, - ATTR_DIRTY_METADATA, - ATTR_CLEAN_METADATA, ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED, ]; @@ -53,8 +49,6 @@ pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ ATTR_THEN_THIS_WOULD_NEED, ATTR_DIRTY, ATTR_CLEAN, - ATTR_DIRTY_METADATA, - ATTR_CLEAN_METADATA, ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED, ]; diff --git a/src/librustc/infer/README.md b/src/librustc/infer/README.md index b4075f6973098..e7daff3e2c371 100644 --- a/src/librustc/infer/README.md +++ b/src/librustc/infer/README.md @@ -1,239 +1,227 @@ # Type inference engine -This is loosely based on standard HM-type inference, but with an -extension to try and accommodate subtyping. There is nothing -principled about this extension; it's sound---I hope!---but it's a -heuristic, ultimately, and does not guarantee that it finds a valid -typing even if one exists (in fact, there are known scenarios where it -fails, some of which may eventually become problematic). - -## Key idea - -The main change is that each type variable T is associated with a -lower-bound L and an upper-bound U. L and U begin as bottom and top, -respectively, but gradually narrow in response to new constraints -being introduced. When a variable is finally resolved to a concrete -type, it can (theoretically) select any type that is a supertype of L -and a subtype of U. - -There are several critical invariants which we maintain: - -- the upper-bound of a variable only becomes lower and the lower-bound - only becomes higher over time; -- the lower-bound L is always a subtype of the upper bound U; -- the lower-bound L and upper-bound U never refer to other type variables, - but only to types (though those types may contain type variables). - -> An aside: if the terms upper- and lower-bound confuse you, think of -> "supertype" and "subtype". The upper-bound is a "supertype" -> (super=upper in Latin, or something like that anyway) and the lower-bound -> is a "subtype" (sub=lower in Latin). I find it helps to visualize -> a simple class hierarchy, like Java minus interfaces and -> primitive types. The class Object is at the root (top) and other -> types lie in between. The bottom type is then the Null type. -> So the tree looks like: -> -> ```text -> Object -> / \ -> String Other -> \ / -> (null) -> ``` -> -> So the upper bound type is the "supertype" and the lower bound is the -> "subtype" (also, super and sub mean upper and lower in Latin, or something -> like that anyway). - -## Satisfying constraints - -At a primitive level, there is only one form of constraint that the -inference understands: a subtype relation. So the outside world can -say "make type A a subtype of type B". If there are variables -involved, the inferencer will adjust their upper- and lower-bounds as -needed to ensure that this relation is satisfied. (We also allow "make -type A equal to type B", but this is translated into "A <: B" and "B -<: A") - -As stated above, we always maintain the invariant that type bounds -never refer to other variables. This keeps the inference relatively -simple, avoiding the scenario of having a kind of graph where we have -to pump constraints along and reach a fixed point, but it does impose -some heuristics in the case where the user is relating two type -variables A <: B. - -Combining two variables such that variable A will forever be a subtype -of variable B is the trickiest part of the algorithm because there is -often no right choice---that is, the right choice will depend on -future constraints which we do not yet know. The problem comes about -because both A and B have bounds that can be adjusted in the future. -Let's look at some of the cases that can come up. - -Imagine, to start, the best case, where both A and B have an upper and -lower bound (that is, the bounds are not top nor bot respectively). In -that case, if we're lucky, A.ub <: B.lb, and so we know that whatever -A and B should become, they will forever have the desired subtyping -relation. We can just leave things as they are. - -### Option 1: Unify - -However, suppose that A.ub is *not* a subtype of B.lb. In -that case, we must make a decision. One option is to unify A -and B so that they are one variable whose bounds are: - - UB = GLB(A.ub, B.ub) - LB = LUB(A.lb, B.lb) - -(Note that we will have to verify that LB <: UB; if it does not, the -types are not intersecting and there is an error) In that case, A <: B -holds trivially because A==B. However, we have now lost some -flexibility, because perhaps the user intended for A and B to end up -as different types and not the same type. - -Pictorally, what this does is to take two distinct variables with -(hopefully not completely) distinct type ranges and produce one with -the intersection. - -```text - B.ub B.ub - /\ / - A.ub / \ A.ub / - / \ / \ \ / - / X \ UB - / / \ \ / \ - / / / \ / / - \ \ / / \ / - \ X / LB - \ / \ / / \ - \ / \ / / \ - A.lb B.lb A.lb B.lb -``` +The type inference is based on standard HM-type inference, but +extended in various way to accommodate subtyping, region inference, +and higher-ranked types. + +## A note on terminology + +We use the notation `?T` to refer to inference variables, also called +existential variables. + +We use the term "region" and "lifetime" interchangeably. Both refer to +the `'a` in `&'a T`. + +The term "bound region" refers to regions bound in a function +signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is +"free" if it is not bound. +## Creating an inference context -### Option 2: Relate UB/LB - -Another option is to keep A and B as distinct variables but set their -bounds in such a way that, whatever happens, we know that A <: B will hold. -This can be achieved by ensuring that A.ub <: B.lb. In practice there -are two ways to do that, depicted pictorially here: - -```text - Before Option #1 Option #2 - - B.ub B.ub B.ub - /\ / \ / \ - A.ub / \ A.ub /(B')\ A.ub /(B')\ - / \ / \ \ / / \ / / - / X \ __UB____/ UB / - / / \ \ / | | / - / / / \ / | | / - \ \ / / /(A')| | / - \ X / / LB ______LB/ - \ / \ / / / \ / (A')/ \ - \ / \ / \ / \ \ / \ - A.lb B.lb A.lb B.lb A.lb B.lb +You create and "enter" an inference context by doing something like +the following: + +```rust +tcx.infer_ctxt().enter(|infcx| { + // use the inference context `infcx` in here +}) ``` -In these diagrams, UB and LB are defined as before. As you can see, -the new ranges `A'` and `B'` are quite different from the range that -would be produced by unifying the variables. +Each inference context creates a short-lived type arena to store the +fresh types and things that it will create, as described in +[the README in the ty module][ty-readme]. This arena is created by the `enter` +function and disposed after it returns. -### What we do now +[ty-readme]: src/librustc/ty/README.md -Our current technique is to *try* (transactionally) to relate the -existing bounds of A and B, if there are any (i.e., if `UB(A) != top -&& LB(B) != bot`). If that succeeds, we're done. If it fails, then -we merge A and B into same variable. +Within the closure, the infcx will have the type `InferCtxt<'cx, 'gcx, +'tcx>` for some fresh `'cx` and `'tcx` -- the latter corresponds to +the lifetime of this temporary arena, and the `'cx` is the lifetime of +the `InferCtxt` itself. (Again, see [that ty README][ty-readme] for +more details on this setup.) -This is not clearly the correct course. For example, if `UB(A) != -top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)` -and leave the variables unmerged. This is sometimes the better -course, it depends on the program. +The `tcx.infer_ctxt` method actually returns a build, which means +there are some kinds of configuration you can do before the `infcx` is +created. See `InferCtxtBuilder` for more information. -The main case which fails today that I would like to support is: +## Inference variables -```rust -fn foo(x: T, y: T) { ... } +The main purpose of the inference context is to house a bunch of +**inference variables** -- these represent types or regions whose precise +value is not yet known, but will be uncovered as we perform type-checking. + +If you're familiar with the basic ideas of unification from H-M type +systems, or logic languages like Prolog, this is the same concept. If +you're not, you might want to read a tutorial on how H-M type +inference works, or perhaps this blog post on +[unification in the Chalk project]. -fn bar() { - let x: @mut int = @mut 3; - let y: @int = @3; - foo(x, y); -} +[Unification in the Chalk project]: http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/ + +All told, the inference context stores four kinds of inference variables as of this +writing: + +- Type variables, which come in three varieties: + - General type variables (the most common). These can be unified with any type. + - Integral type variables, which can only be unified with an integral type, and + arise from an integer literal expression like `22`. + - Float type variables, which can only be unified with a float type, and + arise from a float literal expression like `22.0`. +- Region variables, which represent lifetimes, and arise all over the dang place. + +All the type variables work in much the same way: you can create a new +type variable, and what you get is `Ty<'tcx>` representing an +unresolved type `?T`. Then later you can apply the various operations +that the inferencer supports, such as equality or subtyping, and it +will possibly **instantiate** (or **bind**) that `?T` to a specific +value as a result. + +The region variables work somewhat differently, and are described +below in a separate section. + +## Enforcing equality / subtyping + +The most basic operations you can perform in the type inferencer is +**equality**, which forces two types `T` and `U` to be the same. The +recommended way to add an equality constraint is using the `at` +method, roughly like so: + +``` +infcx.at(...).eq(t, u); ``` -In principle, the inferencer ought to find that the parameter `T` to -`foo(x, y)` is `@const int`. Today, however, it does not; this is -because the type variable `T` is merged with the type variable for -`X`, and thus inherits its UB/LB of `@mut int`. This leaves no -flexibility for `T` to later adjust to accommodate `@int`. - -Note: `@` and `@mut` are replaced with `Rc` and `Rc>` in current Rust. - -### What to do when not all bounds are present - -In the prior discussion we assumed that A.ub was not top and B.lb was -not bot. Unfortunately this is rarely the case. Often type variables -have "lopsided" bounds. For example, if a variable in the program has -been initialized but has not been used, then its corresponding type -variable will have a lower bound but no upper bound. When that -variable is then used, we would like to know its upper bound---but we -don't have one! In this case we'll do different things depending on -how the variable is being used. - -## Transactional support - -Whenever we adjust merge variables or adjust their bounds, we always -keep a record of the old value. This allows the changes to be undone. - -## Regions - -I've only talked about type variables here, but region variables -follow the same principle. They have upper- and lower-bounds. A -region A is a subregion of a region B if A being valid implies that B -is valid. This basically corresponds to the block nesting structure: -the regions for outer block scopes are superregions of those for inner -block scopes. - -## Integral and floating-point type variables - -There is a third variety of type variable that we use only for -inferring the types of unsuffixed integer literals. Integral type -variables differ from general-purpose type variables in that there's -no subtyping relationship among the various integral types, so instead -of associating each variable with an upper and lower bound, we just -use simple unification. Each integer variable is associated with at -most one integer type. Floating point types are handled similarly to -integral types. - -## GLB/LUB - -Computing the greatest-lower-bound and least-upper-bound of two -types/regions is generally straightforward except when type variables -are involved. In that case, we follow a similar "try to use the bounds -when possible but otherwise merge the variables" strategy. In other -words, `GLB(A, B)` where `A` and `B` are variables will often result -in `A` and `B` being merged and the result being `A`. - -## Type coercion - -We have a notion of assignability which differs somewhat from -subtyping; in particular it may cause region borrowing to occur. See -the big comment later in this file on Type Coercion for specifics. - -### In conclusion - -I showed you three ways to relate `A` and `B`. There are also more, -of course, though I'm not sure if there are any more sensible options. -The main point is that there are various options, each of which -produce a distinct range of types for `A` and `B`. Depending on what -the correct values for A and B are, one of these options will be the -right choice: but of course we don't know the right values for A and B -yet, that's what we're trying to find! In our code, we opt to unify -(Option #1). - -# Implementation details - -We make use of a trait-like implementation strategy to consolidate -duplicated code between subtypes, GLB, and LUB computations. See the -section on "Type Combining" in combine.rs for more details. +The first `at()` call provides a bit of context, i.e., why you are +doing this unification, and in what environment, and the `eq` method +performs the actual equality constraint. + +When you equate things, you force them to be precisely equal. Equating +returns a `InferResult` -- if it returns `Err(err)`, then equating +failed, and the enclosing `TypeError` will tell you what went wrong. + +The success case is perhaps more interesting. The "primary" return +type of `eq` is `()` -- that is, when it succeeds, it doesn't return a +value of any particular interest. Rather, it is executed for its +side-effects of constraining type variables and so forth. However, the +actual return type is not `()`, but rather `InferOk<()>`. The +`InferOk` type is used to carry extra trait obligations -- your job is +to ensure that these are fulfilled (typically by enrolling them in a +fulfillment context). See the [trait README] for more background here. + +[trait README]: ../traits/README.md + +You can also enforce subtyping through `infcx.at(..).sub(..)`. The same +basic concepts apply as above. + +## "Trying" equality + +Sometimes you would like to know if it is *possible* to equate two +types without error. You can test that with `infcx.can_eq` (or +`infcx.can_sub` for subtyping). If this returns `Ok`, then equality +is possible -- but in all cases, any side-effects are reversed. + +Be aware though that the success or failure of these methods is always +**modulo regions**. That is, two types `&'a u32` and `&'b u32` will +return `Ok` for `can_eq`, even if `'a != 'b`. This falls out from the +"two-phase" nature of how we solve region constraints. + +## Snapshots + +As described in the previous section on `can_eq`, often it is useful +to be able to do a series of operations and then roll back their +side-effects. This is done for various reasons: one of them is to be +able to backtrack, trying out multiple possibilities before settling +on which path to take. Another is in order to ensure that a series of +smaller changes take place atomically or not at all. + +To allow for this, the inference context supports a `snapshot` method. +When you call it, it will start recording changes that occur from the +operations you perform. When you are done, you can either invoke +`rollback_to`, which will undo those changes, or else `confirm`, which +will make the permanent. Snapshots can be nested as long as you follow +a stack-like discipline. + +Rather than use snapshots directly, it is often helpful to use the +methods like `commit_if_ok` or `probe` that encapsulte higher-level +patterns. + +## Subtyping obligations + +One thing worth discussing are subtyping obligations. When you force +two types to be a subtype, like `?T <: i32`, we can often convert those +into equality constraints. This follows from Rust's rather limited notion +of subtyping: so, in the above case, `?T <: i32` is equivalent to `?T = i32`. + +However, in some cases we have to be more careful. For example, when +regions are involved. So if you have `?T <: &'a i32`, what we would do +is to first "generalize" `&'a i32` into a type with a region variable: +`&'?b i32`, and then unify `?T` with that (`?T = &'?b i32`). We then +relate this new variable with the original bound: + + &'?b i32 <: &'a i32 + +This will result in a region constraint (see below) of `'?b: 'a`. + +One final interesting case is relating two unbound type variables, +like `?T <: ?U`. In that case, we can't make progress, so we enqueue +an obligation `Subtype(?T, ?U)` and return it via the `InferOk` +mechanism. You'll have to try again when more details about `?T` or +`?U` are known. + +## Region constraints + +Regions are inferred somewhat differently from types. Rather than +eagerly unifying things, we simply collect constraints as we go, but +make (almost) no attempt to solve regions. These constraints have the +form of an outlives constraint: + + 'a: 'b + +Actually the code tends to view them as a subregion relation, but it's the same +idea: + + 'b <= 'a + +(There are various other kinds of constriants, such as "verifys"; see +the `region_constraints` module for details.) + +There is one case where we do some amount of eager unification. If you have an equality constraint +between two regions + + 'a = 'b + +we will record that fact in a unification table. You can then use +`opportunistic_resolve_var` to convert `'b` to `'a` (or vice +versa). This is sometimes needed to ensure termination of fixed-point +algorithms. + +## Extracting region constraints + +Ultimately, region constraints are only solved at the very end of +type-checking, once all other constraints are known. There are two +ways to solve region constraints right now: lexical and +non-lexical. Eventually there will only be one. + +To solve **lexical** region constraints, you invoke +`resolve_regions_and_report_errors`. This will "close" the region +constraint process and invoke the `lexical_region_resolve` code. Once +this is done, any further attempt to equate or create a subtyping +relationship will yield an ICE. + +Non-lexical region constraints are not handled within the inference +context. Instead, the NLL solver (actually, the MIR type-checker) +invokes `take_and_reset_region_constraints` periodically. This +extracts all of the outlives constraints from the region solver, but +leaves the set of variables intact. This is used to get *just* the +region constraints that resulted from some particular point in the +program, since the NLL solver needs to know not just *what* regions +were subregions but *where*. Finally, the NLL solver invokes +`take_region_var_origins`, which "closes" the region constraint +process in the same way as normal solving. + +## Lexical region resolution + +Lexical region resolution is done by initially assigning each region +variable to an empty value. We then process each outlives constraint +repeatedly, growing region variables until a fixed-point is reached. +Region variables can be grown using a least-upper-bound relation on +the region lattice in a fairly straight-forward fashion. diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 40e933b26a257..50a37e12531a7 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -270,6 +270,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid), ambient_variance, needs_wf: false, + root_ty: ty, }; let ty = generalize.relate(&ty, &ty)?; @@ -280,10 +281,23 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + + /// Span, used when creating new type variables and things. span: Span, + + /// The vid of the type variable that is in the process of being + /// instantiated; if we find this within the type we are folding, + /// that means we would have created a cyclic type. for_vid_sub_root: ty::TyVid, + + /// Track the variance as we descend into the type. ambient_variance: ty::Variance, - needs_wf: bool, // see the field `needs_wf` in `Generalization` + + /// See the field `needs_wf` in `Generalization`. + needs_wf: bool, + + /// The root type that we are generalizing. Used when reporting cycles. + root_ty: Ty<'tcx>, } /// Result from a generalization operation. This includes @@ -386,7 +400,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' if sub_vid == self.for_vid_sub_root { // If sub-roots are equal, then `for_vid` and // `vid` are related via subtyping. - return Err(TypeError::CyclicTy); + return Err(TypeError::CyclicTy(self.root_ty)); } else { match variables.probe_root(vid) { Some(u) => { diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index f9ffaee81f157..2ae8f8ae93357 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -104,7 +104,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> a, b); let origin = Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_eqregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_eqregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs index 6c57130a9955f..cade67a44bb0e 100644 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/different_lifetimes.rs @@ -13,35 +13,54 @@ use hir; use infer::InferCtxt; use ty::{self, Region}; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use infer::lexical_region_resolve::RegionResolutionError; use hir::map as hir_map; use middle::resolve_lifetime as rl; use hir::intravisit::{self, Visitor, NestedVisitorMap}; +use infer::error_reporting::util::AnonymousArgInfo; impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - // This method prints the error message for lifetime errors when both the concerned regions - // are anonymous. - // Consider a case where we have - // fn foo(x: &mut Vec<&u8>, y: &u8) - // { x.push(y); }. - // The example gives - // fn foo(x: &mut Vec<&u8>, y: &u8) { - // --- --- these references are declared with different lifetimes... - // x.push(y); - // ^ ...but data from `y` flows into `x` here - // It has been extended for the case of structs too. - // Consider the example - // struct Ref<'a> { x: &'a u32 } - // fn foo(mut x: Vec, y: Ref) { - // --- --- these structs are declared with different lifetimes... - // x.push(y); - // ^ ...but data from `y` flows into `x` here - // } - // It will later be extended to trait objects. + /// Print the error message for lifetime errors when both the concerned regions are anonymous. + /// + /// Consider a case where we have + /// + /// ```no_run + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// x.push(y); + /// } + /// ``` + /// + /// The example gives + /// + /// ```text + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// --- --- these references are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// ``` + /// + /// It has been extended for the case of structs too. + /// + /// Consider the example + /// + /// ```no_run + /// struct Ref<'a> { x: &'a u32 } + /// ``` + /// + /// ```text + /// fn foo(mut x: Vec, y: Ref) { + /// --- --- these structs are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// } + /// ```` + /// + /// It will later be extended to trait objects. pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool { let (span, sub, sup) = match *error { ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), + SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup), _ => return false, // inapplicable }; @@ -57,6 +76,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup)); let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub)); + debug!("try_report_anon_anon_conflict: found_arg1={:?} sup={:?} br1={:?}", ty_sub, sup, @@ -66,56 +86,70 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub, bregion_sub); - let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) = - (self.find_arg_with_region(sup, sup), self.find_arg_with_region(sub, sub)) { + let (ty_sup, ty_fndecl_sup) = ty_sup; + let (ty_sub, ty_fndecl_sub) = ty_sub; - let (anon_arg_sup, is_first_sup, anon_arg_sub, is_first_sub) = - (sup_arg.arg, sup_arg.is_first, sub_arg.arg, sub_arg.is_first); - if self.is_self_anon(is_first_sup, scope_def_id_sup) || - self.is_self_anon(is_first_sub, scope_def_id_sub) { - return false; - } + let AnonymousArgInfo { arg: anon_arg_sup, .. } = + or_false!(self.find_arg_with_region(sup, sup)); + let AnonymousArgInfo { arg: anon_arg_sub, .. } = + or_false!(self.find_arg_with_region(sub, sub)); - if self.is_return_type_anon(scope_def_id_sup, bregion_sup) || - self.is_return_type_anon(scope_def_id_sub, bregion_sub) { - return false; - } + let sup_is_ret_type = + self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); + let sub_is_ret_type = + self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); + + let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() { + format!(" from `{}`", simple_name) + } else { + format!("") + }; + + let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() { + format!(" into `{}`", simple_name) + } else { + format!("") + }; - if anon_arg_sup == anon_arg_sub { - (format!("this type was declared with multiple lifetimes..."), - format!(" with one lifetime"), - format!(" into the other")) - } else { - let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() { - format!(" from `{}`", simple_name) - } else { - format!("") - }; - let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() { - format!(" into `{}`", simple_name) + let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) { + (None, None) => { + let (main_label_1, span_label_1) = if ty_sup == ty_sub { + + (format!("this type is declared with multiple lifetimes..."), + format!("...but data{} flows{} here", + format!(" with one lifetime"), + format!(" into the other"))) } else { - format!("") + (format!("these two types are declared with different lifetimes..."), + format!("...but data{} flows{} here", + span_label_var1, + span_label_var2)) }; + (ty_sup.span, ty_sub.span, main_label_1, span_label_1) + } - let span_label = - format!("these two types are declared with different lifetimes...",); - - (span_label, span_label_var1, span_label_var2) + (Some(ret_span), _) => { + (ty_sub.span, + ret_span, + format!("this parameter and the return type are declared \ + with different lifetimes...",), + format!("...but data{} is returned here", span_label_var1)) + } + (_, Some(ret_span)) => { + (ty_sup.span, + ret_span, + format!("this parameter and the return type are declared \ + with different lifetimes...",), + format!("...but data{} is returned here", span_label_var1)) } - } else { - debug!("no arg with anon region found"); - debug!("try_report_anon_anon_conflict: is_suitable(sub) = {:?}", - self.is_suitable_region(sub)); - debug!("try_report_anon_anon_conflict: is_suitable(sup) = {:?}", - self.is_suitable_region(sup)); - return false; }; + struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch") - .span_label(ty_sup.span, main_label) - .span_label(ty_sub.span, format!("")) - .span_label(span, format!("...but data{} flows{} here", label1, label2)) + .span_label(span_1, main_label) + .span_label(span_2, format!("")) + .span_label(span, span_label) .emit(); return true; } @@ -135,28 +169,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// ``` /// The function returns the nested type corresponding to the anonymous region /// for e.g. `&u8` and Vec<`&u8`. - pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> { + pub fn find_anon_type(&self, + region: Region<'tcx>, + br: &ty::BoundRegion) + -> Option<(&hir::Ty, &hir::FnDecl)> { if let Some(anon_reg) = self.is_suitable_region(region) { let def_id = anon_reg.def_id; if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { - let inputs: &[_] = match self.tcx.hir.get(node_id) { + let fndecl = match self.tcx.hir.get(node_id) { hir_map::NodeItem(&hir::Item { node: hir::ItemFn(ref fndecl, ..), .. }) => { - &fndecl.inputs + &fndecl } hir_map::NodeTraitItem(&hir::TraitItem { - node: hir::TraitItemKind::Method(ref fndecl, ..), .. - }) => &fndecl.decl.inputs, + node: hir::TraitItemKind::Method(ref m, ..), .. + }) | hir_map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(ref fndecl, ..), .. - }) => &fndecl.decl.inputs, - - _ => &[], + node: hir::ImplItemKind::Method(ref m, ..), .. + }) => &m.decl, + _ => return None, }; - return inputs + return fndecl + .inputs .iter() - .filter_map(|arg| self.find_component_for_bound_region(&**arg, br)) - .next(); + .filter_map(|arg| self.find_component_for_bound_region(arg, br)) + .next() + .map(|ty| (ty, &**fndecl)); } } None @@ -243,7 +281,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ def_id={:?}", id, def_id); if id == def_id { @@ -255,7 +293,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => { + ( + Some(rl::Region::LateBound(debruijn_index, id, _)), + ty::BrNamed(def_id, _) + ) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index.depth); debug!("self.infcx.tcx.hir.local_def_id(id)={:?}", id); @@ -268,8 +309,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { (Some(rl::Region::Static), _) | (Some(rl::Region::Free(_, _)), _) | - (Some(rl::Region::EarlyBound(_, _)), _) | - (Some(rl::Region::LateBound(_, _)), _) | + (Some(rl::Region::EarlyBound(_, _, _)), _) | + (Some(rl::Region::LateBound(_, _, _)), _) | (Some(rl::Region::LateBoundAnon(_, _)), _) | (None, _) => { debug!("no arg found"); @@ -330,7 +371,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> { } } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ def_id={:?}", id, def_id); if id == def_id { @@ -339,7 +380,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> { } } - (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index.depth); debug!("id={:?}", id); @@ -351,8 +392,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> { } (Some(rl::Region::Static), _) | - (Some(rl::Region::EarlyBound(_, _)), _) | - (Some(rl::Region::LateBound(_, _)), _) | + (Some(rl::Region::EarlyBound(_, _, _)), _) | + (Some(rl::Region::LateBound(_, _, _)), _) | (Some(rl::Region::LateBoundAnon(_, _)), _) | (Some(rl::Region::Free(_, _)), _) | (None, _) => { diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 3f22950fc773f..514b29120a96a 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -57,8 +57,8 @@ use infer; use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs}; -use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict, - GenericBoundFailure, GenericKind}; +use super::region_constraints::GenericKind; +use super::lexical_region_resolve::RegionResolutionError; use std::fmt; use hir; @@ -66,7 +66,7 @@ use hir::map as hir_map; use hir::def_id::DefId; use middle::region; use traits::{ObligationCause, ObligationCauseCode}; -use ty::{self, Region, Ty, TyCtxt, TypeFoldable}; +use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants}; use ty::error::TypeError; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::{Pos, Span}; @@ -177,13 +177,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReEarlyBound(_) | ty::ReFree(_) => { - let scope = match *region { - ty::ReEarlyBound(ref br) => { - self.parent_def_id(br.def_id).unwrap() - } - ty::ReFree(ref fr) => fr.scope, - _ => bug!() - }; + let scope = region.free_region_binding_scope(self); let prefix = match *region { ty::ReEarlyBound(ref br) => { format!("the lifetime {} as defined on", br.name) @@ -262,6 +256,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { errors: &Vec>) { debug!("report_region_errors(): {} errors to start", errors.len()); + if self.tcx.sess.opts.debugging_opts.nll { + for error in errors { + match *error { + RegionResolutionError::ConcreteFailure(ref origin, ..) | + RegionResolutionError::GenericBoundFailure(ref origin, ..) => { + self.tcx.sess.span_warn( + origin.span(), + "not reporting region error due to -Znll"); + } + + RegionResolutionError::SubSupConflict(ref rvo, ..) => { + self.tcx.sess.span_warn( + rvo.span(), + "not reporting region error due to -Znll"); + } + } + } + + return; + } + // try to pre-process the errors, which will group some of them // together into a `ProcessedErrors` group: let errors = self.process_errors(errors); @@ -272,33 +287,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { debug!("report_region_errors: error = {:?}", error); if !self.try_report_named_anon_conflict(&error) && - !self.try_report_anon_anon_conflict(&error) { - - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - ConcreteFailure(origin, sub, sup) => { - self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); - } - - GenericBoundFailure(kind, param_ty, sub) => { - self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); - } - - SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => { + !self.try_report_anon_anon_conflict(&error) + { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); + } + + RegionResolutionError::GenericBoundFailure(kind, param_ty, sub) => { + self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); + } + + RegionResolutionError::SubSupConflict(var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r) => { self.report_sub_sup_conflict(region_scope_tree, var_origin, sub_origin, sub_r, sup_origin, sup_r); - } - } + } + } } } } @@ -330,9 +349,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // the only thing in the list. let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - ConcreteFailure(..) => false, - SubSupConflict(..) => false, - GenericBoundFailure(..) => true, + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) | + RegionResolutionError::SubSupConflict(..) => false, }; @@ -344,9 +363,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // sort the errors by span, for better error message stability. errors.sort_by_key(|u| match *u { - ConcreteFailure(ref sro, _, _) => sro.span(), - GenericBoundFailure(ref sro, _, _) => sro.span(), - SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), }); errors } @@ -536,6 +555,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) { + fn equals<'tcx>(a: &Ty<'tcx>, b: &Ty<'tcx>) -> bool { + match (&a.sty, &b.sty) { + (a, b) if *a == *b => true, + (&ty::TyInt(_), &ty::TyInfer(ty::InferTy::IntVar(_))) | + (&ty::TyInfer(ty::InferTy::IntVar(_)), &ty::TyInt(_)) | + (&ty::TyInfer(ty::InferTy::IntVar(_)), &ty::TyInfer(ty::InferTy::IntVar(_))) | + (&ty::TyFloat(_), &ty::TyInfer(ty::InferTy::FloatVar(_))) | + (&ty::TyInfer(ty::InferTy::FloatVar(_)), &ty::TyFloat(_)) | + (&ty::TyInfer(ty::InferTy::FloatVar(_)), + &ty::TyInfer(ty::InferTy::FloatVar(_))) => true, + _ => false, + } + } + + fn push_ty_ref<'tcx>(r: &ty::Region<'tcx>, + tnm: &ty::TypeAndMut<'tcx>, + s: &mut DiagnosticStyledString) { + let r = &format!("{}", r); + s.push_highlighted(format!("&{}{}{}", + r, + if r == "" { + "" + } else { + " " + }, + if tnm.mutbl == hir::MutMutable { + "mut " + } else { + "" + })); + s.push_normal(format!("{}", tnm.ty)); + } + match (&t1.sty, &t2.sty) { (&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => { let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); @@ -653,6 +705,29 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { DiagnosticStyledString::highlighted(format!("{}", t2))) } } + + // When finding T != &T, hightlight only the borrow + (&ty::TyRef(r1, ref tnm1), _) if equals(&tnm1.ty, &t2) => { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + push_ty_ref(&r1, tnm1, &mut values.0); + values.1.push_normal(format!("{}", t2)); + values + } + (_, &ty::TyRef(r2, ref tnm2)) if equals(&t1, &tnm2.ty) => { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + values.0.push_normal(format!("{}", t1)); + push_ty_ref(&r2, tnm2, &mut values.1); + values + } + + // When encountering &T != &mut T, highlight only the borrow + (&ty::TyRef(r1, ref tnm1), &ty::TyRef(r2, ref tnm2)) if equals(&tnm1.ty, &tnm2.ty) => { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + push_ty_ref(&r1, tnm1, &mut values.0); + push_ty_ref(&r2, tnm2, &mut values.1); + values + } + _ => { if t1 == t2 { // The two types are the same, elide and don't highlight. @@ -670,17 +745,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { diag: &mut DiagnosticBuilder<'tcx>, cause: &ObligationCause<'tcx>, secondary_span: Option<(Span, String)>, - values: Option>, + mut values: Option>, terr: &TypeError<'tcx>) { - let (expected_found, is_simple_error) = match values { - None => (None, false), + // For some types of errors, expected-found does not make + // sense, so just ignore the values we were given. + match terr { + TypeError::CyclicTy(_) => { values = None; } + _ => { } + } + + let (expected_found, exp_found, is_simple_error) = match values { + None => (None, None, false), Some(values) => { - let is_simple_error = match values { + let (is_simple_error, exp_found) = match values { ValuePairs::Types(exp_found) => { - exp_found.expected.is_primitive() && exp_found.found.is_primitive() + let is_simple_err = exp_found.expected.is_primitive() + && exp_found.found.is_primitive(); + + (is_simple_err, Some(exp_found)) } - _ => false, + _ => (false, None), }; let vals = match self.values_str(&values) { Some((expected, found)) => Some((expected, found)), @@ -690,12 +775,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { return } }; - (vals, is_simple_error) + (vals, exp_found, is_simple_error) } }; let span = cause.span; + diag.span_label(span, terr.to_string()); + if let Some((sp, msg)) = secondary_span { + diag.span_label(sp, msg); + } + if let Some((expected, found)) = expected_found { match (terr, is_simple_error, expected == found) { (&TypeError::Sorts(ref values), false, true) => { @@ -704,21 +794,43 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { &format!(" ({})", values.expected.sort_string(self.tcx)), &format!(" ({})", values.found.sort_string(self.tcx))); } - (_, false, _) => { + (_, false, _) => { + if let Some(exp_found) = exp_found { + let (def_id, ret_ty) = match exp_found.found.sty { + TypeVariants::TyFnDef(def, _) => { + (Some(def), Some(self.tcx.fn_sig(def).output())) + } + _ => (None, None) + }; + + let exp_is_struct = match exp_found.expected.sty { + TypeVariants::TyAdt(def, _) => def.is_struct(), + _ => false + }; + + if let (Some(def_id), Some(ret_ty)) = (def_id, ret_ty) { + if exp_is_struct && exp_found.expected == ret_ty.0 { + let message = format!( + "did you mean `{}(/* fields */)`?", + self.tcx.item_path_str(def_id) + ); + diag.span_label(cause.span, message); + } + } + } + diag.note_expected_found(&"type", expected, found); } _ => (), } } - diag.span_label(span, terr.to_string()); - if let Some((sp, msg)) = secondary_span { - diag.span_label(sp, msg); - } - - self.note_error_origin(diag, &cause); self.check_and_note_conflicting_crates(diag, terr, span); self.tcx.note_and_explain_type_err(diag, terr, span); + + // It reads better to have the error origin as the final + // thing. + self.note_error_origin(diag, &cause); } pub fn report_and_explain_type_error(&self, @@ -726,18 +838,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { terr: &TypeError<'tcx>) -> DiagnosticBuilder<'tcx> { + debug!("report_and_explain_type_error(trace={:?}, terr={:?})", + trace, + terr); + let span = trace.cause.span; - let failure_str = trace.cause.as_failure_str(); - let mut diag = match trace.cause.code { - ObligationCauseCode::IfExpressionWithNoElse => { + let failure_code = trace.cause.as_failure_code(terr); + let mut diag = match failure_code { + FailureCode::Error0317(failure_str) => { struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) } - ObligationCauseCode::MainFunctionType => { + FailureCode::Error0580(failure_str) => { struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) } - _ => { + FailureCode::Error0308(failure_str) => { struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str) } + FailureCode::Error0644(failure_str) => { + struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) + } }; self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr); diag @@ -794,8 +913,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let generics = self.tcx.generics_of(did); // Account for the case where `did` corresponds to `Self`, which doesn't have // the expected type argument. - if generics.types.len() > 0 { - let type_param = generics.type_param(param); + if !param.is_self() { + let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir; hir.as_local_node_id(type_param.def_id).map(|id| { // Get the `hir::TyParam` to verify wether it already has any bounds. @@ -832,14 +951,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }; if let SubregionOrigin::CompareImplMethodObligation { - span, item_name, impl_item_def_id, trait_item_def_id, lint_id + span, item_name, impl_item_def_id, trait_item_def_id, } = origin { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", bound_kind, sub), - lint_id) + &format!("`{}: {}`", bound_kind, sub)) .emit(); return; } @@ -978,6 +1096,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let var_name = self.tcx.hir.name(var_node_id); format!(" for capture of `{}` by closure", var_name) } + infer::NLL(..) => bug!("NLL variable found in lexical phase"), }; struct_span_err!(self.tcx.sess, var_origin.span(), E0495, @@ -987,23 +1106,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } +enum FailureCode { + Error0317(&'static str), + Error0580(&'static str), + Error0308(&'static str), + Error0644(&'static str), +} + impl<'tcx> ObligationCause<'tcx> { - fn as_failure_str(&self) -> &'static str { + fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { + use self::FailureCode::*; use traits::ObligationCauseCode::*; match self.code { - CompareImplMethodObligation { .. } => "method not compatible with trait", - MatchExpressionArm { source, .. } => match source { + CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), + MatchExpressionArm { source, .. } => Error0308(match source { hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types", _ => "match arms have incompatible types", - }, - IfExpression => "if and else have incompatible types", - IfExpressionWithNoElse => "if may be missing an else clause", - EquatePredicate => "equality predicate not satisfied", - MainFunctionType => "main function has wrong type", - StartFunctionType => "start function has wrong type", - IntrinsicType => "intrinsic has wrong type", - MethodReceiver => "mismatched method receiver", - _ => "mismatched types", + }), + IfExpression => Error0308("if and else have incompatible types"), + IfExpressionWithNoElse => Error0317("if may be missing an else clause"), + EquatePredicate => Error0308("equality predicate not satisfied"), + MainFunctionType => Error0580("main function has wrong type"), + StartFunctionType => Error0308("start function has wrong type"), + IntrinsicType => Error0308("intrinsic has wrong type"), + MethodReceiver => Error0308("mismatched method receiver"), + + // In the case where we have no more specific thing to + // say, also take a look at the error code, maybe we can + // tailor to that. + _ => match terr { + TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => + Error0644("closure/generator type that references itself"), + _ => + Error0308("mismatched types"), + } } } diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs index a3bbdab497a9b..6af7415ba5371 100644 --- a/src/librustc/infer/error_reporting/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs @@ -11,23 +11,21 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. use infer::InferCtxt; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use infer::lexical_region_resolve::RegionResolutionError; use ty; impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - // This method generates the error message for the case when - // the function arguments consist of a named region and an anonymous - // region and corresponds to `ConcreteFailure(..)` + /// When given a `ConcreteFailure` for a function with arguments containing a named region and + /// an anonymous region, emit an descriptive diagnostic error. pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool { let (span, sub, sup) = match *error { ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), + SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup), _ => return false, // inapplicable }; - debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})", - sub, - sup); + debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})", sub, sup); // Determine whether the sub and sup consist of one named region ('a) // and one anonymous (elided) region. If so, find the parameter arg @@ -35,15 +33,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // only introduced anonymous regions in parameters) as well as a // version new_ty of its type where the anonymous region is replaced // with the named one.//scope_def_id - let (named, anon_arg_info, region_info) = + let (named, anon, anon_arg_info, region_info) = if self.is_named_region(sub) && self.is_suitable_region(sup).is_some() && self.find_arg_with_region(sup, sub).is_some() { (sub, + sup, self.find_arg_with_region(sup, sub).unwrap(), self.is_suitable_region(sup).unwrap()) } else if self.is_named_region(sup) && self.is_suitable_region(sub).is_some() && self.find_arg_with_region(sub, sup).is_some() { (sup, + sub, self.find_arg_with_region(sub, sup).unwrap(), self.is_suitable_region(sub).unwrap()) } else { @@ -51,10 +51,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }; debug!("try_report_named_anon_conflict: named = {:?}", named); - debug!("try_report_named_anon_conflict: anon_arg_info = {:?}", - anon_arg_info); - debug!("try_report_named_anon_conflict: region_info = {:?}", - region_info); + debug!("try_report_named_anon_conflict: anon_arg_info = {:?}", anon_arg_info); + debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg, anon_arg_info.arg_ty, @@ -76,33 +74,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { return false; } - if self.is_return_type_anon(scope_def_id, br) { - debug!("try_report_named_anon_conflict: is_return_type_anon({:?}, {:?}) = true", - scope_def_id, - br); - return false; - } else if self.is_self_anon(is_first, scope_def_id) { - debug!("try_report_named_anon_conflict: is_self_anon({:?}, {:?}) = true", - is_first, - scope_def_id); - return false; + if let Some((_, fndecl)) = self.find_anon_type(anon, &br) { + if self.is_return_type_anon(scope_def_id, br, fndecl).is_some() || + self.is_self_anon(is_first, scope_def_id) { + return false; + } + } + + let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() { + (format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name)) } else { - let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() { - (format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name)) - } else { - ("parameter type".to_owned(), "type".to_owned()) - }; + ("parameter type".to_owned(), "type".to_owned()) + }; - struct_span_err!(self.tcx.sess, - span, - E0621, - "explicit lifetime required in {}", - error_var) - .span_label(arg.pat.span, - format!("consider changing {} to `{}`", span_label_var, new_ty)) - .span_label(span, format!("lifetime `{}` required", named)) - .emit(); - return true; - } + struct_span_err!(self.tcx.sess, + span, + E0621, + "explicit lifetime required in {}", + error_var) + .span_label(arg.pat.span, + format!("consider changing {} to `{}`", span_label_var, new_ty)) + .span_label(span, format!("lifetime `{}` required", named)) + .emit(); + return true; } } diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 22d9a9e313b77..ea3c0a8ddb450 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -125,9 +125,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // ``` labels.clear(); labels.push((pattern.span, format!("consider giving this closure parameter a type"))); - } - - if let Some(pattern) = local_visitor.found_local_pattern { + } else if let Some(pattern) = local_visitor.found_local_pattern { if let Some(simple_name) = pattern.simple_name() { labels.push((pattern.span, format!("consider giving `{}` a type", simple_name))); } else { diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs index 1f0fd7b01d37d..e46613b3e4da0 100644 --- a/src/librustc/infer/error_reporting/note.rs +++ b/src/librustc/infer/error_reporting/note.rs @@ -445,14 +445,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { infer::CompareImplMethodObligation { span, item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", sup, sub), - lint_id) + &format!("`{}: {}`", sup, sub)) } } } diff --git a/src/librustc/infer/error_reporting/util.rs b/src/librustc/infer/error_reporting/util.rs index 94faec464b244..6bcd98a7a6814 100644 --- a/src/librustc/infer/error_reporting/util.rs +++ b/src/librustc/infer/error_reporting/util.rs @@ -15,6 +15,7 @@ use infer::InferCtxt; use ty::{self, Region, Ty}; use hir::def_id::DefId; use hir::map as hir_map; +use syntax_pos::Span; macro_rules! or_false { ($v:expr) => { @@ -163,7 +164,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Here, we check for the case where the anonymous region // is in the return type. // FIXME(#42703) - Need to handle certain cases here. - pub fn is_return_type_anon(&self, scope_def_id: DefId, br: ty::BoundRegion) -> bool { + pub fn is_return_type_anon(&self, + scope_def_id: DefId, + br: ty::BoundRegion, + decl: &hir::FnDecl) + -> Option { let ret_ty = self.tcx.type_of(scope_def_id); match ret_ty.sty { ty::TyFnDef(_, _) => { @@ -171,12 +176,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let late_bound_regions = self.tcx .collect_referenced_late_bound_regions(&sig.output()); if late_bound_regions.iter().any(|r| *r == br) { - return true; + return Some(decl.output.span()); } } _ => {} } - false + None } // Here we check for the case where anonymous region // corresponds to self and if yes, we display E0312. @@ -216,6 +221,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { _ => false, } } + ty::ReEarlyBound(_) => true, _ => false, } } diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index c274f8bda9fb0..426c61e9ac083 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -43,9 +43,7 @@ use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::TypeFolder; -use ty::subst::Substs; use util::nodemap::FxHashMap; -use hir::def_id::DefId; use std::collections::hash_map::Entry; @@ -56,7 +54,6 @@ pub struct TypeFreshener<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, freshen_count: u32, freshen_map: FxHashMap>, - closure_set: Vec, } impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> { @@ -66,7 +63,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> { infcx, freshen_count: 0, freshen_map: FxHashMap(), - closure_set: vec![], } } @@ -92,88 +88,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> { } } } - - fn next_fresh(&mut self, - freshener: F) - -> Ty<'tcx> - where F: FnOnce(u32) -> ty::InferTy, - { - let index = self.freshen_count; - self.freshen_count += 1; - self.infcx.tcx.mk_infer(freshener(index)) - } - - fn freshen_closure_like(&mut self, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - t: Ty<'tcx>, - markers: M, - combine: C) - -> Ty<'tcx> - where M: FnOnce(&mut Self) -> (Ty<'tcx>, Ty<'tcx>), - C: FnOnce(&'tcx Substs<'tcx>) -> Ty<'tcx> - { - let tcx = self.infcx.tcx; - - let closure_in_progress = self.infcx.in_progress_tables.map_or(false, |tables| { - tcx.hir.as_local_node_id(def_id).map_or(false, |closure_id| { - tables.borrow().local_id_root == - Some(DefId::local(tcx.hir.node_to_hir_id(closure_id).owner)) - }) - }); - - if !closure_in_progress { - // If this closure belongs to another infcx, its kind etc. were - // fully inferred and its signature/kind are exactly what's listed - // in its infcx. So we don't need to add the markers for them. - return t.super_fold_with(self); - } - - // We are encoding a closure in progress. Because we want our freshening - // key to contain all inference information needed to make sense of our - // value, we need to encode the closure signature and kind. The way - // we do that is to add them as 2 variables to the closure substs, - // basically because it's there (and nobody cares about adding extra stuff - // to substs). - // - // This means the "freshened" closure substs ends up looking like - // fresh_substs = [PARENT_SUBSTS* ; UPVARS* ; SIG_MARKER ; KIND_MARKER] - let (marker_1, marker_2) = if self.closure_set.contains(&def_id) { - // We found the closure def-id within its own signature. Just - // leave a new freshened type - any matching operations would - // have found and compared the exterior closure already to - // get here. - // - // In that case, we already know what the signature would - // be - the parent closure on the stack already contains a - // "copy" of the signature, so there is no reason to encode - // it again for injectivity. Just use a fresh type variable - // to make everything comparable. - // - // For example (closure kinds omitted for clarity) - // t=[closure FOO sig=[closure BAR sig=[closure FOO ..]]] - // Would get encoded to - // t=[closure FOO sig=[closure BAR sig=[closure FOO sig=$0]]] - // - // and we can decode by having - // $0=[closure BAR {sig doesn't exist in decode}] - // and get - // t=[closure FOO] - // sig[FOO] = [closure BAR] - // sig[BAR] = [closure FOO] - (self.next_fresh(ty::FreshTy), self.next_fresh(ty::FreshTy)) - } else { - self.closure_set.push(def_id); - let markers = markers(self); - self.closure_set.pop(); - markers - }; - - combine(tcx.mk_substs( - substs.substs.iter().map(|k| k.fold_with(self)).chain( - [marker_1, marker_2].iter().cloned().map(From::from) - ))) - } } impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { @@ -249,51 +163,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { t } - ty::TyClosure(def_id, substs) => { - self.freshen_closure_like( - def_id, substs, t, - |this| { - // HACK: use a "random" integer type to mark the kind. Because - // different closure kinds shouldn't get unified during - // selection, the "subtyping" relationship (where any kind is - // better than no kind) shouldn't matter here, just that the - // types are different. - let closure_kind = this.infcx.closure_kind(def_id); - let closure_kind_marker = match closure_kind { - None => tcx.types.i8, - Some(ty::ClosureKind::Fn) => tcx.types.i16, - Some(ty::ClosureKind::FnMut) => tcx.types.i32, - Some(ty::ClosureKind::FnOnce) => tcx.types.i64, - }; - - let closure_sig = this.infcx.fn_sig(def_id); - (tcx.mk_fn_ptr(closure_sig.fold_with(this)), - closure_kind_marker) - }, - |substs| tcx.mk_closure(def_id, substs) - ) - } - - ty::TyGenerator(def_id, substs, interior) => { - self.freshen_closure_like( - def_id, substs, t, - |this| { - let gen_sig = this.infcx.generator_sig(def_id).unwrap(); - // FIXME: want to revise this strategy when generator - // signatures can actually contain LBRs. - let sig = this.tcx().no_late_bound_regions(&gen_sig) - .unwrap_or_else(|| { - bug!("late-bound regions in signature of {:?}", - def_id) - }); - (sig.yield_ty, sig.return_ty).fold_with(this) - }, - |substs| { - tcx.mk_generator(def_id, ty::ClosureSubsts { substs }, interior) - } - ) - } - + ty::TyGenerator(..) | ty::TyBool | ty::TyChar | ty::TyInt(..) | @@ -312,7 +182,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::TyNever | ty::TyTuple(..) | ty::TyProjection(..) | + ty::TyForeign(..) | ty::TyParam(..) | + ty::TyClosure(..) | ty::TyAnon(..) => { t.super_fold_with(self) } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 9cad6ce6f9fad..756a6947ee3f8 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -78,8 +78,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.type_variables.borrow_mut().types_created_since_snapshot( &snapshot.type_snapshot); let region_vars = - self.region_vars.vars_created_since_snapshot( - &snapshot.region_vars_snapshot); + self.borrow_region_constraints().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); Ok((type_variables, region_vars, value)) } diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index d7afeba7dc96b..fd14e0e40e234 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -15,6 +15,7 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; +use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Greatest lower bound" (common subtype) @@ -67,14 +68,39 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.glb_regions(origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> { - self.fields.higher_ranked_glb(a, b, self.a_is_expected) + debug!("binders(a={:?}, b={:?})", a, b); + let was_error = self.infcx().probe(|_snapshot| { + // Subtle: use a fresh combine-fields here because we recover + // from Err. Doing otherwise could propagate obligations out + // through our `self.obligations` field. + self.infcx() + .combine_fields(self.fields.trace.clone(), self.fields.param_env) + .higher_ranked_glb(a, b, self.a_is_expected) + .is_err() + }); + debug!("binders: was_error={:?}", was_error); + + // When higher-ranked types are involved, computing the LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + match self.relate_with_variance(ty::Variance::Invariant, a, b) { + Ok(_) => Ok(a.clone()), + Err(err) => { + debug!("binders: error occurred, was_error={:?}", was_error); + if !was_error { + Err(TypeError::OldStyleLUB(Box::new(err))) + } else { + Err(err) + } + } + } } } diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 0d02420457e6b..57e237fb9137f 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -17,8 +17,9 @@ use super::{CombinedSnapshot, SubregionOrigin, SkolemizationMap}; use super::combine::CombineFields; -use super::region_inference::{TaintDirections}; +use super::region_constraints::{TaintDirections}; +use std::collections::BTreeMap; use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; @@ -155,7 +156,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&&r| !skol_resolution_map.contains_key(r)) .cloned() .next() - .expect("no representative region"); + .unwrap_or_else(|| { + bug!("no representative region for `{:?}` in `{:?}`", + skol, regions) + }); (skol, representative) }) @@ -173,9 +177,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&r| r != representative) { let origin = SubregionOrigin::Subtype(self.trace.clone()); - self.infcx.region_vars.make_eqregion(origin, - *representative, - *region); + self.infcx.borrow_region_constraints() + .make_eqregion(origin, + *representative, + *region); } } @@ -242,7 +247,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { snapshot: &CombinedSnapshot, debruijn: ty::DebruijnIndex, new_vars: &[ty::RegionVid], - a_map: &FxHashMap>, + a_map: &BTreeMap>, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { // Regions that pre-dated the LUB computation stay as they are. @@ -338,7 +343,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { snapshot: &CombinedSnapshot, debruijn: ty::DebruijnIndex, new_vars: &[ty::RegionVid], - a_map: &FxHashMap>, + a_map: &BTreeMap>, a_vars: &[ty::RegionVid], b_vars: &[ty::RegionVid], r0: ty::Region<'tcx>) @@ -407,7 +412,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn rev_lookup<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, span: Span, - a_map: &FxHashMap>, + a_map: &BTreeMap>, r: ty::Region<'tcx>) -> ty::Region<'tcx> { for (a_br, a_r) in a_map { @@ -424,13 +429,13 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_vars.new_bound(debruijn) + infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn) } } } fn var_ids<'a, 'gcx, 'tcx>(fields: &CombineFields<'a, 'gcx, 'tcx>, - map: &FxHashMap>) + map: &BTreeMap>) -> Vec { map.iter() .map(|(_, &r)| match *r { @@ -478,7 +483,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions) + self.borrow_region_constraints().tainted( + self.tcx, + &snapshot.region_constraints_snapshot, + r, + directions) } fn region_vars_confined_to_snapshot(&self, @@ -536,7 +545,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { */ let mut region_vars = - self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot); + self.borrow_region_constraints().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); let escaping_types = self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot); @@ -578,7 +588,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot) + self.borrow_region_constraints() + .push_skolemized(self.tcx, br, &snapshot.region_constraints_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -763,7 +774,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot); + self.borrow_region_constraints() + .pop_skolemized(self.tcx, &skol_regions, &snapshot.region_constraints_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/region_inference/README.md b/src/librustc/infer/lexical_region_resolve/README.md similarity index 77% rename from src/librustc/infer/region_inference/README.md rename to src/librustc/infer/lexical_region_resolve/README.md index b564faf3d0c24..a90230870a6c0 100644 --- a/src/librustc/infer/region_inference/README.md +++ b/src/librustc/infer/lexical_region_resolve/README.md @@ -1,10 +1,13 @@ -Region inference +# Region inference -# Terminology +## Terminology Note that we use the terms region and lifetime interchangeably. -# Introduction +## Introduction + +See the [general inference README](../README.md) for an overview of +how lexical-region-solving fits into the bigger picture. Region inference uses a somewhat more involved algorithm than type inference. It is not the most efficient thing ever written though it @@ -16,63 +19,6 @@ it's worth spending more time on a more involved analysis. Moreover, regions are a simpler case than types: they don't have aggregate structure, for example. -Unlike normal type inference, which is similar in spirit to H-M and thus -works progressively, the region type inference works by accumulating -constraints over the course of a function. Finally, at the end of -processing a function, we process and solve the constraints all at -once. - -The constraints are always of one of three possible forms: - -- `ConstrainVarSubVar(Ri, Rj)` states that region variable Ri must be - a subregion of Rj -- `ConstrainRegSubVar(R, Ri)` states that the concrete region R (which - must not be a variable) must be a subregion of the variable Ri -- `ConstrainVarSubReg(Ri, R)` states the variable Ri shoudl be less - than the concrete region R. This is kind of deprecated and ought to - be replaced with a verify (they essentially play the same role). - -In addition to constraints, we also gather up a set of "verifys" -(what, you don't think Verify is a noun? Get used to it my -friend!). These represent relations that must hold but which don't -influence inference proper. These take the form of: - -- `VerifyRegSubReg(Ri, Rj)` indicates that Ri <= Rj must hold, - where Rj is not an inference variable (and Ri may or may not contain - one). This doesn't influence inference because we will already have - inferred Ri to be as small as possible, so then we just test whether - that result was less than Rj or not. -- `VerifyGenericBound(R, Vb)` is a more complex expression which tests - that the region R must satisfy the bound `Vb`. The bounds themselves - may have structure like "must outlive one of the following regions" - or "must outlive ALL of the following regions. These bounds arise - from constraints like `T: 'a` -- if we know that `T: 'b` and `T: 'c` - (say, from where clauses), then we can conclude that `T: 'a` if `'b: - 'a` *or* `'c: 'a`. - -# Building up the constraints - -Variables and constraints are created using the following methods: - -- `new_region_var()` creates a new, unconstrained region variable; -- `make_subregion(Ri, Rj)` states that Ri is a subregion of Rj -- `lub_regions(Ri, Rj) -> Rk` returns a region Rk which is - the smallest region that is greater than both Ri and Rj -- `glb_regions(Ri, Rj) -> Rk` returns a region Rk which is - the greatest region that is smaller than both Ri and Rj - -The actual region resolution algorithm is not entirely -obvious, though it is also not overly complex. - -## Snapshotting - -It is also permitted to try (and rollback) changes to the graph. This -is done by invoking `start_snapshot()`, which returns a value. Then -later you can call `rollback_to()` which undoes the work. -Alternatively, you can call `commit()` which ends all snapshots. -Snapshots can be recursive---so you can start a snapshot when another -is in progress, but only the root snapshot can "commit". - ## The problem Basically our input is a directed graph where nodes can be divided @@ -109,9 +55,9 @@ step where we walk over the verify bounds and check that they are satisfied. These bounds represent the "maximal" values that a region variable can take on, basically. -# The Region Hierarchy +## The Region Hierarchy -## Without closures +### Without closures Let's first consider the region hierarchy without thinking about closures, because they add a lot of complications. The region @@ -141,7 +87,7 @@ Within that, there are sublifetimes for the assignment pattern and also the expression `x + y`. The expression itself has sublifetimes for evaluating `x` and `y`. -## Function calls +#s## Function calls Function calls are a bit tricky. I will describe how we handle them *now* and then a bit about how we can improve them (Issue #6268). @@ -259,7 +205,7 @@ there is a reference created whose lifetime does not enclose the borrow expression, we must issue sufficient restrictions to ensure that the pointee remains valid. -## Modeling closures +### Modeling closures Integrating closures properly into the model is a bit of work-in-progress. In an ideal world, we would model closures as @@ -314,8 +260,3 @@ handling of closures, there are no known cases where this leads to a type-checking accepting incorrect code (though it sometimes rejects what might be considered correct code; see rust-lang/rust#22557), but it still doesn't feel like the right approach. - -### Skolemization - -For a discussion on skolemization and higher-ranked subtyping, please -see the module `middle::infer::higher_ranked::doc`. diff --git a/src/librustc/infer/region_inference/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs similarity index 87% rename from src/librustc/infer/region_inference/graphviz.rs rename to src/librustc/infer/lexical_region_resolve/graphviz.rs index 5cf6aa350bdd5..4120948739578 100644 --- a/src/librustc/infer/region_inference/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -9,7 +9,7 @@ // except according to those terms. //! This module provides linkage between libgraphviz traits and -//! `rustc::middle::typeck::infer::region_inference`, generating a +//! `rustc::middle::typeck::infer::region_constraints`, generating a //! rendering of the graph represented by the list of `Constraint` //! instances (which make up the edges of the graph), as well as the //! origin for each constraint (which are attached to the labels on @@ -19,16 +19,18 @@ use graphviz as dot; use hir::def_id::DefIndex; +use rustc_data_structures::indexed_vec::Idx; use ty; use middle::free_region::RegionRelations; use middle::region; use super::Constraint; use infer::SubregionOrigin; -use infer::region_inference::RegionVarBindings; +use infer::region_constraints::RegionConstraintData; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; use std::collections::hash_map::Entry::Vacant; +use std::collections::btree_map::BTreeMap; use std::env; use std::fs::File; use std::io; @@ -55,12 +57,13 @@ graphs will be printed. \n\ } pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( - region_vars: &RegionVarBindings<'a, 'gcx, 'tcx>, + region_data: &RegionConstraintData<'tcx>, region_rels: &RegionRelations<'a, 'gcx, 'tcx>) { + let tcx = region_rels.tcx; let context = region_rels.context; - if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph { + if !tcx.sess.opts.debugging_opts.print_region_graph { return; } @@ -110,12 +113,11 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( } }; - let constraints = &*region_vars.constraints.borrow(); - match dump_region_constraints_to(region_rels, constraints, &output_path) { + match dump_region_data_to(region_rels, ®ion_data.constraints, &output_path) { Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); - region_vars.tcx.sess.err(&msg) + tcx.sess.err(&msg) } } } @@ -123,7 +125,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { graph_name: String, region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>, - map: &'a FxHashMap, SubregionOrigin<'tcx>>, + map: &'a BTreeMap, SubregionOrigin<'tcx>>, node_ids: FxHashMap, } @@ -210,13 +212,13 @@ impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { fn constraint_to_nodes(c: &Constraint) -> (Node, Node) { match *c { - Constraint::ConstrainVarSubVar(rv_1, rv_2) => + Constraint::VarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1), Node::RegionVid(rv_2)), - Constraint::ConstrainRegSubVar(r_1, rv_2) => + Constraint::RegSubVar(r_1, rv_2) => (Node::Region(*r_1), Node::RegionVid(rv_2)), - Constraint::ConstrainVarSubReg(rv_1, r_2) => + Constraint::VarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), Node::Region(*r_2)), - Constraint::ConstrainRegSubReg(r_1, r_2) => + Constraint::RegSubReg(r_1, r_2) => (Node::Region(*r_1), Node::Region(*r_2)), } } @@ -263,17 +265,17 @@ impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { } } -pub type ConstraintMap<'tcx> = FxHashMap, SubregionOrigin<'tcx>>; +pub type ConstraintMap<'tcx> = BTreeMap, SubregionOrigin<'tcx>>; -fn dump_region_constraints_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - map: &ConstraintMap<'tcx>, - path: &str) - -> io::Result<()> { - debug!("dump_region_constraints map (len: {}) path: {}", +fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + map: &ConstraintMap<'tcx>, + path: &str) + -> io::Result<()> { + debug!("dump_region_data map (len: {}) path: {}", map.len(), path); - let g = ConstraintGraph::new(format!("region_constraints"), region_rels, map); - debug!("dump_region_constraints calling render"); + let g = ConstraintGraph::new(format!("region_data"), region_rels, map); + debug!("dump_region_data calling render"); let mut v = Vec::new(); dot::render(&g, &mut v).unwrap(); File::create(path).and_then(|mut f| f.write_all(&v)) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs new file mode 100644 index 0000000000000..5a4f2157298b0 --- /dev/null +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -0,0 +1,766 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The code to do lexical region resolution. + +use infer::SubregionOrigin; +use infer::RegionVariableOrigin; +use infer::region_constraints::Constraint; +use infer::region_constraints::GenericKind; +use infer::region_constraints::RegionConstraintData; +use infer::region_constraints::VarOrigins; +use infer::region_constraints::VerifyBound; +use middle::free_region::RegionRelations; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; +use std::fmt; +use std::u32; +use ty::{self, TyCtxt}; +use ty::{Region, RegionVid}; +use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; + +mod graphviz; + +/// This function performs lexical region resolution given a complete +/// set of constraints and variable origins. It performs a fixed-point +/// iteration to find region values which satisfy all constraints, +/// assuming such values can be found. It returns the final values of +/// all the variables as well as a set of errors that must be reported. +pub fn resolve<'tcx>( + region_rels: &RegionRelations<'_, '_, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx>, +) -> ( + LexicalRegionResolutions<'tcx>, + Vec>, +) { + debug!("RegionConstraintData: resolve_regions()"); + let mut errors = vec![]; + let mut resolver = LexicalResolver { + region_rels, + var_origins, + data, + }; + let values = resolver.infer_variable_values(&mut errors); + (values, errors) +} + +/// Contains the result of lexical region resolution. Offers methods +/// to lookup up the final value of a region variable. +pub struct LexicalRegionResolutions<'tcx> { + values: IndexVec>, + error_region: ty::Region<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum VarValue<'tcx> { + Value(Region<'tcx>), + ErrorValue, +} + +#[derive(Clone, Debug)] +pub enum RegionResolutionError<'tcx> { + /// `ConcreteFailure(o, a, b)`: + /// + /// `o` requires that `a <= b`, but this does not hold + ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), + + /// `GenericBoundFailure(p, s, a) + /// + /// The parameter/associated-type `p` must be known to outlive the lifetime + /// `a` (but none of the known bounds are sufficient). + GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), + + /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: + /// + /// Could not infer a value for `v` because `sub_r <= v` (due to + /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and + /// `sub_r <= sup_r` does not hold. + SubSupConflict( + RegionVariableOrigin, + SubregionOrigin<'tcx>, + Region<'tcx>, + SubregionOrigin<'tcx>, + Region<'tcx>, + ), +} + +struct RegionAndOrigin<'tcx> { + region: Region<'tcx>, + origin: SubregionOrigin<'tcx>, +} + +type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; + +struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + region_rels: &'cx RegionRelations<'cx, 'gcx, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { + fn infer_variable_values( + &mut self, + errors: &mut Vec>, + ) -> LexicalRegionResolutions<'tcx> { + let mut var_data = self.construct_var_data(self.region_rels.tcx); + + // Dorky hack to cause `dump_constraints` to only get called + // if debug mode is enabled: + debug!( + "----() End constraint listing (context={:?}) {:?}---", + self.region_rels.context, + self.dump_constraints(self.region_rels) + ); + graphviz::maybe_print_constraints_for(&self.data, self.region_rels); + + let graph = self.construct_graph(); + self.expand_givens(&graph); + self.expansion(&mut var_data); + self.collect_errors(&mut var_data, errors); + self.collect_var_errors(&var_data, &graph, errors); + var_data + } + + fn num_vars(&self) -> usize { + self.var_origins.len() + } + + /// Initially, the value for all variables is set to `'empty`, the + /// empty region. The `expansion` phase will grow this larger. + fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: tcx.types.re_static, + values: (0..self.num_vars()) + .map(|_| VarValue::Value(tcx.types.re_empty)) + .collect(), + } + } + + fn dump_constraints(&self, free_regions: &RegionRelations<'_, '_, 'tcx>) { + debug!( + "----() Start constraint listing (context={:?}) ()----", + free_regions.context + ); + for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { + debug!("Constraint {} => {:?}", idx, constraint); + } + } + + fn expand_givens(&mut self, graph: &RegionGraph) { + // Givens are a kind of horrible hack to account for + // constraints like 'c <= '0 that are known to hold due to + // closure signatures (see the comment above on the `givens` + // field). They should go away. But until they do, the role + // of this fn is to account for the transitive nature: + // + // Given 'c <= '0 + // and '0 <= '1 + // then 'c <= '1 + + let seeds: Vec<_> = self.data.givens.iter().cloned().collect(); + for (r, vid) in seeds { + // While all things transitively reachable in the graph + // from the variable (`'0` in the example above). + let seed_index = NodeIndex(vid.index() as usize); + for succ_index in graph.depth_traverse(seed_index, OUTGOING) { + let succ_index = succ_index.0; + + // The first N nodes correspond to the region + // variables. Other nodes correspond to constant + // regions. + if succ_index < self.num_vars() { + let succ_vid = RegionVid::new(succ_index); + + // Add `'c <= '1`. + self.data.givens.insert((r, succ_vid)); + } + } + } + } + + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { + self.iterate_until_fixed_point("Expansion", |constraint, origin| { + debug!("expansion: constraint={:?} origin={:?}", constraint, origin); + match *constraint { + Constraint::RegSubVar(a_region, b_vid) => { + let b_data = var_values.value_mut(b_vid); + self.expand_node(a_region, b_vid, b_data) + } + Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { + VarValue::ErrorValue => false, + VarValue::Value(a_region) => { + let b_node = var_values.value_mut(b_vid); + self.expand_node(a_region, b_vid, b_node) + } + }, + Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { + // These constraints are checked after expansion + // is done, in `collect_errors`. + false + } + } + }) + } + + fn expand_node( + &self, + a_region: Region<'tcx>, + b_vid: RegionVid, + b_data: &mut VarValue<'tcx>, + ) -> bool { + debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data); + + // Check if this relationship is implied by a given. + match *a_region { + ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid)) + { + debug!("given"); + return false; + }, + _ => {} + } + + match *b_data { + VarValue::Value(cur_region) => { + let lub = self.lub_concrete_regions(a_region, cur_region); + if lub == cur_region { + return false; + } + + debug!( + "Expanding value of {:?} from {:?} to {:?}", + b_vid, + cur_region, + lub + ); + + *b_data = VarValue::Value(lub); + return true; + } + + VarValue::ErrorValue => { + return false; + } + } + } + + + fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { + let tcx = self.region_rels.tcx; + match (a, b) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + bug!("cannot relate region: LUB({:?}, {:?})", a, b); + } + + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + (&ReEmpty, r) | (r, &ReEmpty) => { + r // everything lives longer than empty + } + + (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { + span_bug!( + self.var_origins[v_id].span(), + "lub_concrete_regions invoked with non-concrete \ + regions: {:?}, {:?}", + a, + b + ); + } + + (&ReEarlyBound(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReEarlyBound(_)) | + (&ReFree(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReFree(_)) => { + // A "free" region can be interpreted as "some region + // at least as big as fr.scope". So, we can + // reasonably compare free regions and scopes: + let fr_scope = match (a, b) { + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels + .region_scope_tree + .early_free_scope(self.region_rels.tcx, br), + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels + .region_scope_tree + .free_scope(self.region_rels.tcx, fr), + _ => bug!(), + }; + let r_id = self.region_rels + .region_scope_tree + .nearest_common_ancestor(fr_scope, s_id); + if r_id == fr_scope { + // if the free region's scope `fr.scope` is bigger than + // the scope region `s_id`, then the LUB is the free + // region itself: + match (a, b) { + (_, &ReScope(_)) => return a, + (&ReScope(_), _) => return b, + _ => bug!(), + } + } + + // otherwise, we don't know what the free region is, + // so we must conservatively say the LUB is static: + tcx.types.re_static + } + + (&ReScope(a_id), &ReScope(b_id)) => { + // The region corresponding to an outer block is a + // subtype of the region corresponding to an inner + // block. + let lub = self.region_rels + .region_scope_tree + .nearest_common_ancestor(a_id, b_id); + tcx.mk_region(ReScope(lub)) + } + + (&ReEarlyBound(_), &ReEarlyBound(_)) | + (&ReFree(_), &ReEarlyBound(_)) | + (&ReEarlyBound(_), &ReFree(_)) | + (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), + + // For these types, we cannot define any additional + // relationship: + (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { + a + } else { + tcx.types.re_static + }, + } + } + + /// After expansion is complete, go and check upper bounds (i.e., + /// cases where the region cannot grow larger than a fixed point) + /// and check that they are satisfied. + fn collect_errors( + &self, + var_data: &mut LexicalRegionResolutions<'tcx>, + errors: &mut Vec>, + ) { + for (constraint, origin) in &self.data.constraints { + debug!( + "collect_errors: constraint={:?} origin={:?}", + constraint, + origin + ); + match *constraint { + Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { + // Expansion will ensure that these constraints hold. Ignore. + } + + Constraint::RegSubReg(sub, sup) => { + if self.region_rels.is_subregion_of(sub, sup) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + origin, + sub, + sup + ); + + errors.push(RegionResolutionError::ConcreteFailure( + (*origin).clone(), + sub, + sup, + )); + } + + Constraint::VarSubReg(a_vid, b_region) => { + let a_data = var_data.value_mut(a_vid); + debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); + + let a_region = match *a_data { + VarValue::ErrorValue => continue, + VarValue::Value(a_region) => a_region, + }; + + // Do not report these errors immediately: + // instead, set the variable value to error and + // collect them later. + if !self.region_rels.is_subregion_of(a_region, b_region) { + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?}={:?} <= {:?}", + origin, + a_vid, + a_region, + b_region + ); + *a_data = VarValue::ErrorValue; + } + } + } + } + + for verify in &self.data.verifys { + debug!("collect_errors: verify={:?}", verify); + let sub = var_data.normalize(verify.region); + + // This was an inference variable which didn't get + // constrained, therefore it can be assume to hold. + if let ty::ReEmpty = *sub { + continue; + } + + if self.bound_is_met(&verify.bound, var_data, sub) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + verify.origin, + verify.region, + verify.bound + ); + + errors.push(RegionResolutionError::GenericBoundFailure( + verify.origin.clone(), + verify.kind.clone(), + sub, + )); + } + } + + /// Go over the variables that were declared to be error variables + /// and create a `RegionResolutionError` for each of them. + fn collect_var_errors( + &self, + var_data: &LexicalRegionResolutions<'tcx>, + graph: &RegionGraph<'tcx>, + errors: &mut Vec>, + ) { + debug!("collect_var_errors"); + + // This is the best way that I have found to suppress + // duplicate and related errors. Basically we keep a set of + // flags for every node. Whenever an error occurs, we will + // walk some portion of the graph looking to find pairs of + // conflicting regions to report to the user. As we walk, we + // trip the flags from false to true, and if we find that + // we've already reported an error involving any particular + // node we just stop and don't report the current error. The + // idea is to report errors that derive from independent + // regions of the graph, but not those that derive from + // overlapping locations. + let mut dup_vec = vec![u32::MAX; self.num_vars()]; + + for (node_vid, value) in var_data.values.iter_enumerated() { + match *value { + VarValue::Value(_) => { /* Inference successful */ } + VarValue::ErrorValue => { + /* Inference impossible, this value contains + inconsistent constraints. + + I think that in this case we should report an + error now---unlike the case above, we can't + wait to see whether the user needs the result + of this variable. The reason is that the mere + existence of this variable implies that the + region graph is inconsistent, whether or not it + is used. + + For example, we may have created a region + variable that is the GLB of two other regions + which do not have a GLB. Even if that variable + is not used, it implies that those two regions + *should* have a GLB. + + At least I think this is true. It may be that + the mere existence of a conflict in a region variable + that is not used is not a problem, so if this rule + starts to create problems we'll have to revisit + this portion of the code and think hard about it. =) */ + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + } + } + } + } + + fn construct_graph(&self) -> RegionGraph<'tcx> { + let num_vars = self.num_vars(); + + let mut graph = graph::Graph::new(); + + for _ in 0..num_vars { + graph.add_node(()); + } + + // Issue #30438: two distinct dummy nodes, one for incoming + // edges (dummy_source) and another for outgoing edges + // (dummy_sink). In `dummy -> a -> b -> dummy`, using one + // dummy node leads one to think (erroneously) there exists a + // path from `b` to `a`. Two dummy nodes sidesteps the issue. + let dummy_source = graph.add_node(()); + let dummy_sink = graph.add_node(()); + + for (constraint, _) in &self.data.constraints { + match *constraint { + Constraint::VarSubVar(a_id, b_id) => { + graph.add_edge( + NodeIndex(a_id.index() as usize), + NodeIndex(b_id.index() as usize), + *constraint, + ); + } + Constraint::RegSubVar(_, b_id) => { + graph.add_edge(dummy_source, NodeIndex(b_id.index() as usize), *constraint); + } + Constraint::VarSubReg(a_id, _) => { + graph.add_edge(NodeIndex(a_id.index() as usize), dummy_sink, *constraint); + } + Constraint::RegSubReg(..) => { + // this would be an edge from `dummy_source` to + // `dummy_sink`; just ignore it. + } + } + } + + return graph; + } + + fn collect_error_for_expanding_node( + &self, + graph: &RegionGraph<'tcx>, + dup_vec: &mut [u32], + node_idx: RegionVid, + errors: &mut Vec>, + ) { + // Errors in expanding nodes result from a lower-bound that is + // not contained by an upper-bound. + let (mut lower_bounds, lower_dup) = + self.collect_concrete_regions(graph, node_idx, graph::INCOMING, dup_vec); + let (mut upper_bounds, upper_dup) = + self.collect_concrete_regions(graph, node_idx, graph::OUTGOING, dup_vec); + + if lower_dup || upper_dup { + return; + } + + // We place free regions first because we are special casing + // SubSupConflict(ReFree, ReFree) when reporting error, and so + // the user will more likely get a specific suggestion. + fn region_order_key(x: &RegionAndOrigin) -> u8 { + match *x.region { + ReEarlyBound(_) => 0, + ReFree(_) => 1, + _ => 2, + } + } + lower_bounds.sort_by_key(region_order_key); + upper_bounds.sort_by_key(region_order_key); + + for lower_bound in &lower_bounds { + for upper_bound in &upper_bounds { + if !self.region_rels + .is_subregion_of(lower_bound.region, upper_bound.region) + { + let origin = self.var_origins[node_idx].clone(); + debug!( + "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ + sup: {:?}", + origin, + node_idx, + lower_bound.region, + upper_bound.region + ); + errors.push(RegionResolutionError::SubSupConflict( + origin, + lower_bound.origin.clone(), + lower_bound.region, + upper_bound.origin.clone(), + upper_bound.region, + )); + return; + } + } + } + + span_bug!( + self.var_origins[node_idx].span(), + "collect_error_for_expanding_node() could not find \ + error for var {:?}, lower_bounds={:?}, \ + upper_bounds={:?}", + node_idx, + lower_bounds, + upper_bounds + ); + } + + fn collect_concrete_regions( + &self, + graph: &RegionGraph<'tcx>, + orig_node_idx: RegionVid, + dir: Direction, + dup_vec: &mut [u32], + ) -> (Vec>, bool) { + struct WalkState<'tcx> { + set: FxHashSet, + stack: Vec, + result: Vec>, + dup_found: bool, + } + let mut state = WalkState { + set: FxHashSet(), + stack: vec![orig_node_idx], + result: Vec::new(), + dup_found: false, + }; + state.set.insert(orig_node_idx); + + // to start off the process, walk the source node in the + // direction specified + process_edges(&self.data, &mut state, graph, orig_node_idx, dir); + + while !state.stack.is_empty() { + let node_idx = state.stack.pop().unwrap(); + + // check whether we've visited this node on some previous walk + if dup_vec[node_idx.index() as usize] == u32::MAX { + dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32; + } else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 { + state.dup_found = true; + } + + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, + node_idx + ); + + process_edges(&self.data, &mut state, graph, node_idx, dir); + } + + let WalkState { + result, dup_found, .. + } = state; + return (result, dup_found); + + fn process_edges<'tcx>( + this: &RegionConstraintData<'tcx>, + state: &mut WalkState<'tcx>, + graph: &RegionGraph<'tcx>, + source_vid: RegionVid, + dir: Direction, + ) { + debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); + + let source_node_index = NodeIndex(source_vid.index() as usize); + for (_, edge) in graph.adjacent_edges(source_node_index, dir) { + match edge.data { + Constraint::VarSubVar(from_vid, to_vid) => { + let opp_vid = if from_vid == source_vid { + to_vid + } else { + from_vid + }; + if state.set.insert(opp_vid) { + state.stack.push(opp_vid); + } + } + + Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { + state.result.push(RegionAndOrigin { + region, + origin: this.constraints.get(&edge.data).unwrap().clone(), + }); + } + + Constraint::RegSubReg(..) => panic!( + "cannot reach reg-sub-reg edge in region inference \ + post-processing" + ), + } + } + } + } + + fn iterate_until_fixed_point(&self, tag: &str, mut body: F) + where + F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool, + { + let mut iteration = 0; + let mut changed = true; + while changed { + changed = false; + iteration += 1; + debug!("---- {} Iteration {}{}", "#", tag, iteration); + for (constraint, origin) in &self.data.constraints { + let edge_changed = body(constraint, origin); + if edge_changed { + debug!("Updated due to constraint {:?}", constraint); + changed = true; + } + } + } + debug!("---- {} Complete after {} iteration(s)", tag, iteration); + } + + fn bound_is_met( + &self, + bound: &VerifyBound<'tcx>, + var_values: &LexicalRegionResolutions<'tcx>, + min: ty::Region<'tcx>, + ) -> bool { + match bound { + VerifyBound::AnyRegion(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) + .any(|r| self.region_rels.is_subregion_of(min, r)), + + VerifyBound::AllRegions(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) + .all(|r| self.region_rels.is_subregion_of(min, r)), + + VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)), + + VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)), + } + } +} + +impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) + } +} + + +impl<'tcx> LexicalRegionResolutions<'tcx> { + fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => self.resolve_var(rid), + _ => r, + } + } + + fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { + &self.values[rid] + } + + fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> { + &mut self.values[rid] + } + + pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + let result = match self.values[rid] { + VarValue::Value(r) => r, + VarValue::ErrorValue => self.error_region, + }; + debug!("resolve_var({:?}) = {:?}", rid, result); + result + } +} diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 04b470b29fc5e..55c7eef607bbe 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -15,6 +15,7 @@ use super::Subtype; use traits::ObligationCause; use ty::{self, Ty, TyCtxt}; +use ty::error::TypeError; use ty::relate::{Relate, RelateResult, TypeRelation}; /// "Least upper bound" (common supertype) @@ -67,14 +68,39 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.lub_regions(origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> { - self.fields.higher_ranked_lub(a, b, self.a_is_expected) + debug!("binders(a={:?}, b={:?})", a, b); + let was_error = self.infcx().probe(|_snapshot| { + // Subtle: use a fresh combine-fields here because we recover + // from Err. Doing otherwise could propagate obligations out + // through our `self.obligations` field. + self.infcx() + .combine_fields(self.fields.trace.clone(), self.fields.param_env) + .higher_ranked_lub(a, b, self.a_is_expected) + .is_err() + }); + debug!("binders: was_error={:?}", was_error); + + // When higher-ranked types are involved, computing the LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + match self.relate_with_variance(ty::Variance::Invariant, a, b) { + Ok(_) => Ok(a.clone()), + Err(err) => { + debug!("binders: error occurred, was_error={:?}", was_error); + if !was_error { + Err(TypeError::OldStyleLUB(Box::new(err))) + } else { + Err(err) + } + } + } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 39bcd7035742e..96a980a15457e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -16,13 +16,12 @@ pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_inference::{GenericKind, VerifyBound}; use hir::def_id::DefId; -use middle::free_region::{FreeRegionMap, RegionRelations}; +use middle::free_region::RegionRelations; use middle::region; use middle::lang_items; -use mir::tcx::LvalueTy; +use mir::tcx::PlaceTy; use ty::subst::{Kind, Subst, Substs}; use ty::{TyVid, IntVid, FloatVid}; use ty::{self, Ty, TyCtxt}; @@ -31,7 +30,8 @@ use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::relate::RelateResult; use traits::{self, ObligationCause, PredicateObligations, Reveal}; use rustc_data_structures::unify::{self, UnificationTable}; -use std::cell::{Cell, RefCell, Ref}; +use std::cell::{Cell, RefCell, Ref, RefMut}; +use std::collections::BTreeMap; use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; @@ -41,7 +41,10 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; -use self::region_inference::{RegionVarBindings, RegionSnapshot}; +use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; +use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins}; +use self::lexical_region_resolve::LexicalRegionResolutions; +use self::outlives::env::OutlivesEnvironment; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -54,7 +57,9 @@ mod glb; mod higher_ranked; pub mod lattice; mod lub; -pub mod region_inference; +pub mod region_constraints; +mod lexical_region_resolve; +pub mod outlives; pub mod resolve; mod freshen; mod sub; @@ -98,8 +103,15 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // Map from floating variable to the kind of float it represents float_unification_table: RefCell>, - // For region variables. - region_vars: RegionVarBindings<'a, 'gcx, 'tcx>, + // Tracks the set of region variables and the constraints between + // them. This is initially `Some(_)` but when + // `resolve_regions_and_report_errors` is invoked, this gets set + // to `None` -- further attempts to perform unification etc may + // fail if new region constraints would've been added. + region_constraints: RefCell>>, + + // Once region inference is done, the values for each variable. + lexical_region_resolutions: RefCell>>, /// Caches the results of trait selection. This cache is used /// for things that have to do with the parameters in scope. @@ -135,11 +147,44 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // This flag is true while there is an active snapshot. in_snapshot: Cell, + + // A set of constraints that regionck must validate. Each + // constraint has the form `T:'a`, meaning "some type `T` must + // outlive the lifetime 'a". These constraints derive from + // instantiated type parameters. So if you had a struct defined + // like + // + // struct Foo { ... } + // + // then in some expression `let x = Foo { ... }` it will + // instantiate the type parameter `T` with a fresh type `$0`. At + // the same time, it will record a region obligation of + // `$0:'static`. This will get checked later by regionck. (We + // can't generally check these things right away because we have + // to wait until types are resolved.) + // + // These are stored in a map keyed to the id of the innermost + // enclosing fn body / static initializer expression. This is + // because the location where the obligation was incurred can be + // relevant with respect to which sublifetime assumptions are in + // place. The reason that we store under the fn-id, and not + // something more fine-grained, is so that it is easier for + // regionck to be sure that it has found *all* the region + // obligations (otherwise, it's easy to fail to walk to a + // particular node-id). + // + // Before running `resolve_regions_and_report_errors`, the creator + // of the inference context is expected to invoke + // `process_region_obligations` (defined in `self::region_obligations`) + // for each body-id in this map, which will process the + // obligations within. This is expected to be done 'late enough' + // that all type inference variables have been bound and so forth. + region_obligations: RefCell)>>, } /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized /// region that each late-bound region was replaced with. -pub type SkolemizationMap<'tcx> = FxHashMap>; +pub type SkolemizationMap<'tcx> = BTreeMap>; /// See `error_reporting` module for more details #[derive(Clone, Debug)] @@ -248,10 +293,6 @@ pub enum SubregionOrigin<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - - // this is `Some(_)` if this error arises from the bug fix for - // #18937. This is a temporary measure. - lint_id: Option, }, } @@ -280,7 +321,7 @@ pub enum LateBoundRegionConversionTime { /// Reasons to create a region inference variable /// /// See `error_reporting` module for more details -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RegionVariableOrigin { // Region variables created for ill-categorized reasons, // mostly indicates places in need of refactoring @@ -308,6 +349,20 @@ pub enum RegionVariableOrigin { UpvarRegion(ty::UpvarId, Span), BoundRegionInCoherence(ast::Name), + + // This origin is used for the inference variables that we create + // during NLL region processing. + NLL(NLLRegionVariableOrigin), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum NLLRegionVariableOrigin { + // During NLL region processing, we create variables for free + // regions that we encounter in the function signature and + // elsewhere. This origin indices we've got one of those. + FreeRegion, + + Inferred(::mir::visit::TyContext), } #[derive(Copy, Clone, Debug)] @@ -317,6 +372,14 @@ pub enum FixupError { UnresolvedTy(TyVid) } +/// See the `region_obligations` field for more information. +#[derive(Clone)] +pub struct RegionObligation<'tcx> { + pub sub_region: ty::Region<'tcx>, + pub sup_type: Ty<'tcx>, + pub cause: ObligationCause<'tcx>, +} + impl fmt::Display for FixupError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::FixupError::*; @@ -379,13 +442,15 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { 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(tcx), + region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), + lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), reported_trait_errors: RefCell::new(FxHashMap()), tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), + region_obligations: RefCell::new(vec![]), })) } } @@ -412,7 +477,8 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { type_snapshot: type_variable::Snapshot, int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, - region_vars_snapshot: RegionSnapshot, + region_constraints_snapshot: RegionSnapshot, + region_obligations_snapshot: usize, was_in_snapshot: bool, _in_progress_tables: Option>>, } @@ -451,15 +517,15 @@ impl_trans_normalize!('gcx, ty::ExistentialTraitRef<'gcx> ); -impl<'gcx> TransNormalize<'gcx> for LvalueTy<'gcx> { +impl<'gcx> TransNormalize<'gcx> for PlaceTy<'gcx> { fn trans_normalize<'a, 'tcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { match *self { - LvalueTy::Ty { ty } => LvalueTy::Ty { ty: ty.trans_normalize(infcx, param_env) }, - LvalueTy::Downcast { adt_def, substs, variant_index } => { - LvalueTy::Downcast { + PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.trans_normalize(infcx, param_env) }, + PlaceTy::Downcast { adt_def, substs, variant_index } => { + PlaceTy::Downcast { adt_def, substs: substs.trans_normalize(infcx, param_env), variant_index, @@ -480,16 +546,16 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { { assert!(!value.needs_subst()); let value = self.erase_late_bound_regions(value); - self.normalize_associated_type(&value) + self.fully_normalize_associated_types_in(&value) } /// Fully normalizes any associated types in `value`, using an /// empty environment and `Reveal::All` mode (therefore, suitable /// only for monomorphized code during trans, basically). - pub fn normalize_associated_type(self, value: &T) -> T + pub fn fully_normalize_associated_types_in(self, value: &T) -> T where T: TransNormalize<'tcx> { - debug!("normalize_associated_type(t={:?})", value); + debug!("fully_normalize_associated_types_in(t={:?})", value); let param_env = ty::ParamEnv::empty(Reveal::All); let value = self.erase_regions(value); @@ -720,7 +786,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), - region_vars_snapshot: self.region_vars.start_snapshot(), + region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(), + region_obligations_snapshot: self.region_obligations.borrow().len(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -736,7 +803,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, + region_obligations_snapshot, was_in_snapshot, _in_progress_tables } = snapshot; @@ -754,8 +822,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .rollback_to(float_snapshot); - self.region_vars - .rollback_to(region_vars_snapshot); + self.region_obligations + .borrow_mut() + .truncate(region_obligations_snapshot); + self.borrow_region_constraints() + .rollback_to(region_constraints_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot) { @@ -764,7 +835,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, + region_obligations_snapshot: _, was_in_snapshot, _in_progress_tables } = snapshot; @@ -782,8 +854,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .commit(float_snapshot); - self.region_vars - .commit(region_vars_snapshot); + self.borrow_region_constraints() + .commit(region_constraints_snapshot); } /// Execute `f` and commit the bindings @@ -838,7 +910,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub: ty::Region<'tcx>, sup: ty::RegionVid) { - self.region_vars.add_given(sub, sup); + self.borrow_region_constraints().add_given(sub, sup); } pub fn can_sub(&self, @@ -878,7 +950,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>) { debug!("sub_regions({:?} <: {:?})", a, b); - self.region_vars.make_subregion(origin, a, b); + self.borrow_region_constraints().make_subregion(origin, a, b); } pub fn equality_predicate(&self, @@ -979,9 +1051,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .new_key(None) } + /// Create a fresh region variable with the next available index. + /// + /// # Parameters + /// + /// - `origin`: information about why we created this variable, for use + /// during diagnostics / error-reporting. pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { - self.tcx.mk_region(ty::ReVar(self.region_vars.new_region_var(origin))) + self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin))) + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) + -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::NLL(origin)) } /// Create a region inference variable for the given @@ -1040,10 +1124,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } - pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - self.region_vars.new_bound(debruijn) - } - /// True if errors have been reported since this infcx was /// created. This is sometimes used as a heuristic to skip /// reporting errors that often occur as a result of earlier @@ -1069,15 +1149,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tainted_by_errors_flag.set(true) } + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_type_vars_if_possible` as well as `fully_resolve`. pub fn resolve_regions_and_report_errors(&self, region_context: DefId, region_map: ®ion::ScopeTree, - free_regions: &FreeRegionMap<'tcx>) { - let region_rels = RegionRelations::new(self.tcx, - region_context, - region_map, - free_regions); - let errors = self.region_vars.resolve_regions(®ion_rels); + outlives_env: &OutlivesEnvironment<'tcx>) { + assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), + "region_obligations not empty: {:#?}", + self.region_obligations.borrow()); + + let region_rels = &RegionRelations::new(self.tcx, + region_context, + region_map, + outlives_env.free_region_map()); + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + let (lexical_region_resolutions, errors) = + lexical_region_resolve::resolve(region_rels, var_origins, data); + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors @@ -1089,6 +1185,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + /// Obtains (and clears) the current set of region + /// constraints. The inference context is still usable: further + /// unifications will simply add new constraints. + /// + /// This method is not meant to be used with normal lexical region + /// resolution. Rather, it is used in the NLL mode as a kind of + /// interim hack: basically we run normal type-check and generate + /// region constraints as normal, but then we take them and + /// translate them into the form that the NLL solver + /// understands. See the NLL module for mode details. + pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { + self.borrow_region_constraints().take_and_reset_data() + } + + /// Takes ownership of the list of variable regions. This implies + /// that all the region constriants have already been taken, and + /// hence that `resolve_regions_and_report_errors` can never be + /// called. This is used only during NLL processing to "hand off" ownership + /// of the set of region vairables into the NLL region context. + pub fn take_region_var_origins(&self) -> VarOrigins { + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + assert!(data.is_empty()); + var_origins + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_type_vars_if_possible(&t).to_string() } @@ -1260,7 +1384,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { span: Span, lbrct: LateBoundRegionConversionTime, value: &ty::Binder) - -> (T, FxHashMap>) + -> (T, BTreeMap>) where T : TypeFoldable<'tcx> { self.tcx.replace_late_bound_regions( @@ -1301,7 +1425,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { Ok(InferOk { value: result, obligations: combine.obligations }) } - /// See `verify_generic_bound` method in `region_inference` + /// See `verify_generic_bound` method in `region_constraints` pub fn verify_generic_bound(&self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, @@ -1312,7 +1436,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a, bound); - self.region_vars.verify_generic_bound(origin, kind, a, bound); + self.borrow_region_constraints().verify_generic_bound(origin, kind, a, bound); } pub fn type_moves_by_default(&self, @@ -1338,26 +1462,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { !traits::type_known_to_meet_bound(self, param_env, ty, copy_def_id, span) } + /// Obtains the latest type of the given closure; this may be a + /// closure in the current function, in which case its + /// `ClosureKind` may not yet be known. pub fn closure_kind(&self, - def_id: DefId) + closure_def_id: DefId, + closure_substs: ty::ClosureSubsts<'tcx>) -> Option { - if let Some(tables) = self.in_progress_tables { - if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - let hir_id = self.tcx.hir.node_to_hir_id(id); - return tables.borrow() - .closure_kinds() - .get(hir_id) - .cloned() - .map(|(kind, _)| kind); - } - } - - // 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)) + let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx); + let closure_kind_ty = self.shallow_resolve(&closure_kind_ty); + closure_kind_ty.to_opt_closure_kind() } /// Obtain the signature of a function or closure. @@ -1365,11 +1480,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// work during the type-checking of the enclosing function and /// return the closure signature in its partially inferred state. pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> { + // Do we have an in-progress set of tables we are inferring? if let Some(tables) = self.in_progress_tables { + // Is this a local item? if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - let hir_id = self.tcx.hir.node_to_hir_id(id); - if let Some(&ty) = tables.borrow().closure_tys().get(hir_id) { - return ty; + // Is it a local *closure*? + if self.tcx.is_closure(def_id) { + let hir_id = self.tcx.hir.node_to_hir_id(id); + // Is this local closure contained within the tables we are inferring? + if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) { + // if so, extract signature from there. + let closure_ty = tables.borrow().node_id_to_type(hir_id); + let (closure_def_id, closure_substs) = match closure_ty.sty { + ty::TyClosure(closure_def_id, closure_substs) => + (closure_def_id, closure_substs), + _ => + bug!("closure with non-closure type: {:?}", closure_ty), + }; + assert_eq!(def_id, closure_def_id); + let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx); + let closure_sig_ty = self.shallow_resolve(&closure_sig_ty); + return closure_sig_ty.fn_sig(self.tcx); + } } } } @@ -1377,17 +1509,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.fn_sig(def_id) } - pub fn generator_sig(&self, def_id: DefId) -> Option> { - if let Some(tables) = self.in_progress_tables { - if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - let hir_id = self.tcx.hir.node_to_hir_id(id); - if let Some(&ty) = tables.borrow().generator_sigs().get(hir_id) { - return ty.map(|t| ty::Binder(t)); - } - } - } + /// Normalizes associated types in `value`, potentially returning + /// new obligations that must further be processed. + pub fn partially_normalize_associated_types_in(&self, + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> InferOk<'tcx, T> + where T : TypeFoldable<'tcx> + { + debug!("partially_normalize_associated_types_in(value={:?})", value); + let mut selcx = traits::SelectionContext::new(self); + let cause = ObligationCause::misc(span, body_id); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, param_env, cause, value); + debug!("partially_normalize_associated_types_in: result={:?} predicates={:?}", + value, + obligations); + InferOk { value, obligations } + } - self.tcx.generator_sig(def_id) + fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> { + RefMut::map( + self.region_constraints.borrow_mut(), + |c| c.as_mut().expect("region constraints already solved")) } } @@ -1466,14 +1612,12 @@ impl<'tcx> SubregionOrigin<'tcx> { traits::ObligationCauseCode::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => + trait_item_def_id, } => SubregionOrigin::CompareImplMethodObligation { span: cause.span, item_name, impl_item_def_id, trait_item_def_id, - lint_id, }, _ => default(), @@ -1492,7 +1636,8 @@ impl RegionVariableOrigin { EarlyBoundRegion(a, ..) => a, LateBoundRegion(a, ..) => a, BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP, - UpvarRegion(_, a) => a + UpvarRegion(_, a) => a, + NLL(..) => bug!("NLL variable used with `span`"), } } } @@ -1533,3 +1678,12 @@ impl<'tcx> TypeFoldable<'tcx> for TypeTrace<'tcx> { self.cause.visit_with(visitor) || self.values.visit_with(visitor) } } + +impl<'tcx> fmt::Debug for RegionObligation<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, + self.sup_type) + } +} + diff --git a/src/librustc/infer/outlives/bounds.rs b/src/librustc/infer/outlives/bounds.rs new file mode 100644 index 0000000000000..8a562471ac5d0 --- /dev/null +++ b/src/librustc/infer/outlives/bounds.rs @@ -0,0 +1,218 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use infer::InferCtxt; +use syntax::ast; +use syntax::codemap::Span; +use traits::FulfillmentContext; +use ty::{self, Ty, TypeFoldable}; +use ty::outlives::Component; +use ty::wf; + +/// Outlives bounds are relationships between generic parameters, +/// whether they both be regions (`'a: 'b`) or whether types are +/// involved (`T: 'a`). These relationships can be extracted from the +/// full set of predicates we understand or also from types (in which +/// case they are called implied bounds). They are fed to the +/// `OutlivesEnv` which in turn is supplied to the region checker and +/// other parts of the inference system. +#[derive(Debug)] +pub enum OutlivesBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// + /// fn foo<'a,T>(x: &'a T) + /// + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + /// - `span`, a span to use when normalizing, hopefully not important, + /// might be useful if a `bug!` occurs. + pub fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec> { + let tcx = self.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Currently (at least) these resulting + // predicates are always guaranteed to be a subset of the original + // type, so we need not fear non-termination. + let mut wf_types = vec![ty]; + + let mut implied_bounds = vec![]; + + let mut fulfill_cx = FulfillmentContext::new(); + + while let Some(ty) = wf_types.pop() { + // Compute the obligations for `ty` to be well-formed. If `ty` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); + + // NB: All of these predicates *ought* to be easily proven + // true. In fact, their correctness is (mostly) implied by + // other parts of the program. However, in #42552, we had + // an annoying scenario where: + // + // - Some `T::Foo` gets normalized, resulting in a + // variable `_1` and a `T: Trait` constraint + // (not sure why it couldn't immediately get + // solved). This result of `_1` got cached. + // - These obligations were dropped on the floor here, + // rather than being registered. + // - Then later we would get a request to normalize + // `T::Foo` which would result in `_1` being used from + // the cache, but hence without the `T: Trait` + // constraint. As a result, `_1` never gets resolved, + // and we get an ICE (in dropck). + // + // Therefore, we register any predicates involving + // inference variables. We restrict ourselves to those + // involving inference variables both for efficiency and + // to avoids duplicate errors that otherwise show up. + fulfill_cx.register_predicate_obligations( + self, + obligations + .iter() + .filter(|o| o.predicate.has_infer_types()) + .cloned(), + ); + + // From the full set of obligations, just filter down to the + // region relationships. + implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { + assert!(!obligation.has_escaping_regions()); + match obligation.predicate { + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::Projection(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ConstEvaluatable(..) => vec![], + + ty::Predicate::WellFormed(subty) => { + wf_types.push(subty); + vec![] + } + + ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => { + vec![OutlivesBound::RegionSubRegion(r_b, r_a)] + } + }, + + ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = self.resolve_type_vars_if_possible(&ty_a); + let components = tcx.outlives_components(ty_a); + Self::implied_bounds_from_components(r_b, components) + } + }, + } + })); + } + + // Ensure that those obligations that we had to solve + // get solved *here*. + match fulfill_cx.select_all_or_error(self) { + Ok(()) => (), + Err(errors) => self.report_fulfillment_errors(&errors, None), + } + + implied_bounds + } + + /// When we have an implied bound that `T: 'a`, we can further break + /// this down to determine what relationships would have to hold for + /// `T: 'a` to hold. We get to assume that the caller has validated + /// those relationships. + fn implied_bounds_from_components( + sub_region: ty::Region<'tcx>, + sup_components: Vec>, + ) -> Vec> { + sup_components + .into_iter() + .flat_map(|component| { + match component { + Component::Region(r) => + vec![OutlivesBound::RegionSubRegion(sub_region, r)], + Component::Param(p) => + vec![OutlivesBound::RegionSubParam(sub_region, p)], + Component::Projection(p) => + vec![OutlivesBound::RegionSubProjection(sub_region, p)], + Component::EscapingProjection(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + vec![], + Component::UnresolvedInferenceVariable(..) => + vec![], + } + }) + .collect() + } +} + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator> + 'tcx { + debug!("explicit_outlives_bounds()"); + param_env + .caller_bounds + .into_iter() + .filter_map(move |predicate| match predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::WellFormed(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::TypeOutlives(..) | + ty::Predicate::ConstEvaluatable(..) => None, + ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map( + |ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a), + ), + }) +} diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs new file mode 100644 index 0000000000000..d47507592f80d --- /dev/null +++ b/src/librustc/infer/outlives/env.rs @@ -0,0 +1,198 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use infer::{GenericKind, InferCtxt}; +use infer::outlives::free_region_map::FreeRegionMap; +use infer::outlives::bounds::{self, OutlivesBound}; +use ty::{self, Ty}; + +use syntax::ast; +use syntax_pos::Span; + +/// The `OutlivesEnvironment` collects information about what outlives +/// what in a given type-checking setting. For example, if we have a +/// where-clause like `where T: 'a` in scope, then the +/// `OutlivesEnvironment` would record that (in its +/// `region_bound_pairs` field). Similarly, it contains methods for +/// processing and adding implied bounds into the outlives +/// environment. +/// +/// Other code at present does not typically take a +/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g., +/// `process_registered_region_obligations` wants the +/// region-bound-pairs). There is no mistaking it: the current setup +/// of tracking region information is quite scattered! The +/// `OutlivesEnvironment`, for example, needs to sometimes be combined +/// with the `middle::RegionRelations`, to yield a full picture of how +/// (lexical) lifetimes interact. However, I'm reluctant to do more +/// refactoring here, since the setup with NLL is quite different. +/// For example, NLL has no need of `RegionRelations`, and is solely +/// interested in the `OutlivesEnvironment`. -nmatsakis +#[derive(Clone)] +pub struct OutlivesEnvironment<'tcx> { + param_env: ty::ParamEnv<'tcx>, + free_region_map: FreeRegionMap<'tcx>, + region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { + pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { + let mut env = OutlivesEnvironment { + param_env, + free_region_map: FreeRegionMap::new(), + region_bound_pairs: vec![], + }; + + env.add_outlives_bounds(None, bounds::explicit_outlives_bounds(param_env)); + + env + } + + /// Borrows current value of the `free_region_map`. + pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { + &self.free_region_map + } + + /// Borrows current value of the `region_bound_pairs`. + pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] { + &self.region_bound_pairs + } + + /// Returns ownership of the `free_region_map`. + pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> { + self.free_region_map + } + + /// This is a hack to support the old-skool regionck, which + /// processes region constraints from the main function and the + /// closure together. In that context, when we enter a closure, we + /// want to be able to "save" the state of the surrounding a + /// function. We can then add implied bounds and the like from the + /// closure arguments into the environment -- these should only + /// apply in the closure body, so once we exit, we invoke + /// `pop_snapshot_post_closure` to remove them. + /// + /// Example: + /// + /// ``` + /// fn foo() { + /// callback(for<'a> |x: &'a T| { + /// // ^^^^^^^ not legal syntax, but probably should be + /// // within this closure body, `T: 'a` holds + /// }) + /// } + /// ``` + /// + /// This "containment" of closure's effects only works so well. In + /// particular, we (intentionally) leak relationships between free + /// regions that are created by the closure's bounds. The case + /// where this is useful is when you have (e.g.) a closure with a + /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this + /// case, we want to keep the relationship `'b: 'a` in the + /// free-region-map, so that later if we have to take `LUB('b, + /// 'a)` we can get the result `'b`. + /// + /// I have opted to keep **all modifications** to the + /// free-region-map, however, and not just those that concern free + /// variables bound in the closure. The latter seems more correct, + /// but it is not the existing behavior, and I could not find a + /// case where the existing behavior went wrong. In any case, it + /// seems like it'd be readily fixed if we wanted. There are + /// similar leaks around givens that seem equally suspicious, to + /// be honest. --nmatsakis + pub fn push_snapshot_pre_closure(&self) -> usize { + self.region_bound_pairs.len() + } + + /// See `push_snapshot_pre_closure`. + pub fn pop_snapshot_post_closure(&mut self, len: usize) { + self.region_bound_pairs.truncate(len); + } + + /// This method adds "implied bounds" into the outlives environment. + /// Implied bounds are outlives relationships that we can deduce + /// on the basis that certain types must be well-formed -- these are + /// either the types that appear in the function signature or else + /// the input types to an impl. For example, if you have a function + /// like + /// + /// ``` + /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } + /// ``` + /// + /// we can assume in the caller's body that `'b: 'a` and that `T: + /// 'b` (and hence, transitively, that `T: 'a`). This method would + /// add those assumptions into the outlives-environment. + /// + /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + pub fn add_implied_bounds( + &mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + fn_sig_tys: &[Ty<'tcx>], + body_id: ast::NodeId, + span: Span, + ) { + debug!("add_implied_bounds()"); + + for &ty in fn_sig_tys { + let ty = infcx.resolve_type_vars_if_possible(&ty); + debug!("add_implied_bounds: ty = {}", ty); + let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); + self.add_outlives_bounds(Some(infcx), implied_bounds) + } + } + + /// Processes outlives bounds that are known to hold, whether from implied or other sources. + /// + /// The `infcx` parameter is optional; if the implied bounds may + /// contain inference variables, it must be supplied, in which + /// case we will register "givens" on the inference context. (See + /// `RegionConstraintData`.) + fn add_outlives_bounds( + &mut self, + infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>, + outlives_bounds: I, + ) where + I: IntoIterator>, + { + // Record relationships such as `T:'x` that don't go into the + // free-region-map but which we use here. + for outlives_bound in outlives_bounds { + debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); + match outlives_bound { + OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) | + OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); + } + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); + } + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Projection(projection_b))); + } + OutlivesBound::RegionSubRegion(r_a, r_b) => { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); + } + } + } + } +} diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs new file mode 100644 index 0000000000000..2127c4714aef0 --- /dev/null +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -0,0 +1,102 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ty::{self, Lift, TyCtxt, Region}; +use rustc_data_structures::transitive_relation::TransitiveRelation; + +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct FreeRegionMap<'tcx> { + // Stores the relation `a < b`, where `a` and `b` are regions. + // + // Invariant: only free regions like `'x` or `'static` are stored + // in this relation, not scopes. + relation: TransitiveRelation> +} + +impl<'tcx> FreeRegionMap<'tcx> { + pub fn new() -> Self { + FreeRegionMap { relation: TransitiveRelation::new() } + } + + pub fn is_empty(&self) -> bool { + self.relation.is_empty() + } + + // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + // (with the exception that `'static: 'x` is not notable) + pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); + if is_free_or_static(sub) && is_free(sup) { + self.relation.add(sub, sup) + } + } + + /// Tests whether `r_a <= r_b`. Both must be free regions or + /// `'static`. + pub fn sub_free_regions<'a, 'gcx>(&self, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> bool { + assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); + if let ty::ReStatic = r_b { + true // `'a <= 'static` is just always true, and not stored in the relation explicitly + } else { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } + } + + /// Compute the least-upper-bound of two free regions. In some + /// cases, this is more conservative than necessary, in order to + /// avoid making arbitrary choices. See + /// `TransitiveRelation::postdom_upper_bound` for more details. + pub fn lub_free_regions<'a, 'gcx>(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> Region<'tcx> { + debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(is_free(r_a)); + assert!(is_free(r_b)); + let result = if r_a == r_b { r_a } else { + match self.relation.postdom_upper_bound(&r_a, &r_b) { + None => tcx.mk_region(ty::ReStatic), + Some(r) => *r, + } + }; + debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); + result + } +} + +fn is_free(r: Region) -> bool { + match *r { + ty::ReEarlyBound(_) | ty::ReFree(_) => true, + _ => false + } +} + +fn is_free_or_static(r: Region) -> bool { + match *r { + ty::ReStatic => true, + _ => is_free(r), + } +} + +impl_stable_hash_for!(struct FreeRegionMap<'tcx> { + relation +}); + +impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { + type Lifted = FreeRegionMap<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option> { + self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) + .map(|relation| FreeRegionMap { relation }) + } +} diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs new file mode 100644 index 0000000000000..6aafebe79c671 --- /dev/null +++ b/src/librustc/infer/outlives/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Various code related to computing outlives relations. + +pub mod env; +pub mod free_region_map; +pub mod bounds; +mod obligations; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs new file mode 100644 index 0000000000000..07eacde0aab88 --- /dev/null +++ b/src/librustc/infer/outlives/obligations.rs @@ -0,0 +1,612 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code that handles "type-outlives" constraints like `T: 'a`. This +//! is based on the `outlives_components` function defined on the tcx, +//! but it adds a bit of heuristics on top, in particular to deal with +//! associated types and projections. +//! +//! When we process a given `T: 'a` obligation, we may produce two +//! kinds of constraints for the region inferencer: +//! +//! - Relationships between inference variables and other regions. +//! For example, if we have `&'?0 u32: 'a`, then we would produce +//! a constraint that `'a <= '?0`. +//! - "Verifys" that must be checked after inferencing is done. +//! For example, if we know that, for some type parameter `T`, +//! `T: 'a + 'b`, and we have a requirement that `T: '?1`, +//! then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`. +//! - Note the difference with the previous case: here, the region +//! variable must be less than something else, so this doesn't +//! affect how inference works (it finds the smallest region that +//! will do); it's just a post-condition that we have to check. +//! +//! **The key point is that once this function is done, we have +//! reduced all of our "type-region outlives" obligations into relationships +//! between individual regions.** +//! +//! One key input to this function is the set of "region-bound pairs". +//! These are basically the relationships between type parameters and +//! regions that are in scope at the point where the outlives +//! obligation was incurred. **When type-checking a function, +//! particularly in the face of closures, this is not known until +//! regionck runs!** This is because some of those bounds come +//! from things we have yet to infer. +//! +//! Consider: +//! +//! ``` +//! fn bar(a: T, b: impl for<'a> Fn(&'a T)); +//! fn foo(x: T) { +//! bar(x, |y| { ... }) +//! // ^ closure arg +//! } +//! ``` +//! +//! Here, the type of `y` may involve inference variables and the +//! like, and it may also contain implied bounds that are needed to +//! type-check the closure body (e.g., here it informs us that `T` +//! outlives the late-bound region `'a`). +//! +//! Note that by delaying the gathering of implied bounds until all +//! inference information is known, we may find relationships between +//! bound regions and other regions in the environment. For example, +//! when we first check a closure like the one expected as argument +//! to `foo`: +//! +//! ``` +//! fn foo FnMut(&'a U)>(_f: F) {} +//! ``` +//! +//! the type of the closure's first argument would be `&'a ?U`. We +//! might later infer `?U` to something like `&'b u32`, which would +//! imply that `'b: 'a`. + +use hir::def_id::DefId; +use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use traits; +use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::subst::{Subst, Substs}; +use ty::outlives::Component; +use syntax::ast; + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Registers that the given region obligation must be resolved + /// from within the scope of `body_id`. These regions are enqueued + /// and later processed by regionck, when full type information is + /// available (see `region_obligations` field for more + /// information). + pub fn register_region_obligation( + &self, + body_id: ast::NodeId, + obligation: RegionObligation<'tcx>, + ) { + debug!("register_region_obligation({:?}, {:?})", body_id, obligation); + self.region_obligations + .borrow_mut() + .push((body_id, obligation)); + } + + /// Process the region obligations that must be proven (during + /// `regionck`) for the given `body_id`, given information about + /// the region bounds in scope and so forth. This function must be + /// invoked for all relevant body-ids before region inference is + /// done (or else an assert will fire). + /// + /// See the `region_obligations` field of `InferCtxt` for some + /// comments about how this funtion fits into the overall expected + /// flow of the the inferencer. The key point is that it is + /// invoked after all type-inference variables have been bound -- + /// towards the end of regionck. This also ensures that the + /// region-bound-pairs are available (see comments above regarding + /// closures). + /// + /// # Parameters + /// + /// - `region_bound_pairs`: the set of region bounds implied by + /// the parameters and where-clauses. In particular, each pair + /// `('a, K)` in this list tells us that the bounds in scope + /// indicate that `K: 'a`, where `K` is either a generic + /// parameter like `T` or a projection like `T::Item`. + /// - `implicit_region_bound`: if some, this is a region bound + /// that is considered to hold for all type parameters (the + /// function body). + /// - `param_env` is the parameter environment for the enclosing function. + /// - `body_id` is the body-id whose region obligations are being + /// processed. + /// + /// # Returns + /// + /// This function may have to perform normalizations, and hence it + /// returns an `InferOk` with subobligations that must be + /// processed. + pub fn process_registered_region_obligations( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ) { + assert!( + !self.in_snapshot.get(), + "cannot process registered region obligations in a snapshot" + ); + + // pull out the region obligations with the given `body_id` (leaving the rest) + let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); + { + let mut r_o = self.region_obligations.borrow_mut(); + for (_, obligation) in r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id) { + my_region_obligations.push(obligation); + } + } + + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + + for RegionObligation { + sup_type, + sub_region, + cause, + } in my_region_obligations + { + let origin = SubregionOrigin::from_obligation_cause( + &cause, + || infer::RelateParamBound(cause.span, sup_type), + ); + + outlives.type_must_outlive(origin, sup_type, sub_region); + } + } + + /// Processes a single ad-hoc region obligation that was not + /// registered in advance. + pub fn type_must_outlive( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + outlives.type_must_outlive(origin, ty, region); + } +} + +#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =) +struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + // See the comments on `process_registered_region_obligations` for the meaning + // of these fields. + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { + fn new( + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + Self { + infcx, + region_bound_pairs, + implicit_region_bound, + param_env, + } + } + + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + fn type_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let ty = self.infcx.resolve_type_vars_if_possible(&ty); + + debug!( + "type_must_outlive(ty={:?}, region={:?}, origin={:?})", + ty, + region, + origin + ); + + assert!(!ty.has_escaping_regions()); + + let components = self.tcx().outlives_components(ty); + self.components_must_outlive(origin, components, region); + } + + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn components_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + components: Vec>, + region: ty::Region<'tcx>, + ) { + for component in components { + let origin = origin.clone(); + match component { + Component::Region(region1) => { + self.infcx.sub_regions(origin, region, region1); + } + Component::Param(param_ty) => { + self.param_ty_must_outlive(origin, region, param_ty); + } + Component::Projection(projection_ty) => { + self.projection_must_outlive(origin, region, projection_ty); + } + Component::EscapingProjection(subcomponents) => { + self.components_must_outlive(origin, subcomponents, region); + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.infcx.tcx.sess.delay_span_bug( + origin.span(), + &format!("unresolved inference variable in outlives: {:?}", v), + ); + } + } + } + } + + fn param_ty_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + param_ty: ty::ParamTy, + ) { + debug!( + "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", + region, + param_ty, + origin + ); + + let verify_bound = self.param_bound(param_ty); + let generic = GenericKind::Param(param_ty); + self.infcx + .verify_generic_bound(origin, generic, region, verify_bound); + } + + fn projection_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + debug!( + "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", + region, + projection_ty, + origin + ); + + // This case is thorny for inference. The fundamental problem is + // that there are many cases where we have choice, and inference + // doesn't like choice (the current region inference in + // particular). :) First off, we have to choose between using the + // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and + // OutlivesProjectionComponent rules, any one of which is + // sufficient. If there are no inference variables involved, it's + // not hard to pick the right rule, but if there are, we're in a + // bit of a catch 22: if we picked which rule we were going to + // use, we could add constraints to the region inference graph + // that make it apply, but if we don't add those constraints, the + // rule might not apply (but another rule might). For now, we err + // on the side of adding too few edges into the graph. + + // Compute the bounds we can derive from the environment or trait + // definition. We know that the projection outlives all the + // regions in this list. + let env_bounds = self.projection_declared_bounds(projection_ty); + + debug!("projection_must_outlive: env_bounds={:?}", env_bounds); + + // If we know that the projection outlives 'static, then we're + // done here. + if env_bounds.contains(&&ty::ReStatic) { + debug!("projection_must_outlive: 'static as declared bound"); + return; + } + + // If declared bounds list is empty, the only applicable rule is + // OutlivesProjectionComponent. If there are inference variables, + // then, we can break down the outlives into more primitive + // components without adding unnecessary edges. + // + // If there are *no* inference variables, however, we COULD do + // this, but we choose not to, because the error messages are less + // good. For example, a requirement like `T::Item: 'r` would be + // translated to a requirement that `T: 'r`; when this is reported + // to the user, it will thus say "T: 'r must hold so that T::Item: + // 'r holds". But that makes it sound like the only way to fix + // the problem is to add `T: 'r`, which isn't true. So, if there are no + // inference variables, we use a verify constraint instead of adding + // edges, which winds up enforcing the same condition. + let needs_infer = projection_ty.needs_infer(); + if env_bounds.is_empty() && needs_infer { + debug!("projection_must_outlive: no declared bounds"); + + for component_ty in projection_ty.substs.types() { + self.type_must_outlive(origin.clone(), component_ty, region); + } + + for r in projection_ty.substs.regions() { + self.infcx.sub_regions(origin.clone(), region, r); + } + + return; + } + + // If we find that there is a unique declared bound `'b`, and this bound + // appears in the trait reference, then the best action is to require that `'b:'r`, + // so do that. This is best no matter what rule we use: + // + // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to + // the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` in addition to + // other conditions + if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { + let unique_bound = env_bounds[0]; + debug!( + "projection_must_outlive: unique declared bound = {:?}", + unique_bound + ); + if projection_ty + .substs + .regions() + .any(|r| env_bounds.contains(&r)) + { + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.infcx.sub_regions(origin.clone(), region, unique_bound); + return; + } + } + + // Fallback to verifying after the fact that there exists a + // declared bound, or that all the components appearing in the + // projection outlive; in some cases, this may add insufficient + // edges into the inference graph, leading to inference failures + // even though a satisfactory solution exists. + let verify_bound = self.projection_bound(env_bounds, projection_ty); + let generic = GenericKind::Projection(projection_ty); + self.infcx + .verify_generic_bound(origin, generic.clone(), region, verify_bound); + } + + fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + match ty.sty { + ty::TyParam(p) => self.param_bound(p), + ty::TyProjection(data) => { + let declared_bounds = self.projection_declared_bounds(data); + self.projection_bound(declared_bounds, data) + } + _ => self.recursive_type_bound(ty), + } + } + + fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + param_bounds.extend(self.implicit_region_bound); + + VerifyBound::AnyRegion(param_bounds) + } + + fn projection_declared_bounds( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + + declared_bounds + .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); + + declared_bounds + } + + fn projection_bound( + &self, + declared_bounds: Vec>, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> VerifyBound<'tcx> { + debug!( + "projection_bound(declared_bounds={:?}, projection_ty={:?})", + declared_bounds, + projection_ty + ); + + // see the extensive comment in projection_must_outlive + let ty = self.infcx + .tcx + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_type_bound(ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + } + + fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = vec![]; + + for subty in ty.walk_shallow() { + bounds.push(self.type_bound(subty)); + } + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(regions)); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } + } + + fn declared_generic_bounds_from_env( + &self, + generic: GenericKind<'tcx>, + ) -> Vec> { + let tcx = self.tcx(); + + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. Comparing using `==` is a bit + // dubious for projections, but it will work for simple cases + // like `T` and `T::Item`. It may not work as well for things + // like `>::Item`. + let generic_ty = generic.to_ty(tcx); + let c_b = self.param_env.caller_bounds; + let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-generic by `'a`, but we + // don't know that this holds from first principles. + for &(r, p) in self.region_bound_pairs { + debug!("generic={:?} p={:?}", generic, p); + if generic == p { + param_bounds.push(r); + } + } + + param_bounds + } + + /// Given a projection like `>::Bar`, returns any bounds + /// declared in the trait definition. For example, if the trait were + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// then this function would return `'x`. This is subject to the + /// limitations around higher-ranked bounds described in + /// `region_bounds_declared_on_associated_item`. + fn declared_projection_bounds_from_trait( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); + for r in &mut bounds { + *r = r.subst(self.tcx(), projection_ty.substs); + } + bounds + } + + /// Given the def-id of an associated item, returns any region + /// bounds attached to that associated item from the trait definition. + /// + /// For example: + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// If we were given the def-id of `Foo::Bar`, we would return + /// `'a`. You could then apply the substitutions from the + /// projection to convert this into your namespace. This also + /// works if the user writes `where >::Bar: 'a` on + /// the trait. In fact, it works by searching for just such a + /// where-clause. + /// + /// It will not, however, work for higher-ranked bounds like: + /// + /// ```rust + /// trait Foo<'a, 'b> + /// where for<'x> >::Bar: 'x + /// { + /// type Bar; + /// } + /// ``` + /// + /// This is for simplicity, and because we are not really smart + /// enough to cope with such bounds anywhere. + fn region_bounds_declared_on_associated_item( + &self, + assoc_item_def_id: DefId, + ) -> Vec> { + let tcx = self.tcx(); + let assoc_item = tcx.associated_item(assoc_item_def_id); + let trait_def_id = assoc_item.container.assert_trait(); + let trait_predicates = tcx.predicates_of(trait_def_id); + let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); + let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); + self.collect_outlives_from_predicate_list( + identity_proj, + traits::elaborate_predicates(tcx, trait_predicates.predicates), + ) + } + + /// Searches through a predicate list for a predicate `T: 'a`. + /// + /// Careful: does not elaborate predicates, and just uses `==` + /// when comparing `ty` for equality, so `ty` must be something + /// that does not involve inference variables and where you + /// otherwise want a precise match. + fn collect_outlives_from_predicate_list( + &self, + ty: Ty<'tcx>, + predicates: I, + ) -> Vec> + where + I: IntoIterator, + P: AsRef>, + { + predicates + .into_iter() + .filter_map(|p| p.as_ref().to_opt_type_outlives()) + .filter_map(|p| p.no_late_bound_regions()) + .filter(|p| p.0 == ty) + .map(|p| p.1) + .collect() + } +} diff --git a/src/librustc/infer/region_constraints/README.md b/src/librustc/infer/region_constraints/README.md new file mode 100644 index 0000000000000..67ad08c753033 --- /dev/null +++ b/src/librustc/infer/region_constraints/README.md @@ -0,0 +1,70 @@ +# Region constraint collection + +## Terminology + +Note that we use the terms region and lifetime interchangeably. + +## Introduction + +As described in the [inference README](../README.md), and unlike +normal type inference, which is similar in spirit to H-M and thus +works progressively, the region type inference works by accumulating +constraints over the course of a function. Finally, at the end of +processing a function, we process and solve the constraints all at +once. + +The constraints are always of one of three possible forms: + +- `ConstrainVarSubVar(Ri, Rj)` states that region variable Ri must be + a subregion of Rj +- `ConstrainRegSubVar(R, Ri)` states that the concrete region R (which + must not be a variable) must be a subregion of the variable Ri +- `ConstrainVarSubReg(Ri, R)` states the variable Ri shoudl be less + than the concrete region R. This is kind of deprecated and ought to + be replaced with a verify (they essentially play the same role). + +In addition to constraints, we also gather up a set of "verifys" +(what, you don't think Verify is a noun? Get used to it my +friend!). These represent relations that must hold but which don't +influence inference proper. These take the form of: + +- `VerifyRegSubReg(Ri, Rj)` indicates that Ri <= Rj must hold, + where Rj is not an inference variable (and Ri may or may not contain + one). This doesn't influence inference because we will already have + inferred Ri to be as small as possible, so then we just test whether + that result was less than Rj or not. +- `VerifyGenericBound(R, Vb)` is a more complex expression which tests + that the region R must satisfy the bound `Vb`. The bounds themselves + may have structure like "must outlive one of the following regions" + or "must outlive ALL of the following regions. These bounds arise + from constraints like `T: 'a` -- if we know that `T: 'b` and `T: 'c` + (say, from where clauses), then we can conclude that `T: 'a` if `'b: + 'a` *or* `'c: 'a`. + +## Building up the constraints + +Variables and constraints are created using the following methods: + +- `new_region_var()` creates a new, unconstrained region variable; +- `make_subregion(Ri, Rj)` states that Ri is a subregion of Rj +- `lub_regions(Ri, Rj) -> Rk` returns a region Rk which is + the smallest region that is greater than both Ri and Rj +- `glb_regions(Ri, Rj) -> Rk` returns a region Rk which is + the greatest region that is smaller than both Ri and Rj + +The actual region resolution algorithm is not entirely +obvious, though it is also not overly complex. + +## Snapshotting + +It is also permitted to try (and rollback) changes to the graph. This +is done by invoking `start_snapshot()`, which returns a value. Then +later you can call `rollback_to()` which undoes the work. +Alternatively, you can call `commit()` which ends all snapshots. +Snapshots can be recursive---so you can start a snapshot when another +is in progress, but only the root snapshot can "commit". + +## Skolemization + +For a discussion on skolemization and higher-ranked subtyping, please +see the module `middle::infer::higher_ranked::doc`. diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs new file mode 100644 index 0000000000000..72740dd40be29 --- /dev/null +++ b/src/librustc/infer/region_constraints/mod.rs @@ -0,0 +1,956 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! See README.md + +use self::UndoLogEntry::*; +use self::CombineMapType::*; + +use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; +use super::unify_key; + +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unify::{self, UnificationTable}; +use ty::{self, Ty, TyCtxt}; +use ty::{Region, RegionVid}; +use ty::ReStatic; +use ty::{BrFresh, ReLateBound, ReSkolemized, ReVar}; + +use std::collections::BTreeMap; +use std::fmt; +use std::mem; +use std::u32; + +mod taint; + +pub struct RegionConstraintCollector<'tcx> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + var_origins: IndexVec, + + data: RegionConstraintData<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'tcx>, + + /// Number of skolemized variables currently active. + skolemization_count: u32, + + /// Global counter used during the GLB algorithm to create unique + /// names for fresh bound regions + bound_count: u32, + + /// The undo log records actions that might later be undone. + /// + /// Note: when the undo_log is empty, we are not actively + /// snapshotting. When the `start_snapshot()` method is called, we + /// push an OpenSnapshot entry onto the list to indicate that we + /// are now actively snapshotting. The reason for this is that + /// otherwise we end up adding entries for things like the lower + /// bound on a variable and so forth, which can never be rolled + /// back. + undo_log: Vec>, + + /// When we add a R1 == R2 constriant, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when dropck and other such code + /// is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that + /// have been equated but appear distinct. + unification_table: UnificationTable, +} + +pub type VarOrigins = IndexVec; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Default)] +pub struct RegionConstraintData<'tcx> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: BTreeMap, SubregionOrigin<'tcx>>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec>, + + /// A "given" is a relationship that is known to hold. In + /// particular, we often know from closure fn signatures that a + /// particular free region must be a subregion of a region + /// variable: + /// + /// foo.iter().filter(<'a> |x: &'a &'b T| ...) + /// + /// In situations like this, `'b` is in fact a region variable + /// introduced by the call to `iter()`, and `'a` is a bound region + /// on the closure (as indicated by the `<'a>` prefix). If we are + /// naive, we wind up inferring that `'b` must be `'static`, + /// because we require that it be greater than `'a` and we do not + /// know what `'a` is precisely. + /// + /// This hashmap is used to avoid that naive scenario. Basically + /// we record the fact that `'a <= 'b` is implied by the fn + /// signature, and then ignore the constraint when solving + /// equations. This is a bit of a hack but seems to work. + pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, +} + +/// A constraint that influences the inference process. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +pub enum Constraint<'tcx> { + /// One region variable is subregion of another + VarSubVar(RegionVid, RegionVid), + + /// Concrete region is subregion of region variable + RegSubVar(Region<'tcx>, RegionVid), + + /// Region variable is subregion of concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'tcx>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'tcx>, Region<'tcx>), +} + +/// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or +/// associated type) must outlive the region `R`. `T` is known to +/// outlive `RS`. Therefore verify that `R <= RS[i]` for some +/// `i`. Inference variables may be involved (but this verification +/// step doesn't influence inference). +#[derive(Debug)] +pub struct Verify<'tcx> { + pub kind: GenericKind<'tcx>, + pub origin: SubregionOrigin<'tcx>, + pub region: Region<'tcx>, + pub bound: VerifyBound<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum GenericKind<'tcx> { + Param(ty::ParamTy), + Projection(ty::ProjectionTy<'tcx>), +} + +/// When we introduce a verification step, we wish to test that a +/// particular region (let's call it `'min`) meets some bound. +/// The bound is described the by the following grammar: +#[derive(Debug)] +pub enum VerifyBound<'tcx> { + /// B = exists {R} --> some 'r in {R} must outlive 'min + /// + /// Put another way, the subject value is known to outlive all + /// regions in {R}, so if any of those outlives 'min, then the + /// bound is met. + AnyRegion(Vec>), + + /// B = forall {R} --> all 'r in {R} must outlive 'min + /// + /// Put another way, the subject value is known to outlive some + /// region in {R}, so if all of those outlives 'min, then the bound + /// is met. + AllRegions(Vec>), + + /// B = exists {B} --> 'min must meet some bound b in {B} + AnyBound(Vec>), + + /// B = forall {B} --> 'min must meet all bounds b in {B} + AllBounds(Vec>), +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct TwoRegions<'tcx> { + a: Region<'tcx>, + b: Region<'tcx>, +} + +#[derive(Copy, Clone, PartialEq)] +enum UndoLogEntry<'tcx> { + /// Pushed when we start a snapshot. + OpenSnapshot, + + /// Replaces an `OpenSnapshot` when a snapshot is committed, but + /// that snapshot is not the root. If the root snapshot is + /// unrolled, all nested snapshots must be committed. + CommitedSnapshot, + + /// We added `RegionVid` + AddVar(RegionVid), + + /// We added the given `constraint` + AddConstraint(Constraint<'tcx>), + + /// We added the given `verify` + AddVerify(usize), + + /// We added the given `given` + AddGiven(Region<'tcx>, ty::RegionVid), + + /// We added a GLB/LUB "combination variable" + AddCombination(CombineMapType, TwoRegions<'tcx>), + + /// During skolemization, we sometimes purge entries from the undo + /// log in a kind of minisnapshot (unlike other snapshots, this + /// purging actually takes place *on success*). In that case, we + /// replace the corresponding entry with `Noop` so as to avoid the + /// need to do a bunch of swapping. (We can't use `swap_remove` as + /// the order of the vector is important.) + Purged, +} + +#[derive(Copy, Clone, PartialEq)] +enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'tcx> = FxHashMap, RegionVid>; + +pub struct RegionSnapshot { + length: usize, + region_snapshot: unify::Snapshot, + skolemization_count: u32, +} + +/// When working with skolemized regions, we often wish to find all of +/// the regions that are either reachable from a skolemized region, or +/// which can reach a skolemized region, or both. We call such regions +/// *tained* regions. This struct allows you to decide what set of +/// tainted regions you want. +#[derive(Debug)] +pub struct TaintDirections { + incoming: bool, + outgoing: bool, +} + +impl TaintDirections { + pub fn incoming() -> Self { + TaintDirections { + incoming: true, + outgoing: false, + } + } + + pub fn outgoing() -> Self { + TaintDirections { + incoming: false, + outgoing: true, + } + } + + pub fn both() -> Self { + TaintDirections { + incoming: true, + outgoing: true, + } + } +} + +impl<'tcx> RegionConstraintCollector<'tcx> { + pub fn new() -> RegionConstraintCollector<'tcx> { + RegionConstraintCollector { + var_origins: VarOrigins::default(), + data: RegionConstraintData::default(), + lubs: FxHashMap(), + glbs: FxHashMap(), + skolemization_count: 0, + bound_count: 0, + undo_log: Vec::new(), + unification_table: UnificationTable::new(), + } + } + + pub fn var_origins(&self) -> &VarOrigins { + &self.var_origins + } + + /// Once all the constraints have been gathered, extract out the final data. + /// + /// Not legal during a snapshot. + pub fn into_origins_and_data(self) -> (VarOrigins, RegionConstraintData<'tcx>) { + assert!(!self.in_snapshot()); + (self.var_origins, self.data) + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + assert!(!self.in_snapshot()); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintCollector { + var_origins, + data, + lubs, + glbs, + skolemization_count, + bound_count: _, + undo_log: _, + unification_table, + } = self; + + assert_eq!(*skolemization_count, 0); + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + *unification_table = UnificationTable::new(); + for vid in var_origins.indices() { + unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); + } + + mem::replace(data, RegionConstraintData::default()) + } + + fn in_snapshot(&self) -> bool { + !self.undo_log.is_empty() + } + + pub fn start_snapshot(&mut self) -> RegionSnapshot { + let length = self.undo_log.len(); + debug!("RegionConstraintCollector: start_snapshot({})", length); + self.undo_log.push(OpenSnapshot); + RegionSnapshot { + length, + region_snapshot: self.unification_table.snapshot(), + skolemization_count: self.skolemization_count, + } + } + + pub fn commit(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: commit({})", snapshot.length); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + assert!( + self.skolemization_count == snapshot.skolemization_count, + "failed to pop skolemized regions: {} now vs {} at start", + self.skolemization_count, + snapshot.skolemization_count + ); + + if snapshot.length == 0 { + self.undo_log.truncate(0); + } else { + (*self.undo_log)[snapshot.length] = CommitedSnapshot; + } + self.unification_table.commit(snapshot.region_snapshot); + } + + pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + while self.undo_log.len() > snapshot.length + 1 { + let undo_entry = self.undo_log.pop().unwrap(); + self.rollback_undo_entry(undo_entry); + } + let c = self.undo_log.pop().unwrap(); + assert!(c == OpenSnapshot); + self.skolemization_count = snapshot.skolemization_count; + self.unification_table.rollback_to(snapshot.region_snapshot); + } + + fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { + match undo_entry { + OpenSnapshot => { + panic!("Failure to observe stack discipline"); + } + Purged | CommitedSnapshot => { + // nothing to do here + } + AddVar(vid) => { + self.var_origins.pop().unwrap(); + assert_eq!(self.var_origins.len(), vid.index() as usize); + } + AddConstraint(ref constraint) => { + self.data.constraints.remove(constraint); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddGiven(sub, sup) => { + self.data.givens.remove(&(sub, sup)); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } + + pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { + let vid = self.var_origins.push(origin.clone()); + + let u_vid = self.unification_table + .new_key(unify_key::RegionVidKey { min_vid: vid }); + assert_eq!(vid, u_vid); + if self.in_snapshot() { + self.undo_log.push(AddVar(vid)); + } + debug!( + "created new region variable {:?} with origin {:?}", + vid, + origin + ); + return vid; + } + + /// Returns the origin for the given variable. + pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { + self.var_origins[vid].clone() + } + + /// Creates a new skolemized region. Skolemized regions are fresh + /// regions used when performing higher-ranked computations. They + /// must be used in a very particular way and are never supposed + /// to "escape" out into error messages or the code at large. + /// + /// The idea is to always create a snapshot. Skolemized regions + /// can be created in the context of this snapshot, but before the + /// snapshot is committed or rolled back, they must be popped + /// (using `pop_skolemized_regions`), so that their numbers can be + /// recycled. Normally you don't have to think about this: you use + /// the APIs in `higher_ranked/mod.rs`, such as + /// `skolemize_late_bound_regions` and `plug_leaks`, which will + /// guide you on this path (ensure that the `SkolemizationMap` is + /// consumed and you are good). There are also somewhat extensive + /// comments in `higher_ranked/README.md`. + /// + /// The `snapshot` argument to this function is not really used; + /// it's just there to make it explicit which snapshot bounds the + /// skolemized region that results. It should always be the top-most snapshot. + pub fn push_skolemized( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + br: ty::BoundRegion, + snapshot: &RegionSnapshot, + ) -> Region<'tcx> { + assert!(self.in_snapshot()); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + + let sc = self.skolemization_count; + self.skolemization_count = sc + 1; + tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) + } + + /// Removes all the edges to/from the skolemized regions that are + /// in `skols`. This is used after a higher-ranked operation + /// completes to remove all trace of the skolemized regions + /// created in that time. + pub fn pop_skolemized( + &mut self, + _tcx: TyCtxt<'_, '_, 'tcx>, + skols: &FxHashSet>, + snapshot: &RegionSnapshot, + ) { + debug!("pop_skolemized_regions(skols={:?})", skols); + + assert!(self.in_snapshot()); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + assert!( + self.skolemization_count as usize >= skols.len(), + "popping more skolemized variables than actually exist, \ + sc now = {}, skols.len = {}", + self.skolemization_count, + skols.len() + ); + + let last_to_pop = self.skolemization_count; + let first_to_pop = last_to_pop - (skols.len() as u32); + + assert!( + first_to_pop >= snapshot.skolemization_count, + "popping more regions than snapshot contains, \ + sc now = {}, sc then = {}, skols.len = {}", + self.skolemization_count, + snapshot.skolemization_count, + skols.len() + ); + debug_assert! { + skols.iter() + .all(|&k| match *k { + ty::ReSkolemized(index, _) => + index.index >= first_to_pop && + index.index < last_to_pop, + _ => + false + }), + "invalid skolemization keys or keys out of range ({}..{}): {:?}", + snapshot.skolemization_count, + self.skolemization_count, + skols + } + + let constraints_to_kill: Vec = self.undo_log + .iter() + .enumerate() + .rev() + .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) + .map(|(index, _)| index) + .collect(); + + for index in constraints_to_kill { + let undo_entry = mem::replace(&mut self.undo_log[index], Purged); + self.rollback_undo_entry(undo_entry); + } + + self.skolemization_count = snapshot.skolemization_count; + return; + + fn kill_constraint<'tcx>( + skols: &FxHashSet>, + undo_entry: &UndoLogEntry<'tcx>, + ) -> bool { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(..)) => false, + &AddConstraint(Constraint::RegSubVar(a, _)) => skols.contains(&a), + &AddConstraint(Constraint::VarSubReg(_, b)) => skols.contains(&b), + &AddConstraint(Constraint::RegSubReg(a, b)) => { + skols.contains(&a) || skols.contains(&b) + } + &AddGiven(..) => false, + &AddVerify(_) => false, + &AddCombination(_, ref two_regions) => { + skols.contains(&two_regions.a) || skols.contains(&two_regions.b) + } + &AddVar(..) | &OpenSnapshot | &Purged | &CommitedSnapshot => false, + } + } + } + + pub fn new_bound( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + debruijn: ty::DebruijnIndex, + ) -> Region<'tcx> { + // Creates a fresh bound variable for use in GLB computations. + // See discussion of GLB computation in the large comment at + // the top of this file for more details. + // + // This computation is potentially wrong in the face of + // rollover. It's conceivable, if unlikely, that one might + // wind up with accidental capture for nested functions in + // that case, if the outer function had bound regions created + // a very long time before and the inner function somehow + // wound up rolling over such that supposedly fresh + // identifiers were in fact shadowed. For now, we just assert + // that there is no rollover -- eventually we should try to be + // robust against this possibility, either by checking the set + // of bound identifiers that appear in a given expression and + // ensure that we generate one that is distinct, or by + // changing the representation of bound regions in a fn + // declaration + + let sc = self.bound_count; + self.bound_count = sc + 1; + + if sc >= self.bound_count { + bug!("rollover in RegionInference new_bound()"); + } + + tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) + } + + fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { + // cannot add constraints once regions are resolved + debug!( + "RegionConstraintCollector: add_constraint({:?})", + constraint + ); + + // never overwrite an existing (constraint, origin) - only insert one if it isn't + // present in the map yet. This prevents origins from outside the snapshot being + // replaced with "less informative" origins e.g. during calls to `can_eq` + let in_snapshot = self.in_snapshot(); + let undo_log = &mut self.undo_log; + self.data.constraints.entry(constraint).or_insert_with(|| { + if in_snapshot { + undo_log.push(AddConstraint(constraint)); + } + origin + }); + } + + fn add_verify(&mut self, verify: Verify<'tcx>) { + // cannot add verifys once regions are resolved + debug!("RegionConstraintCollector: add_verify({:?})", verify); + + // skip no-op cases known to be satisfied + match verify.bound { + VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { + return; + } + _ => {} + } + + let index = self.data.verifys.len(); + self.data.verifys.push(verify); + if self.in_snapshot() { + self.undo_log.push(AddVerify(index)); + } + } + + pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { + // cannot add givens once regions are resolved + if self.data.givens.insert((sub, sup)) { + debug!("add_given({:?} <= {:?})", sub, sup); + + if self.in_snapshot() { + self.undo_log.push(AddGiven(sub, sup)); + } + } + } + + pub fn make_eqregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + if sub != sup { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(origin.clone(), sub, sup); + self.make_subregion(origin, sup, sub); + + if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { + self.unification_table.union(sub, sup); + } + } + } + + pub fn make_subregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + // cannot add constraints once regions are resolved + debug!( + "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}", + sub, + sup, + origin + ); + + match (sub, sup) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) => { + span_bug!( + origin.span(), + "cannot relate bound region: {:?} <= {:?}", + sub, + sup + ); + } + (_, &ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (&ReVar(sub_id), &ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin); + } + (_, &ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin); + } + (&ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup), origin); + } + } + } + + /// See `Verify::VerifyGenericBound` + pub fn verify_generic_bound( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + sub: Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.add_verify(Verify { + kind, + origin, + region: sub, + bound, + }); + } + + pub fn lub_regions( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); + match (a, b) { + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + _ if a == b => { + a // LUB(a,a) = a + } + + _ => self.combine_vars(tcx, Lub, a, b, origin.clone()), + } + } + + pub fn glb_regions( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); + match (a, b) { + (&ReStatic, r) | (r, &ReStatic) => { + r // static lives longer than everything else + } + + _ if a == b => { + a // GLB(a,a) = a + } + + _ => self.combine_vars(tcx, Glb, a, b, origin.clone()), + } + } + + pub fn opportunistic_resolve_var( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + rid: RegionVid, + ) -> ty::Region<'tcx> { + let vid = self.unification_table.find_value(rid).min_vid; + tcx.mk_region(ty::ReVar(vid)) + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> { + match t { + Glb => &mut self.glbs, + Lub => &mut self.lubs, + } + } + + fn combine_vars( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + t: CombineMapType, + a: Region<'tcx>, + b: Region<'tcx>, + origin: SubregionOrigin<'tcx>, + ) -> Region<'tcx> { + let vars = TwoRegions { a: a, b: b }; + if let Some(&c) = self.combine_map(t).get(&vars) { + return tcx.mk_region(ReVar(c)); + } + let c = self.new_region_var(MiscVariable(origin.span())); + self.combine_map(t).insert(vars, c); + if self.in_snapshot() { + self.undo_log.push(AddCombination(t, vars)); + } + let new_r = tcx.mk_region(ReVar(c)); + for &old_r in &[a, b] { + match t { + Glb => self.make_subregion(origin.clone(), new_r, old_r), + Lub => self.make_subregion(origin.clone(), old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { + self.undo_log[mark.length..] + .iter() + .filter_map(|&elt| match elt { + AddVar(vid) => Some(vid), + _ => None, + }) + .collect() + } + + /// Computes all regions that have been related to `r0` since the + /// mark `mark` was made---`r0` itself will be the first + /// entry. The `directions` parameter controls what kind of + /// relations are considered. For example, one can say that only + /// "incoming" edges to `r0` are desired, in which case one will + /// get the set of regions `{r|r <= r0}`. This is used when + /// checking whether skolemized regions are being improperly + /// related to other regions. + pub fn tainted( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + mark: &RegionSnapshot, + r0: Region<'tcx>, + directions: TaintDirections, + ) -> FxHashSet> { + debug!( + "tainted(mark={:?}, r0={:?}, directions={:?})", + mark, + r0, + directions + ); + + // `result_set` acts as a worklist: we explore all outgoing + // edges and add any new regions we find to result_set. This + // is not a terribly efficient implementation. + let mut taint_set = taint::TaintSet::new(directions, r0); + taint_set.fixed_point(tcx, &self.undo_log[mark.length..], &self.data.verifys); + debug!("tainted: result={:?}", taint_set); + return taint_set.into_set(); + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "RegionSnapshot(length={},skolemization={})", + self.length, + self.skolemization_count + ) + } +} + +impl<'tcx> fmt::Debug for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{:?}", p), + GenericKind::Projection(ref p) => write!(f, "{:?}", p), + } + } +} + +impl<'tcx> fmt::Display for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{}", p), + GenericKind::Projection(ref p) => write!(f, "{}", p), + } + } +} + +impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { + pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + match *self { + GenericKind::Param(ref p) => p.to_ty(tcx), + GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), + } + } +} + +impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { + fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) { + match self { + &VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs { + f(r); + }, + + &VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs { + b.for_each_region(f); + }, + } + } + + pub fn must_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), + &VerifyBound::AllRegions(ref bs) => bs.is_empty(), + &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), + &VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty), + &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } + + pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { + if self.must_hold() && vb.must_hold() { + self + } else if self.cannot_hold() && vb.cannot_hold() { + self + } else { + VerifyBound::AllBounds(vec![self, vb]) + } + } +} + +impl<'tcx> RegionConstraintData<'tcx> { + /// True if this region constraint data contains no constraints. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { + constraints, + verifys, + givens, + } = self; + constraints.is_empty() && verifys.is_empty() && givens.is_empty() + } +} diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs new file mode 100644 index 0000000000000..ee45f7bd82801 --- /dev/null +++ b/src/librustc/infer/region_constraints/taint.rs @@ -0,0 +1,96 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::*; + +#[derive(Debug)] +pub(super) struct TaintSet<'tcx> { + directions: TaintDirections, + regions: FxHashSet> +} + +impl<'tcx> TaintSet<'tcx> { + pub(super) fn new(directions: TaintDirections, + initial_region: ty::Region<'tcx>) + -> Self { + let mut regions = FxHashSet(); + regions.insert(initial_region); + TaintSet { directions: directions, regions: regions } + } + + pub(super) fn fixed_point(&mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + undo_log: &[UndoLogEntry<'tcx>], + verifys: &[Verify<'tcx>]) { + let mut prev_len = 0; + while prev_len < self.len() { + debug!("tainted: prev_len = {:?} new_len = {:?}", + prev_len, self.len()); + + prev_len = self.len(); + + for undo_entry in undo_log { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), + tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::RegSubVar(a, b)) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::VarSubReg(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), b); + } + &AddConstraint(Constraint::RegSubReg(a, b)) => { + self.add_edge(a, b); + } + &AddGiven(a, b) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddVerify(i) => { + verifys[i].bound.for_each_region(&mut |b| { + self.add_edge(verifys[i].region, b); + }); + } + &Purged | + &AddCombination(..) | + &AddVar(..) | + &OpenSnapshot | + &CommitedSnapshot => {} + } + } + } + } + + pub(super) fn into_set(self) -> FxHashSet> { + self.regions + } + + fn len(&self) -> usize { + self.regions.len() + } + + fn add_edge(&mut self, + source: ty::Region<'tcx>, + target: ty::Region<'tcx>) { + if self.directions.incoming { + if self.regions.contains(&target) { + self.regions.insert(source); + } + } + + if self.directions.outgoing { + if self.regions.contains(&source) { + self.regions.insert(target); + } + } + } +} + diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs deleted file mode 100644 index 8351be490767a..0000000000000 --- a/src/librustc/infer/region_inference/mod.rs +++ /dev/null @@ -1,1637 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! See README.md - -pub use self::Constraint::*; -pub use self::UndoLogEntry::*; -pub use self::CombineMapType::*; -pub use self::RegionResolutionError::*; -pub use self::VarValue::*; - -use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable}; -use super::unify_key; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; -use rustc_data_structures::unify::{self, UnificationTable}; -use middle::free_region::RegionRelations; -use ty::{self, Ty, TyCtxt}; -use ty::{Region, RegionVid}; -use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; -use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh}; - -use std::cell::{Cell, RefCell}; -use std::fmt; -use std::mem; -use std::u32; - -mod graphviz; - -/// A constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub enum Constraint<'tcx> { - /// One region variable is subregion of another - ConstrainVarSubVar(RegionVid, RegionVid), - - /// Concrete region is subregion of region variable - ConstrainRegSubVar(Region<'tcx>, RegionVid), - - /// Region variable is subregion of concrete region. This does not - /// directly affect inference, but instead is checked after - /// inference is complete. - ConstrainVarSubReg(RegionVid, Region<'tcx>), - - /// A constraint where neither side is a variable. This does not - /// directly affect inference, but instead is checked after - /// inference is complete. - ConstrainRegSubReg(Region<'tcx>, Region<'tcx>), -} - -/// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or -/// associated type) must outlive the region `R`. `T` is known to -/// outlive `RS`. Therefore verify that `R <= RS[i]` for some -/// `i`. Inference variables may be involved (but this verification -/// step doesn't influence inference). -#[derive(Debug)] -pub struct Verify<'tcx> { - kind: GenericKind<'tcx>, - origin: SubregionOrigin<'tcx>, - region: Region<'tcx>, - bound: VerifyBound<'tcx>, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum GenericKind<'tcx> { - Param(ty::ParamTy), - Projection(ty::ProjectionTy<'tcx>), -} - -/// When we introduce a verification step, we wish to test that a -/// particular region (let's call it `'min`) meets some bound. -/// The bound is described the by the following grammar: -#[derive(Debug)] -pub enum VerifyBound<'tcx> { - /// B = exists {R} --> some 'r in {R} must outlive 'min - /// - /// Put another way, the subject value is known to outlive all - /// regions in {R}, so if any of those outlives 'min, then the - /// bound is met. - AnyRegion(Vec>), - - /// B = forall {R} --> all 'r in {R} must outlive 'min - /// - /// Put another way, the subject value is known to outlive some - /// region in {R}, so if all of those outlives 'min, then the bound - /// is met. - AllRegions(Vec>), - - /// B = exists {B} --> 'min must meet some bound b in {B} - AnyBound(Vec>), - - /// B = forall {B} --> 'min must meet all bounds b in {B} - AllBounds(Vec>), -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct TwoRegions<'tcx> { - a: Region<'tcx>, - b: Region<'tcx>, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum UndoLogEntry<'tcx> { - /// Pushed when we start a snapshot. - OpenSnapshot, - - /// Replaces an `OpenSnapshot` when a snapshot is committed, but - /// that snapshot is not the root. If the root snapshot is - /// unrolled, all nested snapshots must be committed. - CommitedSnapshot, - - /// We added `RegionVid` - AddVar(RegionVid), - - /// We added the given `constraint` - AddConstraint(Constraint<'tcx>), - - /// We added the given `verify` - AddVerify(usize), - - /// We added the given `given` - AddGiven(Region<'tcx>, ty::RegionVid), - - /// We added a GLB/LUB "combination variable" - AddCombination(CombineMapType, TwoRegions<'tcx>), - - /// During skolemization, we sometimes purge entries from the undo - /// log in a kind of minisnapshot (unlike other snapshots, this - /// purging actually takes place *on success*). In that case, we - /// replace the corresponding entry with `Noop` so as to avoid the - /// need to do a bunch of swapping. (We can't use `swap_remove` as - /// the order of the vector is important.) - Purged, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum CombineMapType { - Lub, - Glb, -} - -#[derive(Clone, Debug)] -pub enum RegionResolutionError<'tcx> { - /// `ConcreteFailure(o, a, b)`: - /// - /// `o` requires that `a <= b`, but this does not hold - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - - /// `GenericBoundFailure(p, s, a) - /// - /// The parameter/associated-type `p` must be known to outlive the lifetime - /// `a` (but none of the known bounds are sufficient). - GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), - - /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: - /// - /// Could not infer a value for `v` because `sub_r <= v` (due to - /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and - /// `sub_r <= sup_r` does not hold. - SubSupConflict(RegionVariableOrigin, - SubregionOrigin<'tcx>, - Region<'tcx>, - SubregionOrigin<'tcx>, - Region<'tcx>), -} - -#[derive(Clone, Debug)] -pub enum ProcessedErrorOrigin<'tcx> { - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - VariableFailure(RegionVariableOrigin), -} - -pub type CombineMap<'tcx> = FxHashMap, RegionVid>; - -pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - var_origins: RefCell>, - - /// Constraints of the form `A <= B` introduced by the region - /// checker. Here at least one of `A` and `B` must be a region - /// variable. - constraints: RefCell, SubregionOrigin<'tcx>>>, - - /// A "verify" is something that we need to verify after inference is - /// done, but which does not directly affect inference in any way. - /// - /// An example is a `A <= B` where neither `A` nor `B` are - /// inference variables. - verifys: RefCell>>, - - /// A "given" is a relationship that is known to hold. In particular, - /// we often know from closure fn signatures that a particular free - /// region must be a subregion of a region variable: - /// - /// foo.iter().filter(<'a> |x: &'a &'b T| ...) - /// - /// In situations like this, `'b` is in fact a region variable - /// introduced by the call to `iter()`, and `'a` is a bound region - /// on the closure (as indicated by the `<'a>` prefix). If we are - /// naive, we wind up inferring that `'b` must be `'static`, - /// because we require that it be greater than `'a` and we do not - /// know what `'a` is precisely. - /// - /// This hashmap is used to avoid that naive scenario. Basically we - /// record the fact that `'a <= 'b` is implied by the fn signature, - /// and then ignore the constraint when solving equations. This is - /// a bit of a hack but seems to work. - givens: RefCell, ty::RegionVid)>>, - - lubs: RefCell>, - glbs: RefCell>, - skolemization_count: Cell, - bound_count: Cell, - - /// The undo log records actions that might later be undone. - /// - /// Note: when the undo_log is empty, we are not actively - /// snapshotting. When the `start_snapshot()` method is called, we - /// push an OpenSnapshot entry onto the list to indicate that we - /// are now actively snapshotting. The reason for this is that - /// otherwise we end up adding entries for things like the lower - /// bound on a variable and so forth, which can never be rolled - /// back. - undo_log: RefCell>>, - - unification_table: RefCell>, - - /// This contains the results of inference. It begins as an empty - /// option and only acquires a value after inference is complete. - values: RefCell>>>, -} - -pub struct RegionSnapshot { - length: usize, - region_snapshot: unify::Snapshot, - skolemization_count: u32, -} - -/// When working with skolemized regions, we often wish to find all of -/// the regions that are either reachable from a skolemized region, or -/// which can reach a skolemized region, or both. We call such regions -/// *tained* regions. This struct allows you to decide what set of -/// tainted regions you want. -#[derive(Debug)] -pub struct TaintDirections { - incoming: bool, - outgoing: bool, -} - -impl TaintDirections { - pub fn incoming() -> Self { - TaintDirections { incoming: true, outgoing: false } - } - - pub fn outgoing() -> Self { - TaintDirections { incoming: false, outgoing: true } - } - - pub fn both() -> Self { - TaintDirections { incoming: true, outgoing: true } - } -} - -struct TaintSet<'tcx> { - directions: TaintDirections, - regions: FxHashSet> -} - -impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { - fn new(directions: TaintDirections, - initial_region: ty::Region<'tcx>) - -> Self { - let mut regions = FxHashSet(); - regions.insert(initial_region); - TaintSet { directions: directions, regions: regions } - } - - fn fixed_point(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - undo_log: &[UndoLogEntry<'tcx>], - verifys: &[Verify<'tcx>]) { - let mut prev_len = 0; - while prev_len < self.len() { - debug!("tainted: prev_len = {:?} new_len = {:?}", - prev_len, self.len()); - - prev_len = self.len(); - - for undo_entry in undo_log { - match undo_entry { - &AddConstraint(ConstrainVarSubVar(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), - tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainRegSubVar(a, b)) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainVarSubReg(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), b); - } - &AddConstraint(ConstrainRegSubReg(a, b)) => { - self.add_edge(a, b); - } - &AddGiven(a, b) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddVerify(i) => { - verifys[i].bound.for_each_region(&mut |b| { - self.add_edge(verifys[i].region, b); - }); - } - &Purged | - &AddCombination(..) | - &AddVar(..) | - &OpenSnapshot | - &CommitedSnapshot => {} - } - } - } - } - - fn into_set(self) -> FxHashSet> { - self.regions - } - - fn len(&self) -> usize { - self.regions.len() - } - - fn add_edge(&mut self, - source: ty::Region<'tcx>, - target: ty::Region<'tcx>) { - if self.directions.incoming { - if self.regions.contains(&target) { - self.regions.insert(source); - } - } - - if self.directions.outgoing { - if self.regions.contains(&source) { - self.regions.insert(target); - } - } - } -} - -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> { - RegionVarBindings { - tcx, - var_origins: RefCell::new(Vec::new()), - values: RefCell::new(None), - constraints: RefCell::new(FxHashMap()), - verifys: RefCell::new(Vec::new()), - givens: RefCell::new(FxHashSet()), - lubs: RefCell::new(FxHashMap()), - glbs: RefCell::new(FxHashMap()), - skolemization_count: Cell::new(0), - bound_count: Cell::new(0), - undo_log: RefCell::new(Vec::new()), - unification_table: RefCell::new(UnificationTable::new()), - } - } - - fn in_snapshot(&self) -> bool { - !self.undo_log.borrow().is_empty() - } - - pub fn start_snapshot(&self) -> RegionSnapshot { - let length = self.undo_log.borrow().len(); - debug!("RegionVarBindings: start_snapshot({})", length); - self.undo_log.borrow_mut().push(OpenSnapshot); - RegionSnapshot { - length, - region_snapshot: self.unification_table.borrow_mut().snapshot(), - skolemization_count: self.skolemization_count.get(), - } - } - - pub fn commit(&self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: commit({})", snapshot.length); - assert!(self.undo_log.borrow().len() > snapshot.length); - assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() == snapshot.skolemization_count, - "failed to pop skolemized regions: {} now vs {} at start", - self.skolemization_count.get(), - snapshot.skolemization_count); - - let mut undo_log = self.undo_log.borrow_mut(); - if snapshot.length == 0 { - undo_log.truncate(0); - } else { - (*undo_log)[snapshot.length] = CommitedSnapshot; - } - self.unification_table.borrow_mut().commit(snapshot.region_snapshot); - } - - pub fn rollback_to(&self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: rollback_to({:?})", snapshot); - let mut undo_log = self.undo_log.borrow_mut(); - assert!(undo_log.len() > snapshot.length); - assert!((*undo_log)[snapshot.length] == OpenSnapshot); - while undo_log.len() > snapshot.length + 1 { - self.rollback_undo_entry(undo_log.pop().unwrap()); - } - let c = undo_log.pop().unwrap(); - assert!(c == OpenSnapshot); - self.skolemization_count.set(snapshot.skolemization_count); - self.unification_table.borrow_mut() - .rollback_to(snapshot.region_snapshot); - } - - pub fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) { - match undo_entry { - OpenSnapshot => { - panic!("Failure to observe stack discipline"); - } - Purged | CommitedSnapshot => { - // nothing to do here - } - AddVar(vid) => { - let mut var_origins = self.var_origins.borrow_mut(); - var_origins.pop().unwrap(); - assert_eq!(var_origins.len(), vid.index as usize); - } - AddConstraint(ref constraint) => { - self.constraints.borrow_mut().remove(constraint); - } - AddVerify(index) => { - self.verifys.borrow_mut().pop(); - assert_eq!(self.verifys.borrow().len(), index); - } - AddGiven(sub, sup) => { - self.givens.borrow_mut().remove(&(sub, sup)); - } - AddCombination(Glb, ref regions) => { - self.glbs.borrow_mut().remove(regions); - } - AddCombination(Lub, ref regions) => { - self.lubs.borrow_mut().remove(regions); - } - } - } - - pub fn num_vars(&self) -> u32 { - let len = self.var_origins.borrow().len(); - // enforce no overflow - assert!(len as u32 as usize == len); - len as u32 - } - - pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid { - let vid = RegionVid { index: self.num_vars() }; - self.var_origins.borrow_mut().push(origin.clone()); - - let u_vid = self.unification_table.borrow_mut().new_key( - unify_key::RegionVidKey { min_vid: vid } - ); - assert_eq!(vid, u_vid); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVar(vid)); - } - debug!("created new region variable {:?} with origin {:?}", - vid, - origin); - return vid; - } - - pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.var_origins.borrow()[vid.index as usize].clone() - } - - /// Creates a new skolemized region. Skolemized regions are fresh - /// regions used when performing higher-ranked computations. They - /// must be used in a very particular way and are never supposed - /// to "escape" out into error messages or the code at large. - /// - /// The idea is to always create a snapshot. Skolemized regions - /// can be created in the context of this snapshot, but before the - /// snapshot is committed or rolled back, they must be popped - /// (using `pop_skolemized_regions`), so that their numbers can be - /// recycled. Normally you don't have to think about this: you use - /// the APIs in `higher_ranked/mod.rs`, such as - /// `skolemize_late_bound_regions` and `plug_leaks`, which will - /// guide you on this path (ensure that the `SkolemizationMap` is - /// consumed and you are good). There are also somewhat extensive - /// comments in `higher_ranked/README.md`. - /// - /// The `snapshot` argument to this function is not really used; - /// it's just there to make it explicit which snapshot bounds the - /// skolemized region that results. It should always be the top-most snapshot. - pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) - -> Region<'tcx> { - assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); - - let sc = self.skolemization_count.get(); - self.skolemization_count.set(sc + 1); - self.tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) - } - - /// Removes all the edges to/from the skolemized regions that are - /// in `skols`. This is used after a higher-ranked operation - /// completes to remove all trace of the skolemized regions - /// created in that time. - pub fn pop_skolemized(&self, - skols: &FxHashSet>, - snapshot: &RegionSnapshot) { - debug!("pop_skolemized_regions(skols={:?})", skols); - - assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() as usize >= skols.len(), - "popping more skolemized variables than actually exist, \ - sc now = {}, skols.len = {}", - self.skolemization_count.get(), - skols.len()); - - let last_to_pop = self.skolemization_count.get(); - let first_to_pop = last_to_pop - (skols.len() as u32); - - assert!(first_to_pop >= snapshot.skolemization_count, - "popping more regions than snapshot contains, \ - sc now = {}, sc then = {}, skols.len = {}", - self.skolemization_count.get(), - snapshot.skolemization_count, - skols.len()); - debug_assert! { - skols.iter() - .all(|&k| match *k { - ty::ReSkolemized(index, _) => - index.index >= first_to_pop && - index.index < last_to_pop, - _ => - false - }), - "invalid skolemization keys or keys out of range ({}..{}): {:?}", - snapshot.skolemization_count, - self.skolemization_count.get(), - skols - } - - let mut undo_log = self.undo_log.borrow_mut(); - - let constraints_to_kill: Vec = - undo_log.iter() - .enumerate() - .rev() - .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) - .map(|(index, _)| index) - .collect(); - - for index in constraints_to_kill { - let undo_entry = mem::replace(&mut undo_log[index], Purged); - self.rollback_undo_entry(undo_entry); - } - - self.skolemization_count.set(snapshot.skolemization_count); - return; - - fn kill_constraint<'tcx>(skols: &FxHashSet>, - undo_entry: &UndoLogEntry<'tcx>) - -> bool { - match undo_entry { - &AddConstraint(ConstrainVarSubVar(..)) => - false, - &AddConstraint(ConstrainRegSubVar(a, _)) => - skols.contains(&a), - &AddConstraint(ConstrainVarSubReg(_, b)) => - skols.contains(&b), - &AddConstraint(ConstrainRegSubReg(a, b)) => - skols.contains(&a) || skols.contains(&b), - &AddGiven(..) => - false, - &AddVerify(_) => - false, - &AddCombination(_, ref two_regions) => - skols.contains(&two_regions.a) || - skols.contains(&two_regions.b), - &AddVar(..) | - &OpenSnapshot | - &Purged | - &CommitedSnapshot => - false, - } - } - - } - - pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region<'tcx> { - // Creates a fresh bound variable for use in GLB computations. - // See discussion of GLB computation in the large comment at - // the top of this file for more details. - // - // This computation is potentially wrong in the face of - // rollover. It's conceivable, if unlikely, that one might - // wind up with accidental capture for nested functions in - // that case, if the outer function had bound regions created - // a very long time before and the inner function somehow - // wound up rolling over such that supposedly fresh - // identifiers were in fact shadowed. For now, we just assert - // that there is no rollover -- eventually we should try to be - // robust against this possibility, either by checking the set - // of bound identifiers that appear in a given expression and - // ensure that we generate one that is distinct, or by - // changing the representation of bound regions in a fn - // declaration - - let sc = self.bound_count.get(); - self.bound_count.set(sc + 1); - - if sc >= self.bound_count.get() { - bug!("rollover in RegionInference new_bound()"); - } - - self.tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) - } - - fn values_are_none(&self) -> bool { - self.values.borrow().is_none() - } - - fn add_constraint(&self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: add_constraint({:?})", constraint); - - if self.constraints.borrow_mut().insert(constraint, origin).is_none() { - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddConstraint(constraint)); - } - } - } - - fn add_verify(&self, verify: Verify<'tcx>) { - // cannot add verifys once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: add_verify({:?})", verify); - - // skip no-op cases known to be satisfied - match verify.bound { - VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { return; } - _ => { } - } - - let mut verifys = self.verifys.borrow_mut(); - let index = verifys.len(); - verifys.push(verify); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVerify(index)); - } - } - - pub fn add_given(&self, sub: Region<'tcx>, sup: ty::RegionVid) { - // cannot add givens once regions are resolved - assert!(self.values_are_none()); - - let mut givens = self.givens.borrow_mut(); - if givens.insert((sub, sup)) { - debug!("add_given({:?} <= {:?})", sub, sup); - - self.undo_log.borrow_mut().push(AddGiven(sub, sup)); - } - } - - pub fn make_eqregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { - if sub != sup { - // Eventually, it would be nice to add direct support for - // equating regions. - self.make_subregion(origin.clone(), sub, sup); - self.make_subregion(origin, sup, sub); - - if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { - self.unification_table.borrow_mut().union(sub, sup); - } - } - } - - pub fn make_subregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", - sub, - sup, - origin); - - match (sub, sup) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) => { - span_bug!(origin.span(), - "cannot relate bound region: {:?} <= {:?}", - sub, - sup); - } - (_, &ReStatic) => { - // all regions are subregions of static, so we can ignore this - } - (&ReVar(sub_id), &ReVar(sup_id)) => { - self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin); - } - (_, &ReVar(sup_id)) => { - self.add_constraint(ConstrainRegSubVar(sub, sup_id), origin); - } - (&ReVar(sub_id), _) => { - self.add_constraint(ConstrainVarSubReg(sub_id, sup), origin); - } - _ => { - self.add_constraint(ConstrainRegSubReg(sub, sup), origin); - } - } - } - - /// See `Verify::VerifyGenericBound` - pub fn verify_generic_bound(&self, - origin: SubregionOrigin<'tcx>, - kind: GenericKind<'tcx>, - sub: Region<'tcx>, - bound: VerifyBound<'tcx>) { - self.add_verify(Verify { - kind, - origin, - region: sub, - bound, - }); - } - - pub fn lub_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); - match (a, b) { - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - _ if a == b => { - a // LUB(a,a) = a - } - - _ => { - self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), old_r, new_r) - }) - } - } - } - - pub fn glb_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); - match (a, b) { - (&ReStatic, r) | (r, &ReStatic) => { - r // static lives longer than everything else - } - - _ if a == b => { - a // GLB(a,a) = a - } - - _ => { - self.combine_vars(Glb, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), new_r, old_r) - }) - } - } - } - - pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - match *self.values.borrow() { - None => { - span_bug!((*self.var_origins.borrow())[rid.index as usize].span(), - "attempt to resolve region variable before values have \ - been computed!") - } - Some(ref values) => { - let r = lookup(self.tcx, values, rid); - debug!("resolve_var({:?}) = {:?}", rid, r); - r - } - } - } - - pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - let vid = self.unification_table.borrow_mut().find_value(rid).min_vid; - self.tcx.mk_region(ty::ReVar(vid)) - } - - fn combine_map(&self, t: CombineMapType) -> &RefCell> { - match t { - Glb => &self.glbs, - Lub => &self.lubs, - } - } - - pub fn combine_vars(&self, - t: CombineMapType, - a: Region<'tcx>, - b: Region<'tcx>, - origin: SubregionOrigin<'tcx>, - mut relate: F) - -> Region<'tcx> - where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>) - { - let vars = TwoRegions { a: a, b: b }; - if let Some(&c) = self.combine_map(t).borrow().get(&vars) { - return self.tcx.mk_region(ReVar(c)); - } - let c = self.new_region_var(MiscVariable(origin.span())); - self.combine_map(t).borrow_mut().insert(vars, c); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddCombination(t, vars)); - } - relate(self, a, self.tcx.mk_region(ReVar(c))); - relate(self, b, self.tcx.mk_region(ReVar(c))); - debug!("combine_vars() c={:?}", c); - self.tcx.mk_region(ReVar(c)) - } - - pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { - self.undo_log.borrow()[mark.length..] - .iter() - .filter_map(|&elt| { - match elt { - AddVar(vid) => Some(vid), - _ => None, - } - }) - .collect() - } - - /// Computes all regions that have been related to `r0` since the - /// mark `mark` was made---`r0` itself will be the first - /// entry. The `directions` parameter controls what kind of - /// relations are considered. For example, one can say that only - /// "incoming" edges to `r0` are desired, in which case one will - /// get the set of regions `{r|r <= r0}`. This is used when - /// checking whether skolemized regions are being improperly - /// related to other regions. - pub fn tainted(&self, - mark: &RegionSnapshot, - r0: Region<'tcx>, - directions: TaintDirections) - -> FxHashSet> { - debug!("tainted(mark={:?}, r0={:?}, directions={:?})", - mark, r0, directions); - - // `result_set` acts as a worklist: we explore all outgoing - // edges and add any new regions we find to result_set. This - // is not a terribly efficient implementation. - let mut taint_set = TaintSet::new(directions, r0); - taint_set.fixed_point(self.tcx, - &self.undo_log.borrow()[mark.length..], - &self.verifys.borrow()); - debug!("tainted: result={:?}", taint_set.regions); - return taint_set.into_set(); - } - - /// This function performs the actual region resolution. It must be - /// called after all constraints have been added. It performs a - /// fixed-point iteration to find region values which satisfy all - /// constraints, assuming such values can be found; if they cannot, - /// errors are reported. - pub fn resolve_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>) - -> Vec> { - debug!("RegionVarBindings: resolve_regions()"); - let mut errors = vec![]; - let v = self.infer_variable_values(region_rels, &mut errors); - *self.values.borrow_mut() = Some(v); - errors - } - - fn lub_concrete_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - match (a, b) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) | - (&ReErased, _) | - (_, &ReErased) => { - bug!("cannot relate region: LUB({:?}, {:?})", a, b); - } - - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - (&ReEmpty, r) | (r, &ReEmpty) => { - r // everything lives longer than empty - } - - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { - span_bug!((*self.var_origins.borrow())[v_id.index as usize].span(), - "lub_concrete_regions invoked with non-concrete \ - regions: {:?}, {:?}", - a, - b); - } - - (&ReEarlyBound(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReEarlyBound(_)) | - (&ReFree(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReFree(_)) => { - // A "free" region can be interpreted as "some region - // at least as big as fr.scope". So, we can - // reasonably compare free regions and scopes: - let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - region_rels.region_scope_tree.early_free_scope(self.tcx, br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - region_rels.region_scope_tree.free_scope(self.tcx, fr) - } - _ => bug!() - }; - let r_id = region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); - if r_id == fr_scope { - // if the free region's scope `fr.scope` is bigger than - // the scope region `s_id`, then the LUB is the free - // region itself: - match (a, b) { - (_, &ReScope(_)) => return a, - (&ReScope(_), _) => return b, - _ => bug!() - } - } - - // otherwise, we don't know what the free region is, - // so we must conservatively say the LUB is static: - self.tcx.types.re_static - } - - (&ReScope(a_id), &ReScope(b_id)) => { - // The region corresponding to an outer block is a - // subtype of the region corresponding to an inner - // block. - let lub = region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); - self.tcx.mk_region(ReScope(lub)) - } - - (&ReEarlyBound(_), &ReEarlyBound(_)) | - (&ReFree(_), &ReEarlyBound(_)) | - (&ReEarlyBound(_), &ReFree(_)) | - (&ReFree(_), &ReFree(_)) => { - region_rels.lub_free_regions(a, b) - } - - // For these types, we cannot define any additional - // relationship: - (&ReSkolemized(..), _) | - (_, &ReSkolemized(..)) => { - if a == b { - a - } else { - self.tcx.types.re_static - } - } - } - } -} - -// ______________________________________________________________________ - -#[derive(Copy, Clone, Debug)] -pub enum VarValue<'tcx> { - Value(Region<'tcx>), - ErrorValue, -} - -struct RegionAndOrigin<'tcx> { - region: Region<'tcx>, - origin: SubregionOrigin<'tcx>, -} - -type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; - -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - fn infer_variable_values(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - errors: &mut Vec>) - -> Vec> { - let mut var_data = self.construct_var_data(); - - // Dorky hack to cause `dump_constraints` to only get called - // if debug mode is enabled: - debug!("----() End constraint listing (context={:?}) {:?}---", - region_rels.context, - self.dump_constraints(region_rels)); - graphviz::maybe_print_constraints_for(self, region_rels); - - let graph = self.construct_graph(); - self.expand_givens(&graph); - self.expansion(region_rels, &mut var_data); - self.collect_errors(region_rels, &mut var_data, errors); - self.collect_var_errors(region_rels, &var_data, &graph, errors); - var_data - } - - fn construct_var_data(&self) -> Vec> { - (0..self.num_vars() as usize) - .map(|_| Value(self.tcx.types.re_empty)) - .collect() - } - - fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { - debug!("----() Start constraint listing (context={:?}) ()----", - free_regions.context); - for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() { - debug!("Constraint {} => {:?}", idx, constraint); - } - } - - fn expand_givens(&self, graph: &RegionGraph) { - // Givens are a kind of horrible hack to account for - // constraints like 'c <= '0 that are known to hold due to - // closure signatures (see the comment above on the `givens` - // field). They should go away. But until they do, the role - // of this fn is to account for the transitive nature: - // - // Given 'c <= '0 - // and '0 <= '1 - // then 'c <= '1 - - let mut givens = self.givens.borrow_mut(); - let seeds: Vec<_> = givens.iter().cloned().collect(); - for (r, vid) in seeds { - let seed_index = NodeIndex(vid.index as usize); - for succ_index in graph.depth_traverse(seed_index, OUTGOING) { - let succ_index = succ_index.0 as u32; - if succ_index < self.num_vars() { - let succ_vid = RegionVid { index: succ_index }; - givens.insert((r, succ_vid)); - } - } - } - } - - fn expansion(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &mut [VarValue<'tcx>]) { - self.iterate_until_fixed_point("Expansion", |constraint, origin| { - debug!("expansion: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(a_region, b_vid) => { - let b_data = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_data) - } - ConstrainVarSubVar(a_vid, b_vid) => { - match var_values[a_vid.index as usize] { - ErrorValue => false, - Value(a_region) => { - let b_node = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_node) - } - } - } - ConstrainRegSubReg(..) | - ConstrainVarSubReg(..) => { - // These constraints are checked after expansion - // is done, in `collect_errors`. - false - } - } - }) - } - - fn expand_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a_region: Region<'tcx>, - b_vid: RegionVid, - b_data: &mut VarValue<'tcx>) - -> bool { - debug!("expand_node({:?}, {:?} == {:?})", - a_region, - b_vid, - b_data); - - // Check if this relationship is implied by a given. - match *a_region { - ty::ReEarlyBound(_) | - ty::ReFree(_) => { - if self.givens.borrow().contains(&(a_region, b_vid)) { - debug!("given"); - return false; - } - } - _ => {} - } - - match *b_data { - Value(cur_region) => { - let lub = self.lub_concrete_regions(region_rels, a_region, cur_region); - if lub == cur_region { - return false; - } - - debug!("Expanding value of {:?} from {:?} to {:?}", - b_vid, - cur_region, - lub); - - *b_data = Value(lub); - return true; - } - - ErrorValue => { - return false; - } - } - } - - /// After expansion is complete, go and check upper bounds (i.e., - /// cases where the region cannot grow larger than a fixed point) - /// and check that they are satisfied. - fn collect_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &mut Vec>, - errors: &mut Vec>) { - let constraints = self.constraints.borrow(); - for (constraint, origin) in constraints.iter() { - debug!("collect_errors: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(..) | - ConstrainVarSubVar(..) => { - // Expansion will ensure that these constraints hold. Ignore. - } - - ConstrainRegSubReg(sub, sup) => { - if region_rels.is_subregion_of(sub, sup) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - origin, - sub, - sup); - - errors.push(ConcreteFailure((*origin).clone(), sub, sup)); - } - - ConstrainVarSubReg(a_vid, b_region) => { - let a_data = &mut var_data[a_vid.index as usize]; - debug!("contraction: {:?} == {:?}, {:?}", - a_vid, - a_data, - b_region); - - let a_region = match *a_data { - ErrorValue => continue, - Value(a_region) => a_region, - }; - - // Do not report these errors immediately: - // instead, set the variable value to error and - // collect them later. - if !region_rels.is_subregion_of(a_region, b_region) { - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?}={:?} <= {:?}", - origin, - a_vid, - a_region, - b_region); - *a_data = ErrorValue; - } - } - } - } - - for verify in self.verifys.borrow().iter() { - debug!("collect_errors: verify={:?}", verify); - let sub = normalize(self.tcx, var_data, verify.region); - - // This was an inference variable which didn't get - // constrained, therefore it can be assume to hold. - if let ty::ReEmpty = *sub { - continue; - } - - if verify.bound.is_met(region_rels, var_data, sub) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - verify.origin, - verify.region, - verify.bound); - - errors.push(GenericBoundFailure(verify.origin.clone(), - verify.kind.clone(), - sub)); - } - } - - /// Go over the variables that were declared to be error variables - /// and create a `RegionResolutionError` for each of them. - fn collect_var_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &[VarValue<'tcx>], - graph: &RegionGraph<'tcx>, - errors: &mut Vec>) { - debug!("collect_var_errors"); - - // This is the best way that I have found to suppress - // duplicate and related errors. Basically we keep a set of - // flags for every node. Whenever an error occurs, we will - // walk some portion of the graph looking to find pairs of - // conflicting regions to report to the user. As we walk, we - // trip the flags from false to true, and if we find that - // we've already reported an error involving any particular - // node we just stop and don't report the current error. The - // idea is to report errors that derive from independent - // regions of the graph, but not those that derive from - // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; - - for idx in 0..self.num_vars() as usize { - match var_data[idx] { - Value(_) => { - /* Inference successful */ - } - ErrorValue => { - /* Inference impossible, this value contains - inconsistent constraints. - - I think that in this case we should report an - error now---unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ - - let node_vid = RegionVid { index: idx as u32 }; - self.collect_error_for_expanding_node(region_rels, - graph, - &mut dup_vec, - node_vid, - errors); - } - } - } - } - - fn construct_graph(&self) -> RegionGraph<'tcx> { - let num_vars = self.num_vars(); - - let constraints = self.constraints.borrow(); - - let mut graph = graph::Graph::new(); - - for _ in 0..num_vars { - graph.add_node(()); - } - - // Issue #30438: two distinct dummy nodes, one for incoming - // edges (dummy_source) and another for outgoing edges - // (dummy_sink). In `dummy -> a -> b -> dummy`, using one - // dummy node leads one to think (erroneously) there exists a - // path from `b` to `a`. Two dummy nodes sidesteps the issue. - let dummy_source = graph.add_node(()); - let dummy_sink = graph.add_node(()); - - for (constraint, _) in constraints.iter() { - match *constraint { - ConstrainVarSubVar(a_id, b_id) => { - graph.add_edge(NodeIndex(a_id.index as usize), - NodeIndex(b_id.index as usize), - *constraint); - } - ConstrainRegSubVar(_, b_id) => { - graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); - } - ConstrainVarSubReg(a_id, _) => { - graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); - } - ConstrainRegSubReg(..) => { - // this would be an edge from `dummy_source` to - // `dummy_sink`; just ignore it. - } - } - } - - return graph; - } - - fn collect_error_for_expanding_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - graph: &RegionGraph<'tcx>, - dup_vec: &mut [u32], - node_idx: RegionVid, - errors: &mut Vec>) { - // Errors in expanding nodes result from a lower-bound that is - // not contained by an upper-bound. - let (mut lower_bounds, lower_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::INCOMING, - dup_vec); - let (mut upper_bounds, upper_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::OUTGOING, - dup_vec); - - if lower_dup || upper_dup { - return; - } - - // We place free regions first because we are special casing - // SubSupConflict(ReFree, ReFree) when reporting error, and so - // the user will more likely get a specific suggestion. - fn region_order_key(x: &RegionAndOrigin) -> u8 { - match *x.region { - ReEarlyBound(_) => 0, - ReFree(_) => 1, - _ => 2 - } - } - lower_bounds.sort_by_key(region_order_key); - upper_bounds.sort_by_key(region_order_key); - - for lower_bound in &lower_bounds { - for upper_bound in &upper_bounds { - if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { - let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone(); - debug!("region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ - sup: {:?}", - origin, - node_idx, - lower_bound.region, - upper_bound.region); - errors.push(SubSupConflict(origin, - lower_bound.origin.clone(), - lower_bound.region, - upper_bound.origin.clone(), - upper_bound.region)); - return; - } - } - } - - span_bug!((*self.var_origins.borrow())[node_idx.index as usize].span(), - "collect_error_for_expanding_node() could not find \ - error for var {:?}, lower_bounds={:?}, \ - upper_bounds={:?}", - node_idx, - lower_bounds, - upper_bounds); - } - - fn collect_concrete_regions(&self, - graph: &RegionGraph<'tcx>, - orig_node_idx: RegionVid, - dir: Direction, - dup_vec: &mut [u32]) - -> (Vec>, bool) { - struct WalkState<'tcx> { - set: FxHashSet, - stack: Vec, - result: Vec>, - dup_found: bool, - } - let mut state = WalkState { - set: FxHashSet(), - stack: vec![orig_node_idx], - result: Vec::new(), - dup_found: false, - }; - state.set.insert(orig_node_idx); - - // to start off the process, walk the source node in the - // direction specified - process_edges(self, &mut state, graph, orig_node_idx, dir); - - while !state.stack.is_empty() { - let node_idx = state.stack.pop().unwrap(); - - // check whether we've visited this node on some previous walk - if dup_vec[node_idx.index as usize] == u32::MAX { - dup_vec[node_idx.index as usize] = orig_node_idx.index; - } else if dup_vec[node_idx.index as usize] != orig_node_idx.index { - state.dup_found = true; - } - - debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, - node_idx); - - process_edges(self, &mut state, graph, node_idx, dir); - } - - let WalkState {result, dup_found, ..} = state; - return (result, dup_found); - - fn process_edges<'a, 'gcx, 'tcx>(this: &RegionVarBindings<'a, 'gcx, 'tcx>, - state: &mut WalkState<'tcx>, - graph: &RegionGraph<'tcx>, - source_vid: RegionVid, - dir: Direction) { - debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); - - let source_node_index = NodeIndex(source_vid.index as usize); - for (_, edge) in graph.adjacent_edges(source_node_index, dir) { - match edge.data { - ConstrainVarSubVar(from_vid, to_vid) => { - let opp_vid = if from_vid == source_vid { - to_vid - } else { - from_vid - }; - if state.set.insert(opp_vid) { - state.stack.push(opp_vid); - } - } - - ConstrainRegSubVar(region, _) | - ConstrainVarSubReg(_, region) => { - state.result.push(RegionAndOrigin { - region, - origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), - }); - } - - ConstrainRegSubReg(..) => { - panic!("cannot reach reg-sub-reg edge in region inference \ - post-processing") - } - } - } - } - } - - fn iterate_until_fixed_point(&self, tag: &str, mut body: F) - where F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool - { - let mut iteration = 0; - let mut changed = true; - while changed { - changed = false; - iteration += 1; - debug!("---- {} Iteration {}{}", "#", tag, iteration); - for (constraint, origin) in self.constraints.borrow().iter() { - let edge_changed = body(constraint, origin); - if edge_changed { - debug!("Updated due to constraint {:?}", constraint); - changed = true; - } - } - } - debug!("---- {} Complete after {} iteration(s)", tag, iteration); - } - -} - -fn normalize<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - r: ty::Region<'tcx>) - -> ty::Region<'tcx> { - match *r { - ty::ReVar(rid) => lookup(tcx, values, rid), - _ => r, - } -} - -fn lookup<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - rid: ty::RegionVid) - -> ty::Region<'tcx> { - match values[rid.index as usize] { - Value(r) => r, - ErrorValue => tcx.types.re_static, // Previously reported error. - } -} - -impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) - } -} - -impl fmt::Debug for RegionSnapshot { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionSnapshot(length={},skolemization={})", - self.length, self.skolemization_count) - } -} - -impl<'tcx> fmt::Debug for GenericKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenericKind::Param(ref p) => write!(f, "{:?}", p), - GenericKind::Projection(ref p) => write!(f, "{:?}", p), - } - } -} - -impl<'tcx> fmt::Display for GenericKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenericKind::Param(ref p) => write!(f, "{}", p), - GenericKind::Projection(ref p) => write!(f, "{}", p), - } - } -} - -impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { - pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { - match *self { - GenericKind::Param(ref p) => p.to_ty(tcx), - GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), - } - } -} - -impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { - fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) { - match self { - &VerifyBound::AnyRegion(ref rs) | - &VerifyBound::AllRegions(ref rs) => for &r in rs { - f(r); - }, - - &VerifyBound::AnyBound(ref bs) | - &VerifyBound::AllBounds(ref bs) => for b in bs { - b.for_each_region(f); - }, - } - } - - pub fn must_hold(&self) -> bool { - match self { - &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), - &VerifyBound::AllRegions(ref bs) => bs.is_empty(), - &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), - } - } - - pub fn cannot_hold(&self) -> bool { - match self { - &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), - &VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty), - &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), - } - } - - pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { - if self.must_hold() || vb.cannot_hold() { - self - } else if self.cannot_hold() || vb.must_hold() { - vb - } else { - VerifyBound::AnyBound(vec![self, vb]) - } - } - - pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { - if self.must_hold() && vb.must_hold() { - self - } else if self.cannot_hold() && vb.cannot_hold() { - self - } else { - VerifyBound::AllBounds(vec![self, vb]) - } - } - - fn is_met(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &Vec>, - min: ty::Region<'tcx>) - -> bool { - let tcx = region_rels.tcx; - match self { - &VerifyBound::AnyRegion(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .any(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AllRegions(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .all(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AnyBound(ref bs) => - bs.iter() - .any(|b| b.is_met(region_rels, var_values, min)), - - &VerifyBound::AllBounds(ref bs) => - bs.iter() - .all(|b| b.is_met(region_rels, var_values, min)), - } - } -} diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 10899e42afb81..5e70c0ce368fc 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -74,8 +74,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(rid), - _ => r, + ty::ReVar(rid) => + self.infcx.borrow_region_constraints() + .opportunistic_resolve_var(self.tcx(), rid), + _ => + r, } } } @@ -185,7 +188,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.resolve_var(rid), + ty::ReVar(rid) => self.infcx.lexical_region_resolutions + .borrow() + .as_ref() + .expect("region resolution not performed") + .resolve_var(rid), _ => r, } } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 4056999681352..f891f692c7d82 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -137,7 +137,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_subregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_subregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index cc91a637b8931..6aa094d2cd6d7 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -56,7 +56,10 @@ pub enum TypeVariableOrigin { NormalizeProjectionType(Span), TypeInference(Span), TypeParameterDefinition(Span, ast::Name), - TransformedUpvar(Span), + + /// one of the upvars or closure kind parameters in a `ClosureSubsts` + /// (before it has been determined) + ClosureSynthetic(Span), SubstitutionPlaceholder(Span), AutoDeref(Span), AdjustmentType(Span), diff --git a/src/librustc/infer/unify_key.rs b/src/librustc/infer/unify_key.rs index d7e3a53ff25c9..99b11794cc5b5 100644 --- a/src/librustc/infer/unify_key.rs +++ b/src/librustc/infer/unify_key.rs @@ -33,7 +33,7 @@ pub struct RegionVidKey { impl Combine for RegionVidKey { fn combine(&self, other: &RegionVidKey) -> RegionVidKey { - let min_vid = if self.min_vid.index < other.min_vid.index { + let min_vid = if self.min_vid.index() < other.min_vid.index() { self.min_vid } else { other.min_vid @@ -45,8 +45,8 @@ impl Combine for RegionVidKey { impl UnifyKey for ty::RegionVid { type Value = RegionVidKey; - fn index(&self) -> u32 { self.index } - fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid { index: i } } + fn index(&self) -> u32 { self.0 } + fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid(i) } fn tag(_: Option) -> &'static str { "RegionVid" } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 754ceee6be14b..0c3ffdc511def 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -43,24 +43,31 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(conservative_impl_trait)] +#![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(drain_filter)] +#![feature(from_ref)] +#![feature(i128)] #![feature(i128_type)] +#![feature(inclusive_range)] +#![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] +#![feature(macro_vis_matcher)] +#![feature(match_default_bindings)] #![feature(never_type)] #![feature(nonzero)] #![feature(quote)] +#![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] #![feature(specialization)] #![feature(unboxed_closures)] +#![feature(underscore_lifetimes)] #![feature(trace_macros)] #![feature(catch_expr)] #![feature(test)] -#![cfg_attr(stage0, feature(const_fn))] -#![cfg_attr(not(stage0), feature(const_atomic_bool_new))] - -#![recursion_limit="256"] +#![recursion_limit="512"] extern crate arena; #[macro_use] extern crate bitflags; @@ -72,7 +79,7 @@ extern crate graphviz; extern crate libc; extern crate owning_ref; extern crate rustc_back; -extern crate rustc_data_structures; +#[macro_use] extern crate rustc_data_structures; extern crate serialize; extern crate rustc_const_math; extern crate rustc_errors as errors; @@ -83,6 +90,7 @@ extern crate jobserver; extern crate serialize as rustc_serialize; // used by deriving +extern crate rustc_apfloat; extern crate log_settings; extern crate byteorder; #[macro_use] @@ -113,6 +121,7 @@ pub mod lint; pub mod middle { pub mod allocator; + pub mod borrowck; pub mod expr_use_visitor; pub mod const_val; pub mod cstore; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 5fe75d8ca71e3..1008da1e937a5 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -106,12 +106,6 @@ declare_lint! { "unknown crate type found in #[crate_type] directive" } -declare_lint! { - pub FAT_PTR_TRANSMUTES, - Allow, - "detects transmutes of fat pointers" -} - declare_lint! { pub TRIVIAL_CASTS, Allow, @@ -162,15 +156,15 @@ declare_lint! { } declare_lint! { - pub PATTERNS_IN_FNS_WITHOUT_BODY, + pub SAFE_PACKED_BORROWS, Warn, - "patterns in functions without body were erroneously allowed" + "safe borrows of fields of packed structs were was erroneously allowed" } declare_lint! { - pub EXTRA_REQUIREMENT_IN_IMPL, - Deny, - "detects extra requirements in impls that were erroneously allowed" + pub PATTERNS_IN_FNS_WITHOUT_BODY, + Warn, + "patterns in functions without body were erroneously allowed" } declare_lint! { @@ -222,6 +216,18 @@ declare_lint! { "unnecessary use of an `unsafe` block" } +declare_lint! { + pub UNUSED_MUT, + Warn, + "detect mut variables which don't need to be mutable" +} + +declare_lint! { + pub COERCE_NEVER, + Deny, + "detect coercion to !" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -244,7 +250,6 @@ impl LintPass for HardwiredLints { UNUSED_FEATURES, STABLE_FEATURES, UNKNOWN_CRATE_TYPES, - FAT_PTR_TRANSMUTES, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, PRIVATE_IN_PUBLIC, @@ -254,8 +259,8 @@ impl LintPass for HardwiredLints { RENAMED_AND_REMOVED_LINTS, RESOLVE_TRAIT_ON_DEFAULTED_UNIT, SAFE_EXTERN_STATICS, + SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, - EXTRA_REQUIREMENT_IN_IMPL, LEGACY_DIRECTORY_OWNERSHIP, LEGACY_IMPORTS, LEGACY_CONSTRUCTOR_VISIBILITY, @@ -263,7 +268,9 @@ impl LintPass for HardwiredLints { PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, LATE_BOUND_LIFETIME_ARGUMENTS, DEPRECATED, - UNUSED_UNSAFE + UNUSED_UNSAFE, + UNUSED_MUT, + COERCE_NEVER ) } } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 4d1374b69b85c..75cd230e1e5e2 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -26,7 +26,7 @@ use self::TargetLint::*; -use rustc_back::slice; +use std::slice; use lint::{EarlyLintPassObject, LateLintPassObject}; use lint::{Level, Lint, LintId, LintPass, LintBuffer}; use lint::levels::{LintLevelSets, LintLevelsBuilder}; @@ -34,7 +34,8 @@ use middle::privacy::AccessLevels; use rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; use session::{config, early_error, Session}; use traits::Reveal; -use ty::{self, TyCtxt}; +use ty::{self, TyCtxt, Ty}; +use ty::layout::{LayoutError, LayoutOf, TyLayout}; use util::nodemap::FxHashMap; use std::default::Default as StdDefault; @@ -307,7 +308,7 @@ impl LintStore { Some(ids) => CheckLintNameResult::Ok(&ids.0), } } - Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::ref_slice(id)), + Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), } } } @@ -352,6 +353,9 @@ pub struct LateContext<'a, 'tcx: 'a> { lint_sess: LintSession<'tcx, LateLintPassObject>, last_ast_node_with_lint_attrs: ast::NodeId, + + /// Generic type parameters in scope for the item we are in. + pub generics: Option<&'tcx hir::Generics>, } /// Context for lint checking of the AST, after expansion, before lowering to @@ -623,6 +627,14 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { } } +impl<'a, 'tcx> LayoutOf> for &'a LateContext<'a, 'tcx> { + type TyLayout = Result, LayoutError<'tcx>>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + (self.tcx, self.param_env.reveal_all()).layout_of(ty) + } +} + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { /// Because lints are scoped lexically, we want to walk nested /// items in the context of the outer item, so enable @@ -646,13 +658,16 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { } fn visit_item(&mut self, it: &'tcx hir::Item) { + let generics = self.generics.take(); + self.generics = it.node.generics(); self.with_lint_attrs(it.id, &it.attrs, |cx| { cx.with_param_env(it.id, |cx| { run_lints!(cx, check_item, late_passes, it); hir_visit::walk_item(cx, it); run_lints!(cx, check_item_post, late_passes, it); }); - }) + }); + self.generics = generics; } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem) { @@ -774,6 +789,8 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { + let generics = self.generics.take(); + self.generics = Some(&trait_item.generics); self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| { cx.with_param_env(trait_item.id, |cx| { run_lints!(cx, check_trait_item, late_passes, trait_item); @@ -781,9 +798,12 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { run_lints!(cx, check_trait_item_post, late_passes, trait_item); }); }); + self.generics = generics; } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + let generics = self.generics.take(); + self.generics = Some(&impl_item.generics); self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| { cx.with_param_env(impl_item.id, |cx| { run_lints!(cx, check_impl_item, late_passes, impl_item); @@ -791,6 +811,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { run_lints!(cx, check_impl_item_post, late_passes, impl_item); }); }); + self.generics = generics; } fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { @@ -960,12 +981,6 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { ast_visit::walk_path(self, p); } - fn visit_path_list_item(&mut self, prefix: &'a ast::Path, item: &'a ast::PathListItem) { - run_lints!(self, check_path_list_item, early_passes, item); - self.check_id(item.node.id); - ast_visit::walk_path_list_item(self, prefix, item); - } - fn visit_attribute(&mut self, attr: &'a ast::Attribute) { run_lints!(self, check_attribute, early_passes, attr); } @@ -991,6 +1006,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { access_levels, lint_sess: LintSession::new(&tcx.sess.lint_store), last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID, + generics: None, }; // Visit the whole crate. diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 42b5e2dd83de5..906cae53710ff 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -33,12 +33,11 @@ pub use self::LintSource::*; use std::rc::Rc; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, DiagnosticId}; use hir::def_id::{CrateNum, LOCAL_CRATE}; use hir::intravisit::{self, FnKind}; use hir; use session::Session; -use std::ascii::AsciiExt; use std::hash; use syntax::ast; use syntax::codemap::MultiSpan; @@ -84,29 +83,16 @@ impl Lint { } } -/// Build a `Lint` initializer. -#[macro_export] -macro_rules! lint_initializer { - ($name:ident, $level:ident, $desc:expr) => ( - ::rustc::lint::Lint { - name: stringify!($name), - default_level: ::rustc::lint::$level, - desc: $desc, - } - ) -} - /// Declare a static item of type `&'static Lint`. #[macro_export] macro_rules! declare_lint { - (pub $name:ident, $level:ident, $desc:expr) => ( - pub static $name: &'static ::rustc::lint::Lint - = &lint_initializer!($name, $level, $desc); - ); - ($name:ident, $level:ident, $desc:expr) => ( - static $name: &'static ::rustc::lint::Lint - = &lint_initializer!($name, $level, $desc); - ); + ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( + $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + name: stringify!($NAME), + default_level: $crate::lint::$Level, + desc: $desc + }; + ) } /// Declare a static `LintArray` and return it as an expression. @@ -260,7 +246,6 @@ pub trait EarlyLintPass: LintPass { fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { } fn check_lifetime_def(&mut self, _: &EarlyContext, _: &ast::LifetimeDef) { } fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { } - fn check_path_list_item(&mut self, _: &EarlyContext, _: &ast::PathListItem) { } fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { } /// Called when entering a syntax node that can have lint attributes such @@ -476,6 +461,8 @@ pub fn struct_lint_level<'a>(sess: &'a Session, } } + err.code(DiagnosticId::Lint(name)); + // Check for future incompatibility lints and issue a stronger warning. let lints = sess.lint_store.borrow(); if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) { diff --git a/src/librustc/middle/borrowck.rs b/src/librustc/middle/borrowck.rs new file mode 100644 index 0000000000000..380f79361e27f --- /dev/null +++ b/src/librustc/middle/borrowck.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ich::StableHashingContext; +use hir::HirId; +use util::nodemap::FxHashSet; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, + StableHasherResult}; + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct BorrowCheckResult { + pub used_mut_nodes: FxHashSet, +} + +impl<'gcx> HashStable> for BorrowCheckResult { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let BorrowCheckResult { + ref used_mut_nodes, + } = *self; + used_mut_nodes.hash_stable(hcx, hasher); + } +} diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 7b23998046730..440af39a0d469 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -106,7 +106,8 @@ pub enum ErrKind<'tcx> { ErroneousReferencedConstant(Box>), - TypeckError + TypeckError, + CheckMatchError, } impl<'tcx> From for ErrKind<'tcx> { @@ -168,6 +169,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), TypeckError => simple!("type-checking failed"), + CheckMatchError => simple!("match-checking failed"), } } @@ -212,8 +214,9 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { primary_span: Span, primary_kind: &str) { - if let ErrKind::TypeckError = self.kind { - return; + match self.kind { + ErrKind::TypeckError | ErrKind::CheckMatchError => return, + _ => {} } self.struct_error(tcx, primary_span, primary_kind).emit(); } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index a97bfa0536987..4be23fb711d77 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -24,13 +24,13 @@ use hir; use hir::def; -use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; +use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use hir::map as hir_map; use hir::map::definitions::{Definitions, DefKey, DefPathTable}; use hir::svh::Svh; use ich; use ty::{self, TyCtxt}; -use session::Session; +use session::{Session, CrateDisambiguator}; use session::search_paths::PathKind; use util::nodemap::NodeSet; @@ -175,32 +175,6 @@ impl EncodedMetadata { } } -/// The hash for some metadata that (when saving) will be exported -/// from this crate, or which (when importing) was exported by an -/// upstream crate. -#[derive(Debug, RustcEncodable, RustcDecodable, Copy, Clone)] -pub struct EncodedMetadataHash { - pub def_index: DefIndex, - pub hash: ich::Fingerprint, -} - -/// The hash for some metadata that (when saving) will be exported -/// from this crate, or which (when importing) was exported by an -/// upstream crate. -#[derive(Debug, RustcEncodable, RustcDecodable, Clone)] -pub struct EncodedMetadataHashes { - // Stable content hashes for things in crate metadata, indexed by DefIndex. - pub hashes: Vec, -} - -impl EncodedMetadataHashes { - pub fn new() -> EncodedMetadataHashes { - EncodedMetadataHashes { - hashes: Vec::new(), - } - } -} - /// The backend's way to give the crate store access to the metadata in a library. /// Note that it returns the raw metadata bytes stored in the library file, whether /// it is compressed, uncompressed, some weird mix, etc. @@ -267,13 +241,13 @@ pub trait CrateStore { fn export_macros_untracked(&self, cnum: CrateNum); fn dep_kind_untracked(&self, cnum: CrateNum) -> DepKind; fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol; - fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol; + fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator; fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh; fn struct_field_names_untracked(&self, def: DefId) -> Vec; fn item_children_untracked(&self, did: DefId, sess: &Session) -> Vec; fn load_macro_untracked(&self, did: DefId, sess: &Session) -> LoadedMacro; fn extern_mod_stmt_cnum_untracked(&self, emod_id: ast::NodeId) -> Option; - fn item_generics_cloned_untracked(&self, def: DefId) -> ty::Generics; + fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics; fn associated_item_cloned_untracked(&self, def: DefId) -> ty::AssociatedItem; fn postorder_cnums_untracked(&self) -> Vec; @@ -286,7 +260,7 @@ pub trait CrateStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> (EncodedMetadata, EncodedMetadataHashes); + -> EncodedMetadata; fn metadata_encoding_version(&self) -> &[u8]; } @@ -327,7 +301,7 @@ impl CrateStore for DummyCrateStore { { bug!("crate_data_as_rc_any") } // item info fn visibility_untracked(&self, def: DefId) -> ty::Visibility { bug!("visibility") } - fn item_generics_cloned_untracked(&self, def: DefId) -> ty::Generics + fn item_generics_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::Generics { bug!("item_generics_cloned") } // trait/impl-item info @@ -338,7 +312,7 @@ impl CrateStore for DummyCrateStore { fn dep_kind_untracked(&self, cnum: CrateNum) -> DepKind { bug!("is_explicitly_linked") } fn export_macros_untracked(&self, cnum: CrateNum) { bug!("export_macros") } fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol { bug!("crate_name") } - fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol { + fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator { bug!("crate_disambiguator") } fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh { bug!("crate_hash") } @@ -370,7 +344,7 @@ impl CrateStore for DummyCrateStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> (EncodedMetadata, EncodedMetadataHashes) { + -> EncodedMetadata { bug!("encode_metadata") } fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index e88678dea1d74..5c86554f90790 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -171,7 +171,7 @@ fn build_local_id_to_index(body: Option<&hir::Body>, -> FxHashMap> { let mut index = FxHashMap(); - // FIXME (#6298): Would it be better to fold formals from decl + // FIXME(#15020) Would it be better to fold formals from decl // into cfg itself? i.e. introduce a fn-based flow-graph in // addition to the current block-based flow-graph, rather than // have to put traversals like this here? diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index a9d9f6f28ec0d..21eb772b1b376 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -51,7 +51,7 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> { tables: &'a ty::TypeckTables<'tcx>, live_symbols: Box>, struct_has_extern_repr: bool, - ignore_non_const_paths: bool, + in_pat: bool, inherited_pub_visibility: bool, ignore_variant_stack: Vec, } @@ -75,10 +75,10 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn handle_definition(&mut self, def: Def) { match def { - Def::Const(_) | Def::AssociatedConst(..) => { + Def::Const(_) | Def::AssociatedConst(..) | Def::TyAlias(_) => { self.check_def_id(def.def_id()); } - _ if self.ignore_non_const_paths => (), + _ if self.in_pat => (), Def::PrimTy(..) | Def::SelfTy(..) | Def::Local(..) | Def::Upvar(..) => {} Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { @@ -289,9 +289,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { _ => () } - self.ignore_non_const_paths = true; + self.in_pat = true; intravisit::walk_pat(self, pat); - self.ignore_non_const_paths = false; + self.in_pat = false; } fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) { @@ -429,7 +429,7 @@ fn find_live<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &ty::TypeckTables::empty(None), live_symbols: box FxHashSet(), struct_has_extern_repr: false, - ignore_non_const_paths: false, + in_pat: false, inherited_pub_visibility: false, ignore_variant_stack: vec![], }; @@ -531,13 +531,15 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { id: ast::NodeId, span: syntax_pos::Span, name: ast::Name, - node_type: &str) { + node_type: &str, + participle: &str) { if !name.as_str().starts_with("_") { self.tcx .lint_node(lint::builtin::DEAD_CODE, id, span, - &format!("{} is never used: `{}`", node_type, name)); + &format!("{} is never {}: `{}`", + node_type, participle, name)); } } } @@ -562,7 +564,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { hir::ItemStruct(..) | hir::ItemUnion(..) | hir::ItemTrait(..) | - hir::ItemDefaultImpl(..) | + hir::ItemAutoImpl(..) | hir::ItemImpl(..) => self.tcx.sess.codemap().def_span(item.span), _ => item.span, }; @@ -570,7 +572,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { item.id, span, item.name, - item.node.descriptive_variant() + item.node.descriptive_variant(), + "used", ); } else { // Only continue if we didn't warn @@ -583,7 +586,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { g: &'tcx hir::Generics, id: ast::NodeId) { if self.should_warn_about_variant(&variant.node) { - self.warn_dead_code(variant.node.data.id(), variant.span, variant.node.name, "variant"); + self.warn_dead_code(variant.node.data.id(), variant.span, variant.node.name, + "variant", "constructed"); } else { intravisit::walk_variant(self, variant, g, id); } @@ -591,15 +595,15 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) { if self.should_warn_about_foreign_item(fi) { - self.warn_dead_code(fi.id, fi.span, fi.name, fi.node.descriptive_variant()); + self.warn_dead_code(fi.id, fi.span, fi.name, + fi.node.descriptive_variant(), "used"); } intravisit::walk_foreign_item(self, fi); } fn visit_struct_field(&mut self, field: &'tcx hir::StructField) { if self.should_warn_about_field(&field) { - self.warn_dead_code(field.id, field.span, - field.name, "field"); + self.warn_dead_code(field.id, field.span, field.name, "field", "used"); } intravisit::walk_struct_field(self, field); } @@ -611,14 +615,15 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { self.warn_dead_code(impl_item.id, impl_item.span, impl_item.name, - "associated const"); + "associated const", + "used"); } self.visit_nested_body(body_id) } hir::ImplItemKind::Method(_, body_id) => { if !self.symbol_is_live(impl_item.id, None) { let span = self.tcx.sess.codemap().def_span(impl_item.span); - self.warn_dead_code(impl_item.id, span, impl_item.name, "method"); + self.warn_dead_code(impl_item.id, span, impl_item.name, "method", "used"); } self.visit_nested_body(body_id) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index b036b145a96e4..9018b9fe590b2 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -20,17 +20,18 @@ use self::TrackMatchMode::*; use self::OverloadedCallType::*; use hir::def::Def; -use hir::def_id::{DefId}; +use hir::def_id::DefId; use infer::InferCtxt; use middle::mem_categorization as mc; use middle::region; use ty::{self, TyCtxt, adjustment}; use hir::{self, PatKind}; - +use std::rc::Rc; use syntax::ast; use syntax::ptr::P; use syntax_pos::Span; +use util::nodemap::ItemLocalSet; /////////////////////////////////////////////////////////////////////////// // The Delegate trait @@ -262,15 +263,30 @@ macro_rules! return_if_err { } impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx, 'tcx> { + /// Creates the ExprUseVisitor, configuring it with the various options provided: + /// + /// - `delegate` -- who receives the callbacks + /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`) + /// - `region_scope_tree` --- region scope tree for the code being analyzed + /// - `tables` --- typeck results for the code being analyzed + /// - `rvalue_promotable_map` --- if you care about rvalue promotion, then provide + /// the map here (it can be computed with `tcx.rvalue_promotable_map(def_id)`). + /// `None` means that rvalues will be given more conservative lifetimes. + /// + /// See also `with_infer`, which is used *during* typeck. pub fn new(delegate: &'a mut (Delegate<'tcx>+'a), tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, region_scope_tree: &'a region::ScopeTree, - tables: &'a ty::TypeckTables<'tcx>) + tables: &'a ty::TypeckTables<'tcx>, + rvalue_promotable_map: Option>) -> Self { ExprUseVisitor { - mc: mc::MemCategorizationContext::new(tcx, region_scope_tree, tables), + mc: mc::MemCategorizationContext::new(tcx, + region_scope_tree, + tables, + rvalue_promotable_map), delegate, param_env, } @@ -899,7 +915,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { let closure_def_id = self.tcx().hir.local_def_id(closure_expr.id); let upvar_id = ty::UpvarId { var_id: var_hir_id, - closure_expr_id: closure_def_id.index + closure_expr_id: closure_def_id.to_local(), }; let upvar_capture = self.mc.tables.upvar_capture(upvar_id); let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 49a241b86e015..ca6a5dd7f5b0b 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -15,10 +15,10 @@ //! `TransitiveRelation` type and use that to decide when one free //! region outlives another and so forth. +use infer::outlives::free_region_map::FreeRegionMap; use hir::def_id::DefId; use middle::region; -use ty::{self, Lift, TyCtxt, Region}; -use rustc_data_structures::transitive_relation::TransitiveRelation; +use ty::{self, TyCtxt, Region}; /// Combines a `region::ScopeTree` (which governs relationships between /// scopes) and a `FreeRegionMap` (which governs relationships between @@ -63,28 +63,28 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { -> bool { let result = sub_region == super_region || { match (sub_region, super_region) { - (&ty::ReEmpty, _) | - (_, &ty::ReStatic) => + (ty::ReEmpty, _) | + (_, ty::ReStatic) => true, - (&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) => - self.region_scope_tree.is_subscope_of(sub_scope, super_scope), + (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => + self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope), - (&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => { + (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => { let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br); - self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) + self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) } - (&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => { + (ty::ReScope(sub_scope), ty::ReFree(fr)) => { let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr); - self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) + self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) } - (&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) | - (&ty::ReFree(_), &ty::ReEarlyBound(_)) | - (&ty::ReEarlyBound(_), &ty::ReFree(_)) | - (&ty::ReFree(_), &ty::ReFree(_)) => - self.free_regions.relation.contains(&sub_region, &super_region), + (ty::ReEarlyBound(_), ty::ReEarlyBound(_)) | + (ty::ReFree(_), ty::ReEarlyBound(_)) | + (ty::ReEarlyBound(_), ty::ReFree(_)) | + (ty::ReFree(_), ty::ReFree(_)) => + self.free_regions.sub_free_regions(sub_region, super_region), _ => false, @@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { ty::ReStatic => true, ty::ReEarlyBound(_) | ty::ReFree(_) => { let re_static = self.tcx.mk_region(ty::ReStatic); - self.free_regions.relation.contains(&re_static, &super_region) + self.free_regions.sub_free_regions(&re_static, &super_region) } _ => false } @@ -117,88 +117,3 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub struct FreeRegionMap<'tcx> { - // Stores the relation `a < b`, where `a` and `b` are regions. - // - // Invariant: only free regions like `'x` or `'static` are stored - // in this relation, not scopes. - relation: TransitiveRelation> -} - -impl<'tcx> FreeRegionMap<'tcx> { - pub fn new() -> Self { - FreeRegionMap { relation: TransitiveRelation::new() } - } - - pub fn is_empty(&self) -> bool { - self.relation.is_empty() - } - - pub fn relate_free_regions_from_predicates(&mut self, - predicates: &[ty::Predicate<'tcx>]) { - debug!("relate_free_regions_from_predicates(predicates={:?})", predicates); - for predicate in predicates { - match *predicate { - ty::Predicate::Projection(..) | - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::WellFormed(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::TypeOutlives(..) | - ty::Predicate::ConstEvaluatable(..) => { - // No region bounds here - } - ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - self.relate_regions(r_b, r_a); - } - } - } - } - - // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - // (with the exception that `'static: 'x` is not notable) - pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { - if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) { - self.relation.add(sub, sup) - } - } - - pub fn lub_free_regions<'a, 'gcx>(&self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - r_a: Region<'tcx>, - r_b: Region<'tcx>) - -> Region<'tcx> { - assert!(is_free(r_a)); - assert!(is_free(r_b)); - let result = if r_a == r_b { r_a } else { - match self.relation.postdom_upper_bound(&r_a, &r_b) { - None => tcx.mk_region(ty::ReStatic), - Some(r) => *r, - } - }; - debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); - result - } -} - -fn is_free(r: Region) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false - } -} - -impl_stable_hash_for!(struct FreeRegionMap<'tcx> { - relation -}); - -impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { - type Lifted = FreeRegionMap<'tcx>; - fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option> { - self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) - .map(|relation| FreeRegionMap { relation }) - } -} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 679c4f17a6c03..f8933d06360e0 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -40,7 +40,7 @@ macro_rules! language_item_table { enum_from_u32! { - #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum LangItem { $($variant,)* } @@ -211,6 +211,7 @@ language_item_table! { CharImplItem, "char", char_impl; StrImplItem, "str", str_impl; SliceImplItem, "slice", slice_impl; + SliceU8ImplItem, "slice_u8", slice_u8_impl; ConstPtrImplItem, "const_ptr", const_ptr_impl; MutPtrImplItem, "mut_ptr", mut_ptr_impl; I8ImplItem, "i8", i8_impl; @@ -228,7 +229,6 @@ language_item_table! { F32ImplItem, "f32", f32_impl; F64ImplItem, "f64", f64_impl; - SendTraitLangItem, "send", send_trait; SizedTraitLangItem, "sized", sized_trait; UnsizeTraitLangItem, "unsize", unsize_trait; CopyTraitLangItem, "copy", copy_trait; @@ -310,6 +310,34 @@ language_item_table! { NonZeroItem, "non_zero", non_zero; DebugTraitLangItem, "debug_trait", debug_trait; + + // A lang item for each of the 128-bit operators we can optionally lower. + I128AddFnLangItem, "i128_add", i128_add_fn; + U128AddFnLangItem, "u128_add", u128_add_fn; + I128SubFnLangItem, "i128_sub", i128_sub_fn; + U128SubFnLangItem, "u128_sub", u128_sub_fn; + I128MulFnLangItem, "i128_mul", i128_mul_fn; + U128MulFnLangItem, "u128_mul", u128_mul_fn; + I128DivFnLangItem, "i128_div", i128_div_fn; + U128DivFnLangItem, "u128_div", u128_div_fn; + I128RemFnLangItem, "i128_rem", i128_rem_fn; + U128RemFnLangItem, "u128_rem", u128_rem_fn; + I128ShlFnLangItem, "i128_shl", i128_shl_fn; + U128ShlFnLangItem, "u128_shl", u128_shl_fn; + I128ShrFnLangItem, "i128_shr", i128_shr_fn; + U128ShrFnLangItem, "u128_shr", u128_shr_fn; + // And overflow versions for the operators that are checkable. + // While MIR calls these Checked*, they return (T,bool), not Option. + I128AddoFnLangItem, "i128_addo", i128_addo_fn; + U128AddoFnLangItem, "u128_addo", u128_addo_fn; + I128SuboFnLangItem, "i128_subo", i128_subo_fn; + U128SuboFnLangItem, "u128_subo", u128_subo_fn; + I128MuloFnLangItem, "i128_mulo", i128_mulo_fn; + U128MuloFnLangItem, "u128_mulo", u128_mulo_fn; + I128ShloFnLangItem, "i128_shlo", i128_shlo_fn; + U128ShloFnLangItem, "u128_shlo", u128_shlo_fn; + I128ShroFnLangItem, "i128_shro", i128_shro_fn; + U128ShroFnLangItem, "u128_shro", u128_shro_fn; } impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 5102b41598d6f..0d4429de22a84 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -70,7 +70,7 @@ pub use self::Note::*; use self::Aliasability::*; use middle::region; -use hir::def_id::{DefId, DefIndex}; +use hir::def_id::{DefId, LocalDefId}; use hir::map as hir_map; use infer::InferCtxt; use hir::def::{Def, CtorKind}; @@ -86,8 +86,9 @@ use syntax_pos::Span; use std::fmt; use std::rc::Rc; +use util::nodemap::ItemLocalSet; -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Categorization<'tcx> { Rvalue(ty::Region<'tcx>), // temporary val, argument is its scope StaticItem, @@ -108,7 +109,7 @@ pub struct Upvar { } // different kinds of pointers: -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PointerKind<'tcx> { /// `Box` Unique, @@ -176,7 +177,7 @@ pub enum Note { // dereference, but its type is the type *before* the dereference // (`@T`). So use `cmt.ty` to find the type of the value in a consistent // fashion. For more details, see the method `cat_pattern` -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct cmt_<'tcx> { pub id: ast::NodeId, // id of expr/pat producing this value pub span: Span, // span of same expr/pat @@ -190,7 +191,7 @@ pub type cmt<'tcx> = Rc>; pub enum ImmutabilityBlame<'tcx> { ImmLocal(ast::NodeId), - ClosureEnv(DefIndex), + ClosureEnv(LocalDefId), LocalDeref(ast::NodeId), AdtFieldDeref(&'tcx ty::AdtDef, &'tcx ty::FieldDef) } @@ -209,7 +210,7 @@ impl<'tcx> cmt_<'tcx> { adt_def.variant_with_id(variant_did) } _ => { - assert!(adt_def.is_univariant()); + assert_eq!(adt_def.variants.len(), 1); &adt_def.variants[0] } }; @@ -285,6 +286,7 @@ pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, pub region_scope_tree: &'a region::ScopeTree, pub tables: &'a ty::TypeckTables<'tcx>, + rvalue_promotable_map: Option>, infcx: Option<&'a InferCtxt<'a, 'gcx, 'tcx>>, } @@ -392,21 +394,46 @@ impl MutabilityCategory { impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, region_scope_tree: &'a region::ScopeTree, - tables: &'a ty::TypeckTables<'tcx>) + tables: &'a ty::TypeckTables<'tcx>, + rvalue_promotable_map: Option>) -> MemCategorizationContext<'a, 'tcx, 'tcx> { - MemCategorizationContext { tcx, region_scope_tree, tables, infcx: None } + MemCategorizationContext { + tcx, + region_scope_tree, + tables, + rvalue_promotable_map, + infcx: None + } } } impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { + /// Creates a `MemCategorizationContext` during type inference. + /// This is used during upvar analysis and a few other places. + /// Because the typeck tables are not yet complete, the results + /// from the analysis must be used with caution: + /// + /// - rvalue promotions are not known, so the lifetimes of + /// temporaries may be overly conservative; + /// - similarly, as the results of upvar analysis are not yet + /// known, the results around upvar accesses may be incorrect. pub fn with_infer(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, region_scope_tree: &'a region::ScopeTree, tables: &'a ty::TypeckTables<'tcx>) -> MemCategorizationContext<'a, 'gcx, 'tcx> { + let tcx = infcx.tcx; + + // Subtle: we can't do rvalue promotion analysis until the + // typeck phase is complete, which means that you can't trust + // the rvalue lifetimes that result, but that's ok, since we + // don't need to know those during type inference. + let rvalue_promotable_map = None; + MemCategorizationContext { - tcx: infcx.tcx, + tcx, region_scope_tree, tables, + rvalue_promotable_map, infcx: Some(infcx), } } @@ -477,10 +504,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { fn pat_ty(&self, pat: &hir::Pat) -> McResult> { let base_ty = self.node_ty(pat.hir_id)?; - // FIXME (Issue #18207): This code detects whether we are - // looking at a `ref x`, and if so, figures out what the type - // *being borrowed* is. But ideally we would put in a more - // fundamental fix to this conflated use of the node id. + // This code detects whether we are looking at a `ref x`, + // and if so, figures out what the type *being borrowed* is. let ret_ty = match pat.node { PatKind::Binding(..) => { let bm = *self.tables @@ -725,19 +750,29 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { let kind = match self.node_ty(fn_hir_id)?.sty { ty::TyGenerator(..) => ty::ClosureKind::FnOnce, - _ => { - match self.tables.closure_kinds().get(fn_hir_id) { - Some(&(kind, _)) => kind, - None => span_bug!(span, "missing closure kind"), + ty::TyClosure(closure_def_id, closure_substs) => { + match self.infcx { + // During upvar inference we may not know the + // closure kind, just use the LATTICE_BOTTOM value. + Some(infcx) => + infcx.closure_kind(closure_def_id, closure_substs) + .unwrap_or(ty::ClosureKind::LATTICE_BOTTOM), + + None => + self.tcx.global_tcx() + .lift(&closure_substs) + .expect("no inference cx, but inference variables in closure ty") + .closure_kind(closure_def_id, self.tcx.global_tcx()), } } + ref t => span_bug!(span, "unexpected type for fn in mem_categorization: {:?}", t), }; - let closure_expr_def_index = self.tcx.hir.local_def_id(fn_node_id).index; + let closure_expr_def_id = self.tcx.hir.local_def_id(fn_node_id); let var_hir_id = self.tcx.hir.node_to_hir_id(var_id); let upvar_id = ty::UpvarId { var_id: var_hir_id, - closure_expr_id: closure_expr_def_index + closure_expr_id: closure_expr_def_id.to_local(), }; let var_ty = self.node_ty(var_hir_id)?; @@ -812,7 +847,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // The environment of a closure is guaranteed to // outlive any bindings introduced in the body of the // closure itself. - scope: DefId::local(upvar_id.closure_expr_id), + scope: upvar_id.closure_expr_id.to_def_id(), bound_region: ty::BrEnv })); @@ -871,8 +906,9 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { span: Span, expr_ty: Ty<'tcx>) -> cmt<'tcx> { - let promotable = self.tcx.rvalue_promotable_to_static.borrow().get(&id).cloned() - .unwrap_or(false); + let hir_id = self.tcx.hir.node_to_hir_id(id); + let promotable = self.rvalue_promotable_map.as_ref().map(|m| m.contains(&hir_id.local_id)) + .unwrap_or(false); // Always promote `[T; 0]` (even when e.g. borrowed mutably). let promotable = match expr_ty.sty { @@ -887,7 +923,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { let re = if promotable { self.tcx.types.re_static } else { - self.temporary_scope(self.tcx.hir.node_to_hir_id(id).local_id) + self.temporary_scope(hir_id.local_id) }; let ret = self.cat_rvalue(id, span, re, expr_ty); debug!("cat_rvalue_node ret {:?}", ret); @@ -1069,7 +1105,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { -> cmt<'tcx> { // univariant enums do not need downcasts let base_did = self.tcx.parent_def_id(variant_did).unwrap(); - if !self.tcx.adt_def(base_did).is_univariant() { + if self.tcx.adt_def(base_did).variants.len() != 1 { let base_ty = base_cmt.ty; let ret = Rc::new(cmt_ { id: node.id(), @@ -1094,7 +1130,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } // FIXME(#19596) This is a workaround, but there should be a better way to do this - fn cat_pattern_(&self, cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()> + fn cat_pattern_(&self, mut cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()> where F : FnMut(cmt<'tcx>, &hir::Pat) { // Here, `cmt` is the categorization for the value being @@ -1144,6 +1180,56 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { debug!("cat_pattern: {:?} cmt={:?}", pat, cmt); + // If (pattern) adjustments are active for this pattern, adjust the `cmt` correspondingly. + // `cmt`s are constructed differently from patterns. For example, in + // + // ``` + // match foo { + // &&Some(x, ) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the + // corresponding `cmt` we start with a `cmt` for `foo`, and then, by traversing the + // pattern, try to answer the question: given the address of `foo`, how is `x` reached? + // + // `&&Some(x,)` `cmt_foo` + // `&Some(x,)` `deref { cmt_foo}` + // `Some(x,)` `deref { deref { cmt_foo }}` + // (x,)` `field0 { deref { deref { cmt_foo }}}` <- resulting cmt + // + // The above example has no adjustments. If the code were instead the (after adjustments, + // equivalent) version + // + // ``` + // match foo { + // Some(x, ) => { ... }, + // _ => { ... }, + // } + // ``` + // + // Then we see that to get the same result, we must start with `deref { deref { cmt_foo }}` + // instead of `cmt_foo` since the pattern is now `Some(x,)` and not `&&Some(x,)`, even + // though its assigned type is that of `&&Some(x,)`. + for _ in 0..self.tables + .pat_adjustments() + .get(pat.hir_id) + .map(|v| v.len()) + .unwrap_or(0) { + cmt = self.cat_deref(pat, cmt, true /* implicit */)?; + } + let cmt = cmt; // lose mutability + + // Invoke the callback, but only now, after the `cmt` has adjusted. + // + // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that + // case, the initial `cmt` will be that for `&Some(3)` and the pattern is `Some(x)`. We + // don't want to call `op` with these incompatible values. As written, what happens instead + // is that `op` is called with the adjusted cmt (that for `*&Some(3)`) and the pattern + // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` + // result in the cmt `Downcast(*&Some(3)).0` associated to `x` and invoke `op` with + // that (where the `ref` on `x` is implied). op(cmt.clone(), pat); match pat.node { @@ -1423,41 +1509,6 @@ impl<'tcx> cmt_<'tcx> { } } -impl<'tcx> fmt::Debug for cmt_<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{{:?} id:{} m:{:?} ty:{:?}}}", - self.cat, - self.id, - self.mutbl, - self.ty) - } -} - -impl<'tcx> fmt::Debug for Categorization<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Categorization::StaticItem => write!(f, "static"), - Categorization::Rvalue(r) => { write!(f, "rvalue({:?})", r) } - Categorization::Local(id) => { - let name = ty::tls::with(|tcx| tcx.hir.name(id)); - write!(f, "local({})", name) - } - Categorization::Upvar(upvar) => { - write!(f, "upvar({:?})", upvar) - } - Categorization::Deref(ref cmt, ptr) => { - write!(f, "{:?}-{:?}->", cmt.cat, ptr) - } - Categorization::Interior(ref cmt, interior) => { - write!(f, "{:?}.{:?}", cmt.cat, interior) - } - Categorization::Downcast(ref cmt, _) => { - write!(f, "{:?}->(enum)", cmt.cat) - } - } - } -} - pub fn ptr_sigil(ptr: PointerKind) -> &'static str { match ptr { Unique => "Box", @@ -1471,27 +1522,6 @@ pub fn ptr_sigil(ptr: PointerKind) -> &'static str { } } -impl<'tcx> fmt::Debug for PointerKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Unique => write!(f, "Box"), - BorrowedPtr(ty::ImmBorrow, ref r) | - Implicit(ty::ImmBorrow, ref r) => { - write!(f, "&{:?}", r) - } - BorrowedPtr(ty::MutBorrow, ref r) | - Implicit(ty::MutBorrow, ref r) => { - write!(f, "&{:?} mut", r) - } - BorrowedPtr(ty::UniqueImmBorrow, ref r) | - Implicit(ty::UniqueImmBorrow, ref r) => { - write!(f, "&{:?} uniq", r) - } - UnsafePtr(_) => write!(f, "*") - } - } -} - impl fmt::Debug for InteriorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 55d0c6b4c66a3..d5f26d1117c5b 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -58,11 +58,10 @@ fn item_might_be_inlined(item: &hir::Item) -> bool { } fn method_might_be_inlined<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - sig: &hir::MethodSig, impl_item: &hir::ImplItem, impl_src: DefId) -> bool { if attr::requests_inline(&impl_item.attrs) || - generics_require_inlining(&sig.generics) { + generics_require_inlining(&impl_item.generics) { return true } if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_src) { @@ -176,8 +175,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { Some(hir_map::NodeImplItem(impl_item)) => { match impl_item.node { hir::ImplItemKind::Const(..) => true, - hir::ImplItemKind::Method(ref sig, _) => { - if generics_require_inlining(&sig.generics) || + hir::ImplItemKind::Method(..) => { + if generics_require_inlining(&impl_item.generics) || attr::requests_inline(&impl_item.attrs) { true } else { @@ -271,7 +270,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { hir::ItemMod(..) | hir::ItemForeignMod(..) | hir::ItemImpl(..) | hir::ItemTrait(..) | hir::ItemStruct(..) | hir::ItemEnum(..) | - hir::ItemUnion(..) | hir::ItemDefaultImpl(..) | + hir::ItemUnion(..) | hir::ItemAutoImpl(..) | hir::ItemGlobalAsm(..) => {} } } @@ -293,9 +292,9 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { hir::ImplItemKind::Const(_, body) => { self.visit_nested_body(body); } - hir::ImplItemKind::Method(ref sig, body) => { + hir::ImplItemKind::Method(_, body) => { let did = self.tcx.hir.get_parent_did(search_item); - if method_might_be_inlined(self.tcx, sig, impl_item, did) { + if method_might_be_inlined(self.tcx, impl_item, did) { self.visit_nested_body(body) } } @@ -336,6 +335,12 @@ struct CollectPrivateImplItemsVisitor<'a, 'tcx: 'a> { impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> { fn visit_item(&mut self, item: &hir::Item) { + // Anything which has custom linkage gets thrown on the worklist no + // matter where it is in the crate. + if attr::contains_name(&item.attrs, "linkage") { + self.worklist.push(item.id); + } + // We need only trait impls here, not inherent impls, and only non-exported ones if let hir::ItemImpl(.., Some(ref trait_ref), _, ref impl_item_refs) = item.node { if !self.access_levels.is_reachable(item.id) { diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 59c9e8b4c432c..d3aa80e5585e2 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -12,7 +12,7 @@ //! the parent links in the region hierarchy. //! //! Most of the documentation on regions can be found in -//! `middle/infer/region_inference/README.md` +//! `middle/infer/region_constraints/README.md` use ich::{StableHashingContext, NodeIdHashingMode}; use util::nodemap::{FxHashMap, FxHashSet}; @@ -31,7 +31,6 @@ use hir; use hir::def_id::DefId; use hir::intravisit::{self, Visitor, NestedVisitorMap}; use hir::{Block, Arm, Pat, PatKind, Stmt, Expr, Local}; -use mir::transform::MirSource; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; @@ -156,26 +155,11 @@ pub struct BlockRemainder { pub first_statement_index: FirstStatementIndex, } -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable, - RustcDecodable, Copy)] -pub struct FirstStatementIndex { pub idx: u32 } - -impl Idx for FirstStatementIndex { - fn new(idx: usize) -> Self { - assert!(idx <= SCOPE_DATA_REMAINDER_MAX as usize); - FirstStatementIndex { idx: idx as u32 } - } - - fn index(self) -> usize { - self.idx as usize - } -} - -impl fmt::Debug for FirstStatementIndex { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.index(), formatter) - } -} +newtype_index!(FirstStatementIndex + { + pub idx + MAX = SCOPE_DATA_REMAINDER_MAX + }); impl From for Scope { #[inline] @@ -208,7 +192,7 @@ impl Scope { SCOPE_DATA_DESTRUCTION => ScopeData::Destruction(self.id), idx => ScopeData::Remainder(BlockRemainder { block: self.id, - first_statement_index: FirstStatementIndex { idx } + first_statement_index: FirstStatementIndex::new(idx as usize) }) } } @@ -336,7 +320,7 @@ pub struct ScopeTree { /// hierarchy based on their lexical mapping. This is used to /// handle the relationships between regions in a fn and in a /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_inference for + /// section of the README in infer::region_constraints for /// more details. closure_tree: FxHashMap, @@ -413,7 +397,7 @@ pub struct ScopeTree { /// The number of visit_expr and visit_pat calls done in the body. /// Used to sanity check visit_expr/visit_pat call count when - /// calculating geneartor interiors. + /// calculating generator interiors. body_expr_count: FxHashMap, } @@ -423,7 +407,7 @@ pub struct Context { /// of the innermost fn body. Each fn forms its own disjoint tree /// in the region hierarchy. These fn bodies are themselves /// arranged into a tree. See the "Modeling closures" section of - /// the README in infer::region_inference for more + /// the README in infer::region_constraints for more /// details. root_id: Option, @@ -662,7 +646,7 @@ impl<'tcx> ScopeTree { // different functions. Compare those fn for lexical // nesting. The reasoning behind this is subtle. See the // "Modeling closures" section of the README in - // infer::region_inference for more details. + // infer::region_constraints for more details. let a_root_scope = a_ancestors[a_index]; let b_root_scope = a_ancestors[a_index]; return match (a_root_scope.data(), b_root_scope.data()) { @@ -785,7 +769,7 @@ impl<'tcx> ScopeTree { /// Gives the number of expressions visited in a body. /// Used to sanity check visit_expr call count when - /// calculating geneartor interiors. + /// calculating generator interiors. pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option { self.body_expr_count.get(&body_id).map(|r| *r) } @@ -960,7 +944,7 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: hir::ExprAssignOp(..) | hir::ExprIndex(..) | hir::ExprUnary(..) | hir::ExprCall(..) | hir::ExprMethodCall(..) => { - // FIXME(#6268) Nested method calls + // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls // // The lifetimes for a call or method call look as follows: // @@ -1081,8 +1065,6 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, // Here, the expression `[...]` has an extended lifetime due to rule // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. - // - // FIXME(#6308) -- Note that `[]` patterns work more smoothly post-DST. if let Some(expr) = init { record_rvalue_scope_if_borrow_expr(visitor, &expr, blk_scope); @@ -1315,7 +1297,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> { // The body of the every fn is a root scope. self.cx.parent = self.cx.var_parent; - if let MirSource::Fn(_) = MirSource::from_node(self.tcx, owner_id) { + if let hir::BodyOwnerKind::Fn = self.tcx.hir.body_owner_kind(owner_id) { self.visit_expr(&body.value); } else { // Only functions have an outer terminating (drop) scope, while diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index d0c5460fa9714..3683425cee5b4 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -31,16 +31,37 @@ use syntax_pos::Span; use errors::DiagnosticBuilder; use util::common::ErrorReported; use util::nodemap::{NodeMap, NodeSet, FxHashSet, FxHashMap, DefIdMap}; -use rustc_back::slice; +use std::slice; use hir; use hir::intravisit::{self, Visitor, NestedVisitorMap}; +/// The origin of a named lifetime definition. +/// +/// This is used to prevent the usage of in-band lifetimes in `Fn`/`fn` syntax. +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +pub enum LifetimeDefOrigin { + // Explicit binders like `fn foo<'a>(x: &'a u8)` + Explicit, + // In-band declarations like `fn foo(x: &'a u8)` + InBand, +} + +impl LifetimeDefOrigin { + fn from_is_in_band(is_in_band: bool) -> Self { + if is_in_band { + LifetimeDefOrigin::InBand + } else { + LifetimeDefOrigin::Explicit + } + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] pub enum Region { Static, - EarlyBound(/* index */ u32, /* lifetime decl */ DefId), - LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId), + EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin), + LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId, LifetimeDefOrigin), LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } @@ -52,13 +73,16 @@ impl Region { let i = *index; *index += 1; let def_id = hir_map.local_def_id(def.lifetime.id); - (def.lifetime.name, Region::EarlyBound(i, def_id)) + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); + debug!("Region::early: index={} def_id={:?}", i, def_id); + (def.lifetime.name, Region::EarlyBound(i, def_id, origin)) } fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (hir::LifetimeName, Region) { let depth = ty::DebruijnIndex::new(1); let def_id = hir_map.local_def_id(def.lifetime.id); - (def.lifetime.name, Region::LateBound(depth, def_id)) + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); + (def.lifetime.name, Region::LateBound(depth, def_id, origin)) } fn late_anon(index: &Cell) -> Region { @@ -73,16 +97,16 @@ impl Region { Region::Static | Region::LateBoundAnon(..) => None, - Region::EarlyBound(_, id) | - Region::LateBound(_, id) | + Region::EarlyBound(_, id, _) | + Region::LateBound(_, id, _) | Region::Free(_, id) => Some(id) } } fn shifted(self, amount: u32) -> Region { match self { - Region::LateBound(depth, id) => { - Region::LateBound(depth.shifted(amount), id) + Region::LateBound(depth, id, origin) => { + Region::LateBound(depth.shifted(amount), id, origin) } Region::LateBoundAnon(depth, index) => { Region::LateBoundAnon(depth.shifted(amount), index) @@ -93,10 +117,10 @@ impl Region { fn from_depth(self, depth: u32) -> Region { match self { - Region::LateBound(debruijn, id) => { + Region::LateBound(debruijn, id, origin) => { Region::LateBound(ty::DebruijnIndex { depth: debruijn.depth - (depth - 1) - }, id) + }, id, origin) } Region::LateBoundAnon(debruijn, index) => { Region::LateBoundAnon(ty::DebruijnIndex { @@ -109,7 +133,7 @@ impl Region { fn subst(self, params: &[hir::Lifetime], map: &NamedRegionMap) -> Option { - if let Region::EarlyBound(index, _) = self { + if let Region::EarlyBound(index, _, _) = self { params.get(index as usize).and_then(|lifetime| { map.defs.get(&lifetime.id).cloned() }) @@ -186,6 +210,9 @@ struct LifetimeContext<'a, 'tcx: 'a> { // I'm sorry. trait_ref_hack: bool, + // Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. + is_in_fn_syntax: bool, + // List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Name, Span)>, @@ -201,6 +228,11 @@ enum Scope<'a> { /// declaration `Binder` and the location it's referenced from. Binder { lifetimes: FxHashMap, + + /// if we extend this scope with another scope, what is the next index + /// we should use for an early-bound region? + next_early_index: u32, + s: ScopeRef<'a> }, @@ -274,6 +306,7 @@ pub fn krate(sess: &Session, map: &mut map, scope: ROOT_SCOPE, trait_ref_hack: false, + is_in_fn_syntax: false, labels_in_fn: vec![], xcrate_object_lifetime_defaults: DefIdMap(), }; @@ -313,7 +346,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir::ItemExternCrate(_) | hir::ItemUse(..) | hir::ItemMod(..) | - hir::ItemDefaultImpl(..) | + hir::ItemAutoImpl(..) | hir::ItemForeignMod(..) | hir::ItemGlobalAsm(..) => { // These sorts of items have no lifetime parameters at all. @@ -332,7 +365,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir::ItemEnum(_, ref generics) | hir::ItemStruct(_, ref generics) | hir::ItemUnion(_, ref generics) | - hir::ItemTrait(_, ref generics, ..) | + hir::ItemTrait(_, _, ref generics, ..) | hir::ItemImpl(_, _, _, ref generics, ..) => { // These kinds of items have only early bound lifetime parameters. let mut index = if let hir::ItemTrait(..) = item.node { @@ -343,8 +376,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let lifetimes = generics.lifetimes.iter().map(|def| { Region::early(self.hir_map, &mut index, def) }).collect(); + let next_early_index = index + generics.ty_params.len() as u32; let scope = Scope::Binder { lifetimes, + next_early_index, s: ROOT_SCOPE }; self.with(scope, |old_scope, this| { @@ -365,16 +400,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir::ForeignItemStatic(..) => { intravisit::walk_foreign_item(self, item); } + hir::ForeignItemType => { + intravisit::walk_foreign_item(self, item); + } } } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { + debug!("visit_ty: ty={:?}", ty); match ty.node { hir::TyBareFn(ref c) => { + let next_early_index = self.next_early_index(); + let was_in_fn_syntax = self.is_in_fn_syntax; + self.is_in_fn_syntax = true; let scope = Scope::Binder { lifetimes: c.lifetimes.iter().map(|def| { - Region::late(self.hir_map, def) - }).collect(), + Region::late(self.hir_map, def) + }).collect(), + next_early_index, s: self.scope }; self.with(scope, |old_scope, this| { @@ -383,6 +426,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.check_lifetime_defs(old_scope, &c.lifetimes); intravisit::walk_ty(this, ty); }); + self.is_in_fn_syntax = was_in_fn_syntax; } hir::TyTraitObject(ref bounds, ref lifetime) => { for bound in bounds { @@ -402,6 +446,60 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }; self.with(scope, |_, this| this.visit_ty(&mt.ty)); } + hir::TyImplTraitExistential(ref exist_ty, ref lifetimes) => { + // Resolve the lifetimes that are applied to the existential type. + // These are resolved in the current scope. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `fn foo<'a>() -> MyAnonTy<'a> { ... }` + // ^ ^this gets resolved in the current scope + for lifetime in lifetimes { + self.visit_lifetime(lifetime); + + // Check for predicates like `impl for<'a> SomeTrait>` + // and ban them. Type variables instantiated inside binders aren't + // well-supported at the moment, so this doesn't work. + // In the future, this should be fixed and this error should be removed. + let def = self.map.defs.get(&lifetime.id); + if let Some(&Region::LateBound(_, def_id, _)) = def { + if let Some(node_id) = self.hir_map.as_local_node_id(def_id) { + // Ensure that the parent of the def is an item, not HRTB + let parent_id = self.hir_map.get_parent_node(node_id); + let parent_impl_id = hir::ImplItemId { node_id: parent_id }; + let parent_trait_id = hir::TraitItemId { node_id: parent_id }; + let krate = self.hir_map.forest.krate(); + if !(krate.items.contains_key(&parent_id) || + krate.impl_items.contains_key(&parent_impl_id) || + krate.trait_items.contains_key(&parent_trait_id)) + { + span_err!(self.sess, lifetime.span, E0657, + "`impl Trait` can only capture lifetimes \ + bound at the fn or impl level"); + } + } + } + } + + // Resolve the lifetimes in the bounds to the lifetime defs in the generics. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `abstract type MyAnonTy<'b>: MyTrait<'b>;` + // ^ ^ this gets resolved in the scope of + // the exist_ty generics + let hir::ExistTy { ref generics, ref bounds } = *exist_ty; + let mut index = self.next_early_index(); + debug!("visit_ty: index = {}", index); + let lifetimes = generics.lifetimes.iter() + .map(|lt_def| Region::early(self.hir_map, &mut index, lt_def)) + .collect(); + + let next_early_index = index + generics.ty_params.len() as u32; + let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope }; + self.with(scope, |_old_scope, this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_ty_param_bound(bound); + } + }); + } _ => { intravisit::walk_ty(self, ty) } @@ -412,7 +510,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { if let hir::TraitItemKind::Method(ref sig, _) = trait_item.node { self.visit_early_late( Some(self.hir_map.get_parent(trait_item.id)), - &sig.decl, &sig.generics, + &sig.decl, &trait_item.generics, |this| intravisit::walk_trait_item(this, trait_item)) } else { intravisit::walk_trait_item(self, trait_item); @@ -423,7 +521,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node { self.visit_early_late( Some(self.hir_map.get_parent(impl_item.id)), - &sig.decl, &sig.generics, + &sig.decl, &impl_item.generics, |this| intravisit::walk_impl_item(this, impl_item)) } else { intravisit::walk_impl_item(self, impl_item); @@ -432,7 +530,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { if lifetime_ref.is_elided() { - self.resolve_elided_lifetimes(slice::ref_slice(lifetime_ref)); + self.resolve_elided_lifetimes(slice::from_ref(lifetime_ref)); return; } if lifetime_ref.is_static() { @@ -460,6 +558,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_generics(&mut self, generics: &'tcx hir::Generics) { + check_mixed_explicit_and_in_band_defs(&self.sess, &generics.lifetimes); for ty_param in generics.ty_params.iter() { walk_list!(self, visit_ty_param_bound, &ty_param.bounds); if let Some(ref ty) = ty_param.default { @@ -474,10 +573,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .. }) => { if !bound_lifetimes.is_empty() { self.trait_ref_hack = true; + let next_early_index = self.next_early_index(); let scope = Scope::Binder { lifetimes: bound_lifetimes.iter().map(|def| { Region::late(self.hir_map, def) - }).collect(), + }).collect(), + next_early_index, s: self.scope }; let result = self.with(scope, |old_scope, this| { @@ -521,10 +622,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { span_err!(self.sess, trait_ref.span, E0316, "nested quantification of lifetimes"); } + let next_early_index = self.next_early_index(); let scope = Scope::Binder { lifetimes: trait_ref.bound_lifetimes.iter().map(|def| { Region::late(self.hir_map, def) - }).collect(), + }).collect(), + next_early_index, s: self.scope }; self.with(scope, |old_scope, this| { @@ -567,6 +670,22 @@ impl ShadowKind { } } +fn check_mixed_explicit_and_in_band_defs( + sess: &Session, + lifetime_defs: &[hir::LifetimeDef], +) { + let oob_def = lifetime_defs.iter().find(|lt| !lt.in_band); + let in_band_def = lifetime_defs.iter().find(|lt| lt.in_band); + + if let (Some(oob_def), Some(in_band_def)) = (oob_def, in_band_def) { + struct_span_err!(sess, in_band_def.lifetime.span, E0688, + "cannot mix in-band and explicit lifetime definitions") + .span_label(in_band_def.lifetime.span, "in-band lifetime definition here") + .span_label(oob_def.lifetime.span, "explicit lifetime definition here") + .emit(); + } +} + fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { // lifetime/lifetime shadowing is an error @@ -656,7 +775,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) { Scope::Root => { return; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { // FIXME (#24278): non-hygienic comparison if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) { let node_id = hir_map.as_local_node_id(def.id().unwrap()) @@ -685,7 +804,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map) hir::ItemUnion(_, ref generics) | hir::ItemEnum(_, ref generics) | hir::ItemTy(_, ref generics) | - hir::ItemTrait(_, ref generics, ..) => { + hir::ItemTrait(_, _, ref generics, ..) => { let result = object_lifetime_defaults_for_item(hir_map, generics); // Debugging aid. @@ -695,7 +814,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map) match *set { Set1::Empty => "BaseDefault".to_string(), Set1::One(Region::Static) => "'static".to_string(), - Set1::One(Region::EarlyBound(i, _)) => { + Set1::One(Region::EarlyBound(i, _, _)) => { generics.lifetimes[i as usize].lifetime.name.name().to_string() } Set1::One(_) => bug!(), @@ -765,7 +884,8 @@ fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics) def.lifetime.name == name }).map_or(Set1::Many, |(i, def)| { let def_id = hir_map.local_def_id(def.lifetime.id); - Set1::One(Region::EarlyBound(i as u32, def_id)) + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); + Set1::One(Region::EarlyBound(i as u32, def_id, origin)) }) } } @@ -796,6 +916,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { map: *map, scope: &wrap_scope, trait_ref_hack: self.trait_ref_hack, + is_in_fn_syntax: self.is_in_fn_syntax, labels_in_fn, xcrate_object_lifetime_defaults, }; @@ -841,7 +962,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { index += 1; // Self comes first. } match parent.node { - hir::ItemTrait(_, ref generics, ..) | + hir::ItemTrait(_, _, ref generics, ..) | hir::ItemImpl(_, _, _, ref generics, ..) => { index += (generics.lifetimes.len() + generics.ty_params.len()) as u32; } @@ -857,8 +978,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }).collect(); + let next_early_index = index + generics.ty_params.len() as u32; + let scope = Scope::Binder { lifetimes, + next_early_index, s: self.scope }; self.with(scope, move |old_scope, this| { @@ -867,7 +991,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }); } + /// Returns the next index one would use for an early-bound-region + /// if extending the current scope. + fn next_early_index(&self) -> u32 { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root => + return 0, + + Scope::Binder { next_early_index, .. } => + return next_early_index, + + Scope::Body { s, .. } | + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => + scope = s, + } + } + } + fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { + debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref); + // Walk up the scope chain, tracking the number of fn scopes // that we pass through, until we find a lifetime with the // given name or we run out of scopes. @@ -886,7 +1032,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { break None; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { if let Some(&def) = lifetimes.get(&lifetime_ref.name) { break Some(def.shifted(late_depth)); } else { @@ -923,6 +1069,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => {} } } + + // Check for fn-syntax conflicts with in-band lifetime definitions + if self.is_in_fn_syntax { + match def { + Region::EarlyBound(_, _, LifetimeDefOrigin::InBand) | + Region::LateBound(_, _, LifetimeDefOrigin::InBand) => { + struct_span_err!(self.sess, lifetime_ref.span, E0687, + "lifetimes used in `fn` or `Fn` syntax must be \ + explicitly declared using `<...>` binders") + .span_label(lifetime_ref.span, + "in-band lifetime definition") + .emit(); + }, + + Region::Static | + Region::EarlyBound(_, _, LifetimeDefOrigin::Explicit) | + Region::LateBound(_, _, LifetimeDefOrigin::Explicit) | + Region::LateBoundAnon(..) | + Region::Free(..) => {} + } + } + self.insert_lifetime(lifetime_ref, def); } else { struct_span_err!(self.sess, lifetime_ref.span, E0261, @@ -936,8 +1104,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { def: Def, depth: usize, params: &'tcx hir::PathParameters) { + if params.parenthesized { + let was_in_fn_syntax = self.is_in_fn_syntax; + self.is_in_fn_syntax = true; self.visit_fn_like_elision(params.inputs(), Some(¶ms.bindings[0].ty)); + self.is_in_fn_syntax = was_in_fn_syntax; return; } @@ -998,8 +1170,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &map.object_lifetime_defaults[&id] } else { let cstore = self.cstore; + let sess = self.sess; self.xcrate_object_lifetime_defaults.entry(def_id).or_insert_with(|| { - cstore.item_generics_cloned_untracked(def_id).types.into_iter().map(|def| { + cstore.item_generics_cloned_untracked(def_id, sess) + .types + .into_iter() + .map(|def| { def.object_lifetime_default }).collect() }) @@ -1254,7 +1430,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) { match lifetime { - Region::LateBound(debruijn, _) | + Region::LateBound(debruijn, _, _) | Region::LateBoundAnon(debruijn, _) if debruijn.depth < self.binder_depth => { self.have_bound_regions = true; @@ -1513,7 +1689,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { if let Some(&def) = lifetimes.get(&lifetime.name) { let node_id = self.hir_map .as_local_node_id(def.id().unwrap()) @@ -1542,7 +1718,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { probably a bug in syntax::fold"); } - debug!("{} resolved to {:?} span={:?}", + debug!("insert_lifetime: {} resolved to {:?} span={:?}", self.hir_map.node_to_string(lifetime_ref.id), def, self.sess.codemap().span_to_string(lifetime_ref.span)); @@ -1574,7 +1750,6 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, let mut appears_in_output = AllCollector { regions: FxHashSet(), - impl_trait: false }; intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output); @@ -1587,7 +1762,6 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, // ignore binders here and scrape up all names we see. let mut appears_in_where_clause = AllCollector { regions: FxHashSet(), - impl_trait: false }; for ty_param in generics.ty_params.iter() { walk_list!(&mut appears_in_where_clause, @@ -1597,6 +1771,17 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, walk_list!(&mut appears_in_where_clause, visit_where_predicate, &generics.where_clause.predicates); + // We need to collect argument impl Trait lifetimes as well, + // we do so here. + walk_list!(&mut appears_in_where_clause, + visit_ty, + decl.inputs.iter().filter(|ty| { + if let hir::TyImplTraitUniversal(..) = ty.node { + true + } else { + false + } + })); for lifetime_def in &generics.lifetimes { if !lifetime_def.bounds.is_empty() { // `'a: 'b` means both `'a` and `'b` are referenced @@ -1617,9 +1802,6 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, // appears in the where clauses? early-bound. if appears_in_where_clause.regions.contains(&name) { continue; } - // any `impl Trait` in the return type? early-bound. - if appears_in_output.impl_trait { continue; } - // does not appear in the inputs, but appears in the return type? early-bound. if !constrained_by_input.regions.contains(&name) && appears_in_output.regions.contains(&name) { @@ -1678,7 +1860,6 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, struct AllCollector { regions: FxHashSet, - impl_trait: bool } impl<'v> Visitor<'v> for AllCollector { @@ -1689,12 +1870,5 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { self.regions.insert(lifetime_ref.name); } - - fn visit_ty(&mut self, ty: &hir::Ty) { - if let hir::TyImplTrait(_) = ty.node { - self.impl_trait = true; - } - intravisit::walk_ty(self, ty); - } } } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 89049958309a1..2f527413432bc 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -18,8 +18,9 @@ use hir::def::Def; use hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE}; use ty::{self, TyCtxt}; use middle::privacy::AccessLevels; +use session::DiagnosticMessageId; use syntax::symbol::Symbol; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::{Span, MultiSpan, DUMMY_SP}; use syntax::ast; use syntax::ast::{NodeId, Attribute}; use syntax::feature_gate::{GateIssue, emit_feature_err, find_lang_feature_accepted_version}; @@ -429,7 +430,7 @@ impl<'a, 'tcx> Index<'tcx> { // while maintaining the invariant that all sysroot crates are unstable // by default and are unable to be used. if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { - let reason = "this crate is being loaded from the sysroot, and \ + let reason = "this crate is being loaded from the sysroot, an \ unstable location; did you mean to load this crate \ from crates.io via `Cargo.toml` instead?"; let stability = tcx.intern_stability(Stability { @@ -515,11 +516,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { return; } - let lint_deprecated = |note: Option| { + let lint_deprecated = |def_id: DefId, note: Option| { + let path = self.item_path_str(def_id); + let msg = if let Some(note) = note { - format!("use of deprecated item: {}", note) + format!("use of deprecated item '{}': {}", path, note) } else { - format!("use of deprecated item") + format!("use of deprecated item '{}'", path) }; self.lint_node(lint::builtin::DEPRECATED, id, span, &msg); @@ -537,7 +540,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }; if !skip { - lint_deprecated(depr_entry.attr.note); + lint_deprecated(def_id, depr_entry.attr.note); } } @@ -556,7 +559,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..}) = stability { if id != ast::DUMMY_NODE_ID { - lint_deprecated(Some(reason)); + lint_deprecated(def_id, Some(reason)); } } @@ -597,8 +600,29 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { feature.as_str(), &r), None => format!("use of unstable library feature '{}'", &feature) }; - emit_feature_err(&self.sess.parse_sess, &feature.as_str(), span, - GateIssue::Library(Some(issue)), &msg); + + + let msp: MultiSpan = span.into(); + let cm = &self.sess.parse_sess.codemap(); + let span_key = msp.primary_span().and_then(|sp: Span| + if sp != DUMMY_SP { + let file = cm.lookup_char_pos(sp.lo()).file; + if file.name.starts_with("<") && file.name.ends_with(" macros>") { + None + } else { + Some(span) + } + } else { + None + } + ); + + let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); + let fresh = self.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + emit_feature_err(&self.sess.parse_sess, &feature.as_str(), span, + GateIssue::Library(Some(issue)), &msg); + } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are diff --git a/src/librustc/mir/README.md b/src/librustc/mir/README.md index e8ed8bf104cc8..fb0c7ce1df23d 100644 --- a/src/librustc/mir/README.md +++ b/src/librustc/mir/README.md @@ -6,7 +6,7 @@ register and define new MIR transformations and analyses. Most of the code that operates on MIR can be found in the `librustc_mir` crate or other crates. The code found here in -`librustc` is just the datatype definitions, alonging the functions +`librustc` is just the datatype definitions, along with the functions which operate on MIR to be placed everywhere else. ## MIR Data Types and visitor @@ -27,7 +27,7 @@ As a MIR *consumer*, you are expected to use one of the queries that returns a "final MIR". As of the time of this writing, there is only one: `optimized_mir(def_id)`, but more are expected to come in the future. For foreign def-ids, we simply read the MIR from the other -crate's metadata. But for local query, this query will construct the +crate's metadata. But for local def-ids, the query will construct the MIR and then iteratively optimize it by putting it through various pipeline stages. This section describes those pipeline stages and how you can extend them. @@ -51,7 +51,7 @@ a `&'tcx Steal>`, allocated using **stolen** by the next suite of optimizations -- this is an optimization to avoid cloning the MIR. Attempting to use a stolen result will cause a panic in the compiler. Therefore, it is important -that you not read directly from these intermediate queries except as +that you do not read directly from these intermediate queries except as part of the MIR processing pipeline. Because of this stealing mechanism, some care must also be taken to diff --git a/src/librustc/mir/interpret/cast.rs b/src/librustc/mir/interpret/cast.rs index c9f08da92122c..53a0a9c4fdeec 100644 --- a/src/librustc/mir/interpret/cast.rs +++ b/src/librustc/mir/interpret/cast.rs @@ -1,7 +1,10 @@ use ty::{self, Ty}; use syntax::ast::{FloatTy, IntTy, UintTy}; +use rustc_const_math::ConstFloat; use super::{PrimVal, EvalContext, EvalResult, MemoryPointer, PointerArithmetic, Machine}; +use rustc_apfloat::ieee::{Single, Double}; +use rustc_apfloat::Float; impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub(super) fn cast_primval( @@ -19,7 +22,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { val @ PrimVal::Bytes(_) => { use super::PrimValKind::*; match src_kind { - F32 => self.cast_from_float(val.to_f32()? as f64, dest_ty), + F32 => self.cast_from_float(val.to_f32()?, dest_ty), F64 => self.cast_from_float(val.to_f64()?, dest_ty), I8 | I16 | I32 | I64 | I128 => { @@ -78,10 +81,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))), TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))), - TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)), - TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)), - TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i128 as f32)), - TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), + TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => err!(InvalidChar(v)), @@ -93,17 +94,26 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - fn cast_from_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use ty::TypeVariants::*; match ty.sty { - // Casting negative floats to unsigned integers yields zero. - TyUint(_) if val < 0.0 => self.cast_from_int(0, ty, false), - TyInt(_) if val < 0.0 => self.cast_from_int(val as i128 as u128, ty, true), + TyUint(t) => { + let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); + match val.ty { + FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)), + FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)), + } + }, - TyInt(_) | ty::TyUint(_) => self.cast_from_int(val as u128, ty, false), + TyInt(t) => { + let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); + match val.ty { + FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)), + FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)), + } + }, - TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)), - TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)), + TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))), _ => err!(Unimplemented(format!("float to {:?} cast", ty))), } } diff --git a/src/librustc/mir/interpret/const_eval.rs b/src/librustc/mir/interpret/const_eval.rs index 10da2b9ded765..446200e137582 100644 --- a/src/librustc/mir/interpret/const_eval.rs +++ b/src/librustc/mir/interpret/const_eval.rs @@ -1,12 +1,11 @@ -use traits::Reveal; use ty::{self, TyCtxt, Ty, Instance, layout}; use mir; use syntax::ast::Mutability; use syntax::codemap::Span; -use super::{EvalResult, EvalError, EvalErrorKind, GlobalId, Lvalue, Value, PrimVal, EvalContext, - StackPopCleanup, PtrAndAlign, MemoryKind, ValTy}; +use super::{EvalResult, EvalError, EvalErrorKind, GlobalId, Place, Value, PrimVal, EvalContext, + StackPopCleanup, PtrAndAlign, ValTy, HasMemory}; use rustc_const_math::ConstInt; @@ -16,73 +15,76 @@ use std::error::Error; pub fn eval_body<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, -) -> EvalResult<'tcx, (Value, Ty<'tcx>)> { + param_env: ty::ParamEnv<'tcx>, +) -> (EvalResult<'tcx, (PtrAndAlign, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeFunctionEvaluator>) { + debug!("eval_body: {:?}, {:?}", instance, param_env); let limits = super::ResourceLimits::default(); - let mut ecx = EvalContext::::new(tcx, limits, (), ()); + let mut ecx = EvalContext::::new(tcx, limits, param_env, ()); let cid = GlobalId { instance, promoted: None, }; - if ecx.tcx.has_attr(instance.def_id(), "linkage") { - return Err(ConstEvalError::NotConst("extern global".to_string()).into()); - } - let mir = ecx.load_mir(instance.def)?; - if !ecx.globals.contains_key(&cid) { - let size = ecx.type_size_with_substs(mir.return_ty, instance.substs)? - .expect("unsized global"); - let align = ecx.type_align_with_substs(mir.return_ty, instance.substs)?; - let ptr = ecx.memory.allocate( - size, - align, - MemoryKind::UninitializedStatic, - )?; - let aligned = !ecx.is_packed(mir.return_ty)?; - ecx.globals.insert( - cid, - PtrAndAlign { - ptr: ptr.into(), - aligned, - }, - ); - let mutable = !mir.return_ty.is_freeze( - ecx.tcx, - ty::ParamEnv::empty(Reveal::All), - mir.span, - ); - let mutability = if mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }; - let cleanup = StackPopCleanup::MarkStatic(mutability); - let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); - trace!("const_eval: pushing stack frame for global: {}", name); - ecx.push_stack_frame( - instance, - mir.span, - mir, - Lvalue::from_ptr(ptr), - cleanup, - )?; + let try = (|| { + if ecx.tcx.has_attr(instance.def_id(), "linkage") { + return Err(ConstEvalError::NotConst("extern global".to_string()).into()); + } + let mir = ecx.load_mir(instance.def)?; + if tcx.interpret_interner.borrow().get_cached(cid).is_none() { + let layout = ecx.type_layout_with_substs(mir.return_ty(), instance.substs)?; + assert!(!layout.is_unsized()); + let ptr = ecx.memory.allocate( + layout.size.bytes(), + layout.align.abi(), + None, + )?; + tcx.interpret_interner.borrow_mut().cache( + cid, + PtrAndAlign { + ptr: ptr.into(), + aligned: !layout.is_packed(), + }, + ); + let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable); + let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); + trace!("const_eval: pushing stack frame for global: {}", name); + ecx.push_stack_frame( + instance, + mir.span, + mir, + Place::from_ptr(ptr), + cleanup.clone(), + )?; - while ecx.step()? {} - } - let value = Value::ByRef(*ecx.globals.get(&cid).expect("global not cached")); - let valty = ValTy { - value, - ty: mir.return_ty, - }; - // FIXME: store cached value in TyCtxt - Ok(value, mir.return_ty)) + while ecx.step()? {} + + // reinsert the stack frame so any future queries have the correct substs + ecx.push_stack_frame( + instance, + mir.span, + mir, + Place::from_ptr(ptr), + cleanup, + )?; + } + let value = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"); + let ret_ty = ecx.monomorphize(mir.return_ty(), instance.substs); + Ok((value, ret_ty)) + })(); + (try, ecx) } pub fn eval_body_as_integer<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, instance: Instance<'tcx>, ) -> EvalResult<'tcx, ConstInt> { - let (prim, ty) = eval_body_as_primval(tcx, instance)?; - let prim = prim.to_bytes()?; + let (ptr_ty, ecx) = eval_body(tcx, instance, param_env); + let (ptr, ty) = ptr_ty?; + let prim = match ecx.read_maybe_aligned(ptr.aligned, |ectx| ectx.try_read_value(ptr.ptr, ty))? { + Some(Value::ByVal(prim)) => prim.to_bytes()?, + _ => return err!(TypeNotPrimitive(ty)), + }; use syntax::ast::{IntTy, UintTy}; use ty::TypeVariants::*; use rustc_const_math::{ConstIsize, ConstUsize}; @@ -115,7 +117,7 @@ pub fn eval_body_as_integer<'a, 'tcx>( }) } -struct CompileTimeFunctionEvaluator; +pub struct CompileTimeFunctionEvaluator; impl<'tcx> Into> for ConstEvalError { fn into(self) -> EvalError<'tcx> { @@ -160,17 +162,23 @@ impl Error for ConstEvalError { } impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { - type Data = (); + type Data = ty::ParamEnv<'tcx>; type MemoryData = (); type MemoryKinds = !; + fn param_env<'a>( + ecx: &EvalContext<'a, 'tcx, Self>, + ) -> ty::ParamEnv<'tcx> { + ecx.machine_data + } fn eval_fn_call<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, - destination: Option<(Lvalue, mir::BasicBlock)>, + destination: Option<(Place, mir::BasicBlock)>, _args: &[ValTy<'tcx>], span: Span, _sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx, bool> { + debug!("eval_fn_call: {:?}", instance); if !ecx.tcx.is_const_fn(instance.def_id()) { return Err( ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(), @@ -187,34 +195,59 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { } Err(other) => return Err(other), }; - let (return_lvalue, return_to_block) = match destination { - Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), - None => (Lvalue::undef(), StackPopCleanup::None), + let (return_place, return_to_block) = match destination { + Some((place, block)) => (place, StackPopCleanup::Goto(block)), + None => (Place::undef(), StackPopCleanup::None), }; ecx.push_stack_frame( instance, span, mir, - return_lvalue, + return_place, return_to_block, )?; Ok(false) } + fn call_intrinsic<'a>( - _ecx: &mut EvalContext<'a, 'tcx, Self>, - _instance: ty::Instance<'tcx>, + ecx: &mut EvalContext<'a, 'tcx, Self>, + instance: ty::Instance<'tcx>, _args: &[ValTy<'tcx>], - _dest: Lvalue, - _dest_ty: Ty<'tcx>, - _dest_layout: &'tcx layout::Layout, - _target: mir::BasicBlock, + dest: Place, + dest_layout: layout::TyLayout<'tcx>, + target: mir::BasicBlock, ) -> EvalResult<'tcx> { - Err( - ConstEvalError::NeedsRfc("calling intrinsics".to_string()).into(), - ) + let substs = instance.substs; + + let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..]; + match intrinsic_name { + "min_align_of" => { + let elem_ty = substs.type_at(0); + let elem_align = ecx.type_align(elem_ty)?; + let align_val = PrimVal::from_u128(elem_align as u128); + ecx.write_primval(dest, align_val, dest_layout.ty)?; + } + + "size_of" => { + let ty = substs.type_at(0); + let size = ecx.type_size(ty)?.expect( + "size_of intrinsic called on unsized value", + ) as u128; + ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?; + } + + name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()), + } + + ecx.goto_block(target); + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(()) } fn try_ptr_op<'a>( @@ -241,7 +274,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { fn box_alloc<'a>( _ecx: &mut EvalContext<'a, 'tcx, Self>, _ty: ty::Ty<'tcx>, - _dest: Lvalue, + _dest: Place, ) -> EvalResult<'tcx> { Err( ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(), diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index e3356ea19fa1d..9ebfe25c107a9 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -113,7 +113,7 @@ pub enum EvalErrorKind<'tcx> { DeallocatedWrongMemoryKind(String, String), ReallocateNonBasePtr, DeallocateNonBasePtr, - IncorrectAllocationInformation, + IncorrectAllocationInformation(u64, usize, u64, u64), Layout(layout::LayoutError<'tcx>), HeapAllocZeroBytes, HeapAllocNonPowerOfTwoAlignment(u64), @@ -121,6 +121,9 @@ pub enum EvalErrorKind<'tcx> { Panic, ReadFromReturnPointer, PathNotFound(Vec), + UnimplementedTraitSelection, + /// Abort in case type errors are reached + TypeckError, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -220,7 +223,7 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to reallocate with a pointer not to the beginning of an existing object", DeallocateNonBasePtr => "tried to deallocate with a pointer not to the beginning of an existing object", - IncorrectAllocationInformation => + IncorrectAllocationInformation(..) => "tried to deallocate or reallocate using incorrect alignment or size", Layout(_) => "rustc layout computation failed", @@ -238,6 +241,10 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to read from the return pointer", EvalErrorKind::PathNotFound(_) => "a path could not be resolved, maybe the crate is not loaded", + UnimplementedTraitSelection => + "there were unresolved type arguments during trait selection", + TypeckError => + "encountered constants with type errors, stopping evaluation", } } @@ -307,6 +314,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "Cannot find path {:?}", path), MachineError(ref inner) => write!(f, "machine error: {}", inner), + IncorrectAllocationInformation(size, size2, align, align2) => + write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size, align, size2, align2), _ => write!(f, "{}", self.description()), } } diff --git a/src/librustc/mir/interpret/eval_context.rs b/src/librustc/mir/interpret/eval_context.rs index d9416c09699a6..26e9362c70949 100644 --- a/src/librustc/mir/interpret/eval_context.rs +++ b/src/librustc/mir/interpret/eval_context.rs @@ -7,15 +7,14 @@ use middle::const_val::ConstVal; use middle::region; use mir; use traits::Reveal; -use ty::layout::{self, Layout, Size, Align, HasDataLayout}; +use ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout}; use ty::subst::{Subst, Substs, Kind}; -use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::{self, Ty, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; use syntax::ast::Mutability; -use syntax::abi::Abi; -use super::{EvalError, EvalResult, EvalErrorKind, GlobalId, Lvalue, LvalueExtra, Memory, +use super::{EvalError, EvalResult, EvalErrorKind, GlobalId, Place, PlaceExtra, Memory, MemoryPointer, HasMemory, MemoryKind, operator, PrimVal, PrimValKind, Value, Pointer, ValidationQuery, Machine}; @@ -29,12 +28,9 @@ pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> { /// The virtual memory system. pub memory: Memory<'a, 'tcx, M>, - /// Lvalues that were suspended by the validation subsystem, and will be recovered later + /// Places that were suspended by the validation subsystem, and will be recovered later pub(crate) suspended: HashMap>>, - /// Precomputed statics, constants and promoteds. - pub globals: HashMap, PtrAndAlign>, - /// The virtual call stack. pub(crate) stack: Vec>, @@ -62,13 +58,13 @@ pub struct Frame<'tcx> { pub span: codemap::Span, //////////////////////////////////////////////////////////////////////////////// - // Return lvalue and locals + // Return place and locals //////////////////////////////////////////////////////////////////////////////// /// The block to return to when returning from the current stack frame pub return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to. - pub return_lvalue: Lvalue, + pub return_place: Place, /// The list of locals for this stack frame, stored in order as /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option`s. @@ -148,7 +144,7 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { #[derive(Copy, Clone, Debug)] pub struct PtrAndAlign { pub ptr: Pointer, - /// Remember whether this lvalue is *supposed* to be aligned. + /// Remember whether this place is *supposed* to be aligned. pub aligned: bool, } @@ -164,6 +160,55 @@ impl PtrAndAlign { } } +impl<'a, 'tcx, M: Machine<'tcx>> HasDataLayout for &'a EvalContext<'a, 'tcx, M> { + #[inline] + fn data_layout(&self) -> &layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> HasDataLayout + for &'c &'b mut EvalContext<'a, 'tcx, M> { + #[inline] + fn data_layout(&self) -> &layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx, M> { + #[inline] + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> layout::HasTyCtxt<'tcx> + for &'c &'b mut EvalContext<'a, 'tcx, M> { + #[inline] + fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'a, 'tcx, M: Machine<'tcx>> LayoutOf> for &'a EvalContext<'a, 'tcx, M> { + type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + (self.tcx, M::param_env(self)).layout_of(ty) + .map_err(|layout| EvalErrorKind::Layout(layout).into()) + } +} + +impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> LayoutOf> + for &'c &'b mut EvalContext<'a, 'tcx, M> { + type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; + + #[inline] + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + (&**self).layout_of(ty) + } +} + impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn new( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -174,9 +219,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { EvalContext { machine_data, tcx, - memory: Memory::new(&tcx.data_layout, limits.memory_size, memory_data), + memory: Memory::new(tcx, limits.memory_size, memory_data), suspended: HashMap::new(), - globals: HashMap::new(), stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, @@ -197,7 +241,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { "cannot alloc memory for unsized type", ); let align = self.type_align_with_substs(ty, substs)?; - self.memory.allocate(size, align, MemoryKind::Stack) + self.memory.allocate(size, align, Some(MemoryKind::Stack)) } pub fn memory(&self) -> &Memory<'a, 'tcx, M> { @@ -219,7 +263,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { - let ptr = self.memory.allocate_cached(s.as_bytes())?; + let ptr = self.memory.allocate_cached(s.as_bytes()); Ok(Value::ByValPair( PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128), @@ -240,17 +284,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { - let ptr = self.memory.allocate_cached(bs.data)?; + let ptr = self.memory.allocate_cached(bs.data); PrimVal::Ptr(ptr) } Unevaluated(def_id, substs) => { - let instance = self.resolve_associated_const(def_id, substs); + let instance = self.resolve(def_id, substs)?; let cid = GlobalId { instance, promoted: None, }; - return Ok(Value::ByRef(*self.globals.get(&cid).expect("static/const not cached"))); + return Ok(Value::ByRef(self.tcx.interpret_interner.borrow().get_cached(cid).expect("static/const not cached"))); } Aggregate(..) | @@ -262,16 +306,29 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(Value::ByVal(primval)) } + pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { + let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + ty::Instance::resolve( + self.tcx, + M::param_env(self), + def_id, + substs, + ).ok_or(EvalErrorKind::TypeckError.into()) // turn error prop into a panic to expose associated type in const issue + } + pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - // generics are weird, don't run this function on a generic - assert!(!ty.needs_subst()); - ty.is_sized(self.tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP) + ty.is_sized(self.tcx, M::param_env(self), DUMMY_SP) } pub fn load_mir( &self, instance: ty::InstanceDef<'tcx>, ) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> { + // do not continue if typeck errors occurred (can only occur in local crate) + let did = instance.def_id(); + if did.is_local() && self.tcx.has_typeck_tables(did) && self.tcx.typeck_tables_of(did).tainted_by_errors { + return err!(TypeckError); + } trace!("load mir {:?}", instance); match instance { ty::InstanceDef::Item(def_id) => { @@ -288,7 +345,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // let's simply get rid of them let without_lifetimes = self.tcx.erase_regions(&ty); let substituted = without_lifetimes.subst(self.tcx, substs); - let substituted = self.tcx.normalize_associated_type(&substituted); + let substituted = self.tcx.fully_normalize_monormophic_ty(&substituted); substituted } @@ -299,9 +356,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { &mut self, ty: ty::Ty<'tcx>, value: Value, - ) -> EvalResult<'tcx, (u64, u64)> { - if let Some(size) = self.type_size(ty)? { - Ok((size as u64, self.type_align(ty)? as u64)) + ) -> EvalResult<'tcx, (Size, Align)> { + let layout = self.type_layout(ty)?; + if !layout.is_unsized() { + Ok(layout.size_and_align()) } else { match ty.sty { ty::TyAdt(..) | ty::TyTuple(..) => { @@ -310,26 +368,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // and it also rounds up to alignment, which we want to avoid, // as the unsized field's alignment could be smaller. assert!(!ty.is_simd()); - let layout = self.type_layout(ty)?; debug!("DST {} layout: {:?}", ty, layout); - let (sized_size, sized_align) = match *layout { - ty::layout::Layout::Univariant { ref variant, .. } => { - ( - variant.offsets.last().map_or(0, |o| o.bytes()), - variant.align, - ) - } - _ => { - bug!( - "size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", - ty, - layout - ); - } - }; + let sized_size = layout.fields.offset(layout.fields.count() - 1); + let sized_align = layout.align; debug!( - "DST {} statically sized prefix size: {} align: {:?}", + "DST {} statically sized prefix size: {:?} align: {:?}", ty, sized_size, sized_align @@ -345,7 +389,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } ty::TyTuple(ref types, _) => { let field_ty = types.last().unwrap(); - let field_ty = self.tcx.normalize_associated_type(field_ty); + let field_ty = self.tcx.fully_normalize_monormophic_ty(field_ty); self.size_and_align_of_dst(field_ty, value)? } _ => bug!("We already checked that we know this type"), @@ -363,8 +407,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). - let align = - sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap()); + let align = sized_align.max(unsized_align); // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. @@ -377,8 +420,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // // `(size + (align-1)) & -align` - let size = Size::from_bytes(size).abi_align(align).bytes(); - Ok((size, align.abi())) + Ok((size.abi_align(align), align)) } ty::TyDynamic(..) => { let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?; @@ -387,13 +429,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty)?.expect( - "slice element must be sized", - ) as u64; + let (elem_size, align) = layout.field(&self, 0)?.size_and_align(); let (_, len) = value.into_slice(&mut self.memory)?; - let align = self.type_align(elem_ty)?; - Ok((len * elem_size, align as u64)) + Ok((elem_size * len, align)) } _ => bug!("size_of_val::<{:?}>", ty), @@ -403,7 +441,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { /// Returns the normalized type of a struct field fn field_ty(&self, param_substs: &Substs<'tcx>, f: &ty::FieldDef) -> ty::Ty<'tcx> { - self.tcx.normalize_associated_type( + self.tcx.fully_normalize_monormophic_ty( &f.ty(self.tcx, param_substs), ) } @@ -416,7 +454,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.type_align_with_substs(ty, self.substs()) } - pub fn type_size_with_substs( + pub(super) fn type_size_with_substs( &self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>, @@ -425,34 +463,33 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { if layout.is_unsized() { Ok(None) } else { - Ok(Some(layout.size(&self.tcx.data_layout).bytes())) + Ok(Some(layout.size.bytes())) } } - pub fn type_align_with_substs( + pub(super) fn type_align_with_substs( &self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>, ) -> EvalResult<'tcx, u64> { self.type_layout_with_substs(ty, substs).map(|layout| { - layout.align(&self.tcx.data_layout).abi() + layout.align.abi() }) } - pub fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { + pub fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, TyLayout<'tcx>> { self.type_layout_with_substs(ty, self.substs()) } - fn type_layout_with_substs( + pub(super) fn type_layout_with_substs( &self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>, - ) -> EvalResult<'tcx, &'tcx Layout> { + ) -> EvalResult<'tcx, TyLayout<'tcx>> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)) - .map_err(|layout| EvalErrorKind::Layout(layout).into()) + self.layout_of(ty) } pub fn push_stack_frame( @@ -460,7 +497,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { instance: ty::Instance<'tcx>, span: codemap::Span, mir: &'tcx mir::Mir<'tcx>, - return_lvalue: Lvalue, + return_place: Place, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -504,7 +541,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { mir, block: mir::START_BLOCK, return_to_block, - return_lvalue, + return_place, locals, span, instance, @@ -532,14 +569,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } match frame.return_to_block { StackPopCleanup::MarkStatic(mutable) => { - if let Lvalue::Ptr { ptr, .. } = frame.return_lvalue { + if let Place::Ptr { ptr, .. } = frame.return_place { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions self.memory.mark_static_initalized( ptr.to_ptr()?.alloc_id, mutable, )? } else { - bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue); + bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_place); } } StackPopCleanup::Goto(target) => self.goto_block(target), @@ -558,81 +595,22 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { trace!("deallocating local"); let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); - match self.memory.get(ptr.alloc_id)?.kind { - // for a constant like `const FOO: &i32 = &1;` the local containing - // the `1` is referred to by the global. We transitively marked everything - // the global refers to as static itself, so we don't free it here - MemoryKind::Static => {} - MemoryKind::Stack => self.memory.deallocate(ptr, None, MemoryKind::Stack)?, - other => bug!("local contained non-stack memory: {:?}", other), - } + self.memory.deallocate_local(ptr)?; }; Ok(()) } - pub fn assign_discr_and_fields( - &mut self, - dest: Lvalue, - dest_ty: Ty<'tcx>, - discr_offset: u64, - operands: &[mir::Operand<'tcx>], - discr_val: u128, - variant_idx: usize, - discr_size: u64, - discr_signed: bool, - ) -> EvalResult<'tcx> { - // FIXME(solson) - let dest_ptr = self.force_allocation(dest)?.to_ptr()?; - - let discr_dest = dest_ptr.offset(discr_offset, &self)?; - self.memory.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr_size, discr_signed)?; - - let dest = Lvalue::Ptr { - ptr: PtrAndAlign { - ptr: dest_ptr.into(), - aligned: true, - }, - extra: LvalueExtra::DowncastVariant(variant_idx), - }; - - self.assign_fields(dest, dest_ty, operands) - } - - pub fn assign_fields( - &mut self, - dest: Lvalue, - dest_ty: Ty<'tcx>, - operands: &[mir::Operand<'tcx>], - ) -> EvalResult<'tcx> { - if self.type_size(dest_ty)? == Some(0) { - // zst assigning is a nop - return Ok(()); - } - if self.ty_to_primval_kind(dest_ty).is_ok() { - assert_eq!(operands.len(), 1); - let value = self.eval_operand(&operands[0])?; - return self.write_value(value, dest); - } - for (field_index, operand) in operands.iter().enumerate() { - let value = self.eval_operand(operand)?; - let field_dest = self.lvalue_field(dest, mir::Field::new(field_index), dest_ty, value.ty)?; - self.write_value(value, field_dest)?; - } - Ok(()) - } - /// Evaluate an assignment statement. /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue - /// type writes its results directly into the memory specified by the lvalue. - pub(super) fn eval_rvalue_into_lvalue( + /// type writes its results directly into the memory specified by the place. + pub(super) fn eval_rvalue_into_place( &mut self, rvalue: &mir::Rvalue<'tcx>, - lvalue: &mir::Lvalue<'tcx>, + place: &mir::Place<'tcx>, ) -> EvalResult<'tcx> { - let dest = self.eval_lvalue(lvalue)?; - let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty)?; + let dest = self.eval_place(place)?; + let dest_ty = self.place_ty(place); use mir::Rvalue::*; match *rvalue { @@ -687,133 +665,29 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { )?; } - // Skip everything for zsts - Aggregate(..) if self.type_size(dest_ty)? == Some(0) => {} - Aggregate(ref kind, ref operands) => { self.inc_step_counter_and_check_limit(operands.len() as u64)?; - use ty::layout::Layout::*; - match *dest_layout { - Univariant { ref variant, .. } => { - self.write_maybe_aligned_mut(!variant.packed, |ecx| { - ecx.assign_fields(dest, dest_ty, operands) - })?; - } - - Array { .. } => { - self.assign_fields(dest, dest_ty, operands)?; - } - - General { - discr, - ref variants, - .. - } => { - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { - let discr_val = adt_def - .discriminants(self.tcx) - .nth(variant) - .expect("broken mir: Adt variant id invalid") - .to_u128_unchecked(); - let discr_size = discr.size().bytes(); - - self.assign_discr_and_fields( - dest, - dest_ty, - variants[variant].offsets[0].bytes(), - operands, - discr_val, - variant, - discr_size, - false, - )?; - } else { - bug!("tried to assign {:?} to Layout::General", kind); - } - } - - RawNullablePointer { nndiscr, .. } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { - if nndiscr == variant as u64 { - assert_eq!(operands.len(), 1); - let operand = &operands[0]; - let value = self.eval_operand(operand)?; - self.write_value(value, dest)?; - } else { - if let Some(operand) = operands.get(0) { - assert_eq!(operands.len(), 1); - let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty)?, Some(0)); - } - self.write_null(dest, dest_ty)?; - } - } else { - bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); - } - } - - StructWrappedNullablePointer { - nndiscr, - ref discrfield_source, - ref nonnull, - .. - } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { - if nndiscr == variant as u64 { - self.write_maybe_aligned_mut(!nonnull.packed, |ecx| { - ecx.assign_fields(dest, dest_ty, operands) - })?; - } else { - for operand in operands { - let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty)?, Some(0)); - } - self.write_struct_wrapped_null_pointer( - dest_ty, - nndiscr, - discrfield_source, - dest, - )?; - } - } else { - bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); - } - } - CEnum { .. } => { - assert_eq!(operands.len(), 0); - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { - let n = adt_def - .discriminants(self.tcx) - .nth(variant) - .expect("broken mir: Adt variant index invalid") - .to_u128_unchecked(); - self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; + let (dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + self.write_discriminant_value(dest_ty, dest, variant_index)?; + if adt_def.is_enum() { + (self.place_downcast(dest, variant_index)?, active_field_index) } else { - bug!("tried to assign {:?} to Layout::CEnum", kind); + (dest, active_field_index) } } + _ => (dest, None) + }; - Vector { count, .. } => { - debug_assert_eq!(count, operands.len() as u64); - self.assign_fields(dest, dest_ty, operands)?; - } - - UntaggedUnion { ref variants } => { - assert_eq!(operands.len(), 1); - let operand = &operands[0]; - let value = self.eval_operand(operand)?; - self.write_maybe_aligned_mut(!variants.packed, |ecx| { - ecx.write_value(value, dest) - })?; - } - - _ => { - return err!(Unimplemented(format!( - "can't handle destination layout {:?} when assigning {:?}", - dest_layout, - kind - ))); + let layout = self.type_layout(dest_ty)?; + for (i, operand) in operands.iter().enumerate() { + let value = self.eval_operand(operand)?; + // Ignore zero-sized fields. + if !self.type_layout(value.ty)?.is_zst() { + let field_index = active_field_index.unwrap_or(i); + let (field_dest, _) = self.place_field(dest, mir::Field::new(field_index), layout)?; + self.write_value(value, field_dest)?; } } } @@ -828,25 +702,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ) } }; - self.inc_step_counter_and_check_limit(length)?; let elem_size = self.type_size(elem_ty)?.expect( "repeat element type must be sized", ); let value = self.eval_operand(operand)?.value; - // FIXME(solson) let dest = Pointer::from(self.force_allocation(dest)?.to_ptr()?); + // FIXME: speed up repeat filling for i in 0..length { let elem_dest = dest.offset(i * elem_size, &self)?; self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } - Len(ref lvalue) => { + Len(ref place) => { // FIXME(CTFE): don't allow computing the length of arrays in const eval - let src = self.eval_lvalue(lvalue)?; - let ty = self.lvalue_ty(lvalue); + let src = self.eval_place(place)?; + let ty = self.place_ty(place); let (_, len) = src.elem_ty_and_len(ty); self.write_primval( dest, @@ -855,18 +728,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { )?; } - Ref(_, _, ref lvalue) => { - let src = self.eval_lvalue(lvalue)?; - // We ignore the alignment of the lvalue here -- special handling for packed structs ends + Ref(_, _, ref place) => { + let src = self.eval_place(place)?; + // We ignore the alignment of the place here -- special handling for packed structs ends // at the `&` operator. let (ptr, extra) = self.force_allocation(src)?.to_ptr_extra_aligned(); let val = match extra { - LvalueExtra::None => ptr.ptr.to_value(), - LvalueExtra::Length(len) => ptr.ptr.to_value_with_len(len), - LvalueExtra::Vtable(vtable) => ptr.ptr.to_value_with_vtable(vtable), - LvalueExtra::DowncastVariant(..) => { - bug!("attempted to take a reference to an enum downcast lvalue") + PlaceExtra::None => ptr.ptr.to_value(), + PlaceExtra::Length(len) => ptr.ptr.to_value_with_len(len), + PlaceExtra::Vtable(vtable) => ptr.ptr.to_value_with_vtable(vtable), + PlaceExtra::DowncastVariant(..) => { + bug!("attempted to take a reference to an enum downcast place") } }; let valty = ValTy { @@ -935,7 +808,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ReifyFnPointer => { match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs) => { - let instance = resolve(self.tcx, def_id, substs); + let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance); let valty = ValTy { value: Value::ByVal(PrimVal::Ptr(fn_ptr)), @@ -961,7 +834,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ClosureFnPointer => { match self.operand_ty(operand).sty { ty::TyClosure(def_id, substs) => { - let instance = resolve_closure( + let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + let instance = ty::Instance::resolve_closure( self.tcx, def_id, substs, @@ -980,11 +854,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - Discriminant(ref lvalue) => { - let lval = self.eval_lvalue(lvalue)?; - let ty = self.lvalue_ty(lvalue); - let ptr = self.force_allocation(lval)?.to_ptr()?; - let discr_val = self.read_discriminant_value(ptr, ty)?; + Discriminant(ref place) => { + let ty = self.place_ty(place); + let place = self.eval_place(place)?; + let discr_val = self.read_discriminant_value(place, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::>()); if adt_def.discriminants(self.tcx).all(|v| { @@ -1007,33 +880,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub(crate) fn write_struct_wrapped_null_pointer( - &mut self, - dest_ty: ty::Ty<'tcx>, - nndiscr: u64, - discrfield_source: &layout::FieldPath, - dest: Lvalue, - ) -> EvalResult<'tcx> { - let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty( - dest_ty, - nndiscr, - discrfield_source, - )?; - let nonnull = self.force_allocation(dest)?.to_ptr()?.offset( - offset.bytes(), - &self, - )?; - trace!("struct wrapped nullable pointer type: {}", ty); - // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty)?.expect( - "bad StructWrappedNullablePointer discrfield", - ); - self.memory.write_maybe_aligned_mut(!packed, |mem| { - // We're writing 0, signedness does not matter - mem.write_primval(nonnull, PrimVal::Bytes(0), discr_size, false) - }) - } - pub(super) fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ref tam) | @@ -1043,220 +889,25 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - pub(super) fn nonnull_offset_and_ty( - &self, - ty: Ty<'tcx>, - nndiscr: u64, - discrfield: &[u32], - ) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> { - // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant - let path = discrfield.iter().skip(2).map(|&i| i as usize); - - // Handle the field index for the outer non-null variant. - let (inner_offset, inner_ty) = match ty.sty { - ty::TyAdt(adt_def, substs) => { - let variant = &adt_def.variants[nndiscr as usize]; - let index = discrfield[1]; - let field = &variant.fields[index as usize]; - ( - self.get_field_offset(ty, index as usize)?, - field.ty(self.tcx, substs), - ) - } - _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), - }; - - self.field_path_offset_and_ty(inner_offset, inner_ty, path) - } - - fn field_path_offset_and_ty>( - &self, - mut offset: Size, - mut ty: Ty<'tcx>, - path: I, - ) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> { - // Skip the initial 0 intended for LLVM GEP. - let mut packed = false; - for field_index in path { - let field_offset = self.get_field_offset(ty, field_index)?; - trace!( - "field_path_offset_and_ty: {}, {}, {:?}, {:?}", - field_index, - ty, - field_offset, - offset - ); - let field_ty = self.get_field_ty(ty, field_index)?; - ty = field_ty.ty; - packed = packed || field_ty.packed; - offset = offset - .checked_add(field_offset, &self.tcx.data_layout) - .unwrap(); - } - - Ok((offset, TyAndPacked { ty, packed })) - } - fn get_fat_field( - &self, - pointee_ty: Ty<'tcx>, - field_index: usize, - ) -> EvalResult<'tcx, Ty<'tcx>> { - match (field_index, &self.tcx.struct_tail(pointee_ty).sty) { - (1, &ty::TyStr) | - (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyDynamic(..)) | - (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), - _ => bug!("invalid fat pointee type: {}", pointee_ty), - } - } - /// Returns the field type and whether the field is packed pub fn get_field_ty( &self, ty: Ty<'tcx>, field_index: usize, ) -> EvalResult<'tcx, TyAndPacked<'tcx>> { - match ty.sty { - ty::TyAdt(adt_def, _) if adt_def.is_box() => Ok(TyAndPacked { - ty: self.get_fat_field(ty.boxed_ty(), field_index)?, - packed: false, - }), - ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { - use ty::layout::Layout::*; - match *self.type_layout(ty)? { - RawNullablePointer { nndiscr, .. } => Ok(TyAndPacked { - ty: adt_def.variants[nndiscr as usize].fields[field_index].ty( - self.tcx, - substs, - ), - packed: false, - }), - StructWrappedNullablePointer { - nndiscr, - ref nonnull, - .. - } => { - let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty( - self.tcx, - substs, - ); - Ok(TyAndPacked { - ty, - packed: nonnull.packed, - }) - } - // mir optimizations treat single variant enums as structs - General { .. } if adt_def.variants.len() == 1 => Ok(TyAndPacked { - ty: adt_def.variants[0].fields[field_index].ty(self.tcx, substs), - packed: false, - }), - _ => { - err!(Unimplemented(format!( - "get_field_ty can't handle enum type: {:?}, {:?}", - ty, - ty.sty - ))) - } - } - } - ty::TyAdt(adt_def, substs) => { - let variant_def = adt_def.struct_variant(); - use ty::layout::Layout::*; - match *self.type_layout(ty)? { - UntaggedUnion { ref variants } => Ok(TyAndPacked { - ty: variant_def.fields[field_index].ty(self.tcx, substs), - packed: variants.packed, - }), - Univariant { ref variant, .. } => Ok(TyAndPacked { - ty: variant_def.fields[field_index].ty(self.tcx, substs), - packed: variant.packed, - }), - _ => { - err!(Unimplemented(format!( - "get_field_ty can't handle struct type: {:?}, {:?}", - ty, - ty.sty - ))) - } - } - } - - ty::TyTuple(fields, _) => Ok(TyAndPacked { - ty: fields[field_index], - packed: false, - }), - - ty::TyRef(_, ref tam) | - ty::TyRawPtr(ref tam) => Ok(TyAndPacked { - ty: self.get_fat_field(tam.ty, field_index)?, - packed: false, - }), - - ty::TyArray(ref inner, _) => Ok(TyAndPacked { - ty: inner, - packed: false, - }), - - ty::TyClosure(def_id, ref closure_substs) => Ok(TyAndPacked { - ty: closure_substs.upvar_tys(def_id, self.tcx).nth(field_index).unwrap(), - packed: false, - }), - - _ => { - err!(Unimplemented( - format!("can't handle type: {:?}, {:?}", ty, ty.sty), - )) - } - } + let layout = self.type_layout(ty)?.field(self, field_index)?; + Ok(TyAndPacked { + ty: layout.ty, + packed: layout.is_packed() + }) } - fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { - // Also see lvalue_field in lvalue.rs, which handles more cases but needs an actual value at the given type - let layout = self.type_layout(ty)?; - - use ty::layout::Layout::*; - match *layout { - Univariant { ref variant, .. } => Ok(variant.offsets[field_index]), - FatPointer { .. } => { - let bytes = field_index as u64 * self.memory.pointer_size(); - Ok(Size::from_bytes(bytes)) - } - StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets[field_index]), - UntaggedUnion { .. } => Ok(Size::from_bytes(0)), - // mir optimizations treat single variant enums as structs - General { ref variants, .. } if variants.len() == 1 => Ok(variants[0].offsets[field_index]), - _ => { - let msg = format!( - "get_field_offset: can't handle type: {:?}, with layout: {:?}", - ty, - layout - ); - err!(Unimplemented(msg)) - } - } + pub fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { + Ok(self.type_layout(ty)?.fields.offset(field_index)) } pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { - let layout = self.type_layout(ty)?; - - use ty::layout::Layout::*; - match *layout { - Univariant { ref variant, .. } => Ok(variant.offsets.len() as u64), - FatPointer { .. } => Ok(2), - StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len() as u64), - Vector { count, .. } | - Array { count, .. } => Ok(count), - Scalar { .. } => Ok(0), - UntaggedUnion { .. } => Ok(1), - _ => { - let msg = format!( - "get_field_count: can't handle type: {:?}, with layout: {:?}", - ty, - layout - ); - err!(Unimplemented(msg)) - } - } + Ok(self.type_layout(ty)?.fields.count() as u64) } pub(super) fn eval_operand_to_primval( @@ -1279,9 +930,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> { use mir::Operand::*; match *op { - Consume(ref lvalue) => { + // FIXME: do some more logic on `move` to invalidate the old location + Copy(ref place) | + Move(ref place) => { Ok(ValTy { - value: self.eval_and_read_lvalue(lvalue)?, + value: self.eval_and_read_place(place)?, ty: self.operand_ty(op), }) }, @@ -1297,7 +950,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { instance: self.frame().instance, promoted: Some(index), }; - Value::ByRef(*self.globals.get(&cid).expect("promoted not cached")) + Value::ByRef(self.tcx.interpret_interner.borrow().get_cached(cid).expect("promoted not cached")) } }; @@ -1310,97 +963,107 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } pub fn read_discriminant_value( - &self, - adt_ptr: MemoryPointer, - adt_ty: Ty<'tcx>, + &mut self, + place: Place, + ty: Ty<'tcx>, ) -> EvalResult<'tcx, u128> { - use ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty)?; - //trace!("read_discriminant_value {:#?}", adt_layout); - - let discr_val = match *adt_layout { - General { discr, .. } => { - let discr_size = discr.size().bytes(); - self.memory.read_primval(adt_ptr, discr_size, false)?.to_bytes()? + let layout = self.type_layout(ty)?; + //trace!("read_discriminant_value {:#?}", layout); + + match layout.variants { + layout::Variants::Single { index } => { + return Ok(index as u128); } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } - CEnum { - discr, - signed, + let (discr_place, discr) = self.place_field(place, mir::Field::new(0), layout)?; + let raw_discr = self.value_to_primval(ValTy { + value: self.read_place(discr_place)?, + ty: discr.ty + })?; + let discr_val = match layout.variants { + layout::Variants::Single { .. } => bug!(), + layout::Variants::Tagged { .. } => raw_discr.to_bytes()?, + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, .. } => { - let discr_size = discr.size().bytes(); - self.memory.read_primval(adt_ptr, discr_size, signed)?.to_bytes()? + let variants_start = niche_variants.start as u128; + let variants_end = niche_variants.end as u128; + match raw_discr { + PrimVal::Ptr(_) => { + assert!(niche_start == 0); + assert!(variants_start == variants_end); + dataful_variant as u128 + }, + PrimVal::Bytes(raw_discr) => { + let discr = raw_discr.wrapping_sub(niche_start) + .wrapping_add(variants_start); + if variants_start <= discr && discr <= variants_end { + discr + } else { + dataful_variant as u128 + } + }, + PrimVal::Undef => return err!(ReadUndefBytes), + } } + }; - RawNullablePointer { nndiscr, value } => { - let discr_size = value.size(&self.tcx.data_layout).bytes(); - trace!("rawnullablepointer with size {}", discr_size); - self.read_nonnull_discriminant_value( - adt_ptr, - nndiscr as u128, - discr_size, - )? + Ok(discr_val) + } + + + pub(crate) fn write_discriminant_value( + &mut self, + dest_ty: Ty<'tcx>, + dest: Place, + variant_index: usize, + ) -> EvalResult<'tcx> { + let layout = self.type_layout(dest_ty)?; + + match layout.variants { + layout::Variants::Single { index } => { + if index != variant_index { + // If the layout of an enum is `Single`, all + // other variants are necessarily uninhabited. + assert_eq!(layout.for_variant(&self, variant_index).abi, + layout::Abi::Uninhabited); + } } + layout::Variants::Tagged { .. } => { + let discr_val = dest_ty.ty_adt_def().unwrap() + .discriminant_for_variant(self.tcx, variant_index) + .to_u128_unchecked(); - StructWrappedNullablePointer { - nndiscr, - ref discrfield_source, + let (discr_dest, discr) = self.place_field(dest, mir::Field::new(0), layout)?; + self.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr.ty)?; + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, .. } => { - let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty( - adt_ty, - nndiscr, - discrfield_source, - )?; - let nonnull = adt_ptr.offset(offset.bytes(), &*self)?; - trace!("struct wrapped nullable pointer type: {}", ty); - // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty)?.expect( - "bad StructWrappedNullablePointer discrfield", - ); - self.read_maybe_aligned(!packed, |ectx| { - ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size) - })? + if variant_index != dataful_variant { + let (niche_dest, niche) = + self.place_field(dest, mir::Field::new(0), layout)?; + let niche_value = ((variant_index - niche_variants.start) as u128) + .wrapping_add(niche_start); + self.write_primval(niche_dest, PrimVal::Bytes(niche_value), niche.ty)?; + } } + } - // The discriminant_value intrinsic returns 0 for non-sum types. - Array { .. } | - FatPointer { .. } | - Scalar { .. } | - Univariant { .. } | - Vector { .. } | - UntaggedUnion { .. } => 0, - }; - - Ok(discr_val) - } - - fn read_nonnull_discriminant_value( - &self, - ptr: MemoryPointer, - nndiscr: u128, - discr_size: u64, - ) -> EvalResult<'tcx, u128> { - trace!( - "read_nonnull_discriminant_value: {:?}, {}, {}", - ptr, - nndiscr, - discr_size - ); - // We are only interested in 0 vs. non-0, the sign does not matter for this - let null = match self.memory.read_primval(ptr, discr_size, false)? { - PrimVal::Bytes(0) => true, - PrimVal::Bytes(_) | - PrimVal::Ptr(..) => false, - PrimVal::Undef => return err!(ReadUndefBytes), - }; - assert!(nndiscr == 0 || nndiscr == 1); - Ok(if !null { nndiscr } else { 1 - nndiscr }) + Ok(()) } pub fn read_global_as_value(&self, gid: GlobalId) -> Value { - Value::ByRef(*self.globals.get(&gid).expect("global not cached")) + Value::ByRef(self.tcx.interpret_interner.borrow().get_cached(gid).expect("global not cached")) } pub fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { @@ -1416,31 +1079,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } - pub fn is_packed(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, bool> { - let layout = self.type_layout(ty)?; - use ty::layout::Layout::*; - Ok(match *layout { - Univariant { ref variant, .. } => variant.packed, - - StructWrappedNullablePointer { ref nonnull, .. } => nonnull.packed, - - UntaggedUnion { ref variants } => variants.packed, - - // can only apply #[repr(packed)] to struct and union - _ => false, - }) - } - - pub fn force_allocation(&mut self, lvalue: Lvalue) -> EvalResult<'tcx, Lvalue> { - let new_lvalue = match lvalue { - Lvalue::Local { frame, local } => { + pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> { + let new_place = match place { + Place::Local { frame, local } => { // -1 since we don't store the return value match self.stack[frame].locals[local.index() - 1] { None => return err!(DeadLocal), Some(Value::ByRef(ptr)) => { - Lvalue::Ptr { + Place::Ptr { ptr, - extra: LvalueExtra::None, + extra: PlaceExtra::None, } } Some(val) => { @@ -1451,13 +1099,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.stack[frame].locals[local.index() - 1] = Some(Value::by_ref(ptr.into())); // it stays live self.write_value_to_ptr(val, ptr.into(), ty)?; - Lvalue::from_ptr(ptr) + Place::from_ptr(ptr) } } } - Lvalue::Ptr { .. } => lvalue, + Place::Ptr { .. } => place, }; - Ok(new_lvalue) + Ok(new_place) } /// ensures this Value is not a ByRef @@ -1491,11 +1139,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - pub fn write_null(&mut self, dest: Lvalue, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty) - } - - pub fn write_ptr(&mut self, dest: Lvalue, val: Pointer, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> { + pub fn write_ptr(&mut self, dest: Place, val: Pointer, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> { let valty = ValTy { value: val.to_value(), ty: dest_ty, @@ -1505,7 +1149,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn write_primval( &mut self, - dest: Lvalue, + dest: Place, val: PrimVal, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { @@ -1519,7 +1163,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn write_value( &mut self, ValTy { value: src_val, ty: dest_ty } : ValTy<'tcx>, - dest: Lvalue, + dest: Place, ) -> EvalResult<'tcx> { //trace!("Writing {:?} to {:?} at type {:?}", src_val, dest, dest_ty); // Note that it is really important that the type here is the right one, and matches the type things are read at. @@ -1527,18 +1171,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // correct if we never look at this data with the wrong type. match dest { - Lvalue::Ptr { + Place::Ptr { ptr: PtrAndAlign { ptr, aligned }, extra, } => { - assert_eq!(extra, LvalueExtra::None); + assert_eq!(extra, PlaceExtra::None); self.write_maybe_aligned_mut( aligned, |ectx| ectx.write_value_to_ptr(src_val, ptr, dest_ty), ) } - Lvalue::Local { frame, local } => { + Place::Local { frame, local } => { let dest = self.stack[frame].get_local(local)?; self.write_value_possibly_by_val( src_val, @@ -1615,64 +1259,61 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { dest: Pointer, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { + trace!("write_value_to_ptr: {:#?}", value); match value { Value::ByRef(PtrAndAlign { ptr, aligned }) => { self.read_maybe_aligned_mut(aligned, |ectx| ectx.copy(ptr, dest, dest_ty)) } Value::ByVal(primval) => { - let size = self.type_size(dest_ty)?.expect("dest type must be sized"); - if size == 0 { + let layout = self.type_layout(dest_ty)?; + if layout.is_zst() { assert!(primval.is_undef()); Ok(()) } else { // TODO: Do we need signedness? - self.memory.write_primval(dest.to_ptr()?, primval, size, false) + self.memory.write_maybe_aligned_mut(!layout.is_packed(), |mem| { + mem.write_primval(dest.to_ptr()?, primval, layout.size.bytes(), false) + }) } } - Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest.to_ptr()?, dest_ty), - } - } - - pub fn write_pair_to_ptr( - &mut self, - a: PrimVal, - b: PrimVal, - ptr: MemoryPointer, - mut ty: Ty<'tcx>, - ) -> EvalResult<'tcx> { - let mut packed = false; - while self.get_field_count(ty)? == 1 { - let field = self.get_field_ty(ty, 0)?; - ty = field.ty; - packed = packed || field.packed; + Value::ByValPair(a, b) => { + let ptr = dest.to_ptr()?; + let mut layout = self.type_layout(dest_ty)?; + trace!("write_value_to_ptr valpair: {:#?}", layout); + let mut packed = layout.is_packed(); + 'outer: loop { + for i in 0..layout.fields.count() { + let field = layout.field(&self, i)?; + if layout.fields.offset(i).bytes() == 0 && layout.size == field.size { + layout = field; + packed |= layout.is_packed(); + continue 'outer; + } + } + break; + } + trace!("write_value_to_ptr valpair: {:#?}", layout); + assert_eq!(layout.fields.count(), 2); + let field_0 = layout.field(&self, 0)?; + let field_1 = layout.field(&self, 1)?; + trace!("write_value_to_ptr field 0: {:#?}", field_0); + trace!("write_value_to_ptr field 1: {:#?}", field_1); + assert_eq!( + field_0.is_packed(), + field_1.is_packed(), + "the two fields must agree on being packed" + ); + packed |= field_0.is_packed(); + let field_0_ptr = ptr.offset(layout.fields.offset(0).bytes(), &self)?.into(); + let field_1_ptr = ptr.offset(layout.fields.offset(1).bytes(), &self)?.into(); + // TODO: What about signedess? + self.memory.write_maybe_aligned_mut(!packed, |mem| { + mem.write_primval(field_0_ptr, a, field_0.size.bytes(), false)?; + mem.write_primval(field_1_ptr, b, field_1.size.bytes(), false) + })?; + Ok(()) + } } - assert_eq!(self.get_field_count(ty)?, 2); - let field_0 = self.get_field_offset(ty, 0)?; - let field_1 = self.get_field_offset(ty, 1)?; - let field_0_ty = self.get_field_ty(ty, 0)?; - let field_1_ty = self.get_field_ty(ty, 1)?; - assert_eq!( - field_0_ty.packed, - field_1_ty.packed, - "the two fields must agree on being packed" - ); - packed = packed || field_0_ty.packed; - let field_0_size = self.type_size(field_0_ty.ty)?.expect( - "pair element type must be sized", - ); - let field_1_size = self.type_size(field_1_ty.ty)?.expect( - "pair element type must be sized", - ); - let field_0_ptr = ptr.offset(field_0.bytes(), &self)?.into(); - let field_1_ptr = ptr.offset(field_1.bytes(), &self)?.into(); - // TODO: What about signedess? - self.write_maybe_aligned_mut(!packed, |ectx| { - ectx.memory.write_primval(field_0_ptr, a, field_0_size, false) - })?; - self.write_maybe_aligned_mut(!packed, |ectx| { - ectx.memory.write_primval(field_1_ptr, b, field_1_size, false) - })?; - Ok(()) } pub fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { @@ -1718,41 +1359,19 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ty::TyAdt(def, _) if def.is_box() => PrimValKind::Ptr, - ty::TyAdt(def, substs) => { - use ty::layout::Layout::*; - match *self.type_layout(ty)? { - CEnum { discr, signed, .. } => { - let size = discr.size().bytes(); - if signed { - PrimValKind::from_int_size(size) - } else { - PrimValKind::from_uint_size(size) - } - } - - RawNullablePointer { value, .. } => { + ty::TyAdt(..) => { + match self.type_layout(ty)?.abi { + layout::Abi::Scalar(ref scalar) => { use ty::layout::Primitive::*; - match value { - // TODO(solson): Does signedness matter here? What should the sign be? - Int(int) => PrimValKind::from_uint_size(int.size().bytes()), + match scalar.value { + Int(i, false) => PrimValKind::from_uint_size(i.size().bytes()), + Int(i, true) => PrimValKind::from_int_size(i.size().bytes()), F32 => PrimValKind::F32, F64 => PrimValKind::F64, Pointer => PrimValKind::Ptr, } } - // represent single field structs as their single field - Univariant { .. } => { - // enums with just one variant are no different, but `.struct_variant()` doesn't work for enums - let variant = &def.variants[0]; - // FIXME: also allow structs with only a single non zst field - if variant.fields.len() == 1 { - return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs)); - } else { - return err!(TypeNotPrimitive(ty)); - } - } - _ => return err!(TypeNotPrimitive(ty)), } } @@ -1807,7 +1426,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - fn try_read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + pub fn try_read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; let ptr = ptr.to_ptr()?; @@ -1867,9 +1486,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { if def.is_box() { return self.read_ptr(ptr, ty.boxed_ty()).map(Some); } - use ty::layout::Layout::*; - if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { - let size = discr.size().bytes(); + + if let layout::Abi::Scalar(ref scalar) = self.type_layout(ty)?.abi { + let mut signed = false; + if let layout::Int(_, s) = scalar.value { + signed = s; + } + let size = scalar.value.size(self).bytes(); self.memory.read_primval(ptr, size, signed)? } else { return Ok(None); @@ -1906,7 +1529,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { &mut self, src: Value, src_ty: Ty<'tcx>, - dest: Lvalue, + dest: Place, dest_ty: Ty<'tcx>, sty: Ty<'tcx>, dty: Ty<'tcx>, @@ -1957,7 +1580,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { &mut self, src: Value, src_ty: Ty<'tcx>, - dest: Lvalue, + dest: Place, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match (&src_ty.sty, &dest_ty.sty) { @@ -1994,18 +1617,42 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let src_fields = def_a.variants[0].fields.iter(); let dst_fields = def_b.variants[0].fields.iter(); + let iter = src_fields.zip(dst_fields).enumerate(); //let src = adt::MaybeSizedValue::sized(src); //let dst = adt::MaybeSizedValue::sized(dst); + let src_ptr = match src { Value::ByRef(PtrAndAlign { ptr, aligned: true }) => ptr, + // the entire struct is just a pointer + Value::ByVal(_) => { + for (i, (src_f, dst_f)) in iter { + let src_fty = self.field_ty(substs_a, src_f); + let dst_fty = self.field_ty(substs_b, dst_f); + if self.type_size(dst_fty)? == Some(0) { + continue; + } + let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); + let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); + assert_eq!(src_field_offset, 0); + assert_eq!(dst_field_offset, 0); + assert_eq!(self.type_size(src_fty)?, self.type_size(src_ty)?); + assert_eq!(self.type_size(dst_fty)?, self.type_size(dest_ty)?); + return self.unsize_into( + src, + src_fty, + dest, + dst_fty, + ); + } + bug!("by val unsize into where the value doesn't cover the entire type") + } // TODO: Is it possible for unaligned pointers to occur here? _ => bug!("expected aligned pointer, got {:?}", src), }; // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr()?; - let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { let src_fty = self.field_ty(substs_a, src_f); let dst_fty = self.field_ty(substs_b, dst_f); @@ -2022,7 +1669,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.unsize_into( Value::by_ref(src_f_ptr), src_fty, - Lvalue::from_ptr(dst_f_ptr), + Place::from_ptr(dst_f_ptr), dst_fty, )?; } @@ -2039,10 +1686,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - pub fn dump_local(&self, lvalue: Lvalue) { + pub fn dump_local(&self, place: Place) { // Debug output - match lvalue { - Lvalue::Local { frame, local } => { + match place { + Place::Local { frame, local } => { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); if frame != self.cur_frame() { @@ -2087,7 +1734,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { trace!("{}", msg); self.memory.dump_allocs(allocs); } - Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned }, .. } => { + Place::Ptr { ptr: PtrAndAlign { ptr, aligned }, .. } => { match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { trace!("by {}ref:", if aligned { "" } else { "unaligned " }); @@ -2117,28 +1764,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn report(&self, e: &mut EvalError) { if let Some(ref mut backtrace) = e.backtrace { let mut trace_text = "\n\nAn error occurred in miri:\n".to_string(); - let mut skip_init = true; backtrace.resolve(); + write!(trace_text, "backtrace frames: {}\n", backtrace.frames().len()).unwrap(); 'frames: for (i, frame) in backtrace.frames().iter().enumerate() { - for symbol in frame.symbols() { - if let Some(name) = symbol.name() { - // unmangle the symbol via `to_string` - let name = name.to_string(); - if name.starts_with("miri::after_analysis") { - // don't report initialization gibberish - break 'frames; - } else if name.starts_with("backtrace::capture::Backtrace::new") - // debug mode produces funky symbol names - || name.starts_with("backtrace::capture::{{impl}}::new") - { - // don't report backtrace internals - skip_init = false; - continue 'frames; - } - } - } - if skip_init { - continue; + if frame.symbols().is_empty() { + write!(trace_text, "{}: no symbols\n", i).unwrap(); } for symbol in frame.symbols() { write!(trace_text, "{}: ", i).unwrap(); @@ -2223,316 +1853,11 @@ impl<'tcx> Frame<'tcx> { // TODO(solson): Upstream these methods into rustc::ty::layout. -pub(super) trait IntegerExt { - fn size(self) -> Size; -} - -impl IntegerExt for layout::Integer { - fn size(self) -> Size { - use ty::layout::Integer::*; - match self { - I1 | I8 => Size::from_bits(8), - I16 => Size::from_bits(16), - I32 => Size::from_bits(32), - I64 => Size::from_bits(64), - I128 => Size::from_bits(128), - } - } -} - -/// FIXME: expose trans::monomorphize::resolve_closure -pub fn resolve_closure<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - requested_kind: ty::ClosureKind, -) -> ty::Instance<'tcx> { - let actual_kind = tcx.closure_kind(def_id); - match needs_fn_once_adapter_shim(actual_kind, requested_kind) { - Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), - _ => ty::Instance::new(def_id, substs.substs), - } -} - -fn fn_once_adapter_instance<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - closure_did: DefId, - substs: ty::ClosureSubsts<'tcx>, -) -> ty::Instance<'tcx> { - debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); - let call_once = tcx.associated_items(fn_once) - .find(|it| it.kind == ty::AssociatedKind::Method) - .unwrap() - .def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once }; - - let self_ty = tcx.mk_closure_from_closure_substs(closure_did, substs); - - let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); - assert_eq!(sig.inputs().len(), 1); - let substs = tcx.mk_substs( - [Kind::from(self_ty), Kind::from(sig.inputs()[0])] - .iter() - .cloned(), - ); - - debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); - ty::Instance { def, substs } -} - -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. - 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. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - Ok(true) - } - _ => Err(()), - } -} - -/// The point where linking happens. Resolve a (def_id, substs) -/// pair to an instance. -pub fn resolve<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, -) -> ty::Instance<'tcx> { - debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { - debug!(" => associated item, attempting to find impl"); - let item = tcx.associated_item(def_id); - resolve_associated_item(tcx, &item, trait_def_id, substs) - } else { - let item_type = def_ty(tcx, def_id, substs); - let def = match item_type.sty { - ty::TyFnDef(..) - if { - let f = item_type.fn_sig(tcx); - f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic - } => { - debug!(" => intrinsic"); - ty::InstanceDef::Intrinsic(def_id) - } - _ => { - if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - let ty = substs.type_at(0); - if needs_drop_glue(tcx, ty) { - debug!(" => nontrivial drop glue"); - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) - } - } else { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) - } - } - }; - ty::Instance { def, substs } - }; - debug!( - "resolve(def_id={:?}, substs={:?}) = {}", - def_id, - substs, - result - ); - result -} - -pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bool { - assert!(t.is_normalized_for_trans()); - - let t = tcx.erase_regions(&t); - - // FIXME (#22815): note that type_needs_drop conservatively - // approximates in some cases and may say a type expression - // requires drop glue when it actually does not. - // - // (In this case it is not clear whether any harm is done, i.e. - // erroneously returning `true` in some cases where we could have - // returned `false` does not appear unsound. The impact on - // code quality is unknown at this time.) - - let env = ty::ParamEnv::empty(Reveal::All); - if !t.needs_drop(tcx, env) { - return false; - } - match t.sty { - ty::TyAdt(def, _) if def.is_box() => { - let typ = t.boxed_ty(); - if !typ.needs_drop(tcx, env) && type_is_sized(tcx, typ) { - let layout = t.layout(tcx, ty::ParamEnv::empty(Reveal::All)).unwrap(); - // `Box` does not allocate. - layout.size(&tcx.data_layout).bytes() != 0 - } else { - true - } - } - _ => true, - } -} - -fn resolve_associated_item<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - trait_item: &ty::AssociatedItem, - trait_id: DefId, - rcvr_substs: &'tcx Substs<'tcx>, -) -> ty::Instance<'tcx> { - let def_id = trait_item.def_id; - debug!( - "resolve_associated_item(trait_item={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, - trait_id, - rcvr_substs - ); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.trans_fulfill_obligation(DUMMY_SP, ty::Binder(trait_ref)); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - ::traits::VtableImpl(impl_data) => { - let (def_id, substs) = - ::traits::find_associated_item(tcx, trait_item, rcvr_substs, &impl_data); - let substs = tcx.erase_regions(&substs); - ty::Instance::new(def_id, substs) - } - ::traits::VtableGenerator(closure_data) => { - ty::Instance { - def: ty::InstanceDef::Item(closure_data.closure_def_id), - substs: closure_data.substs.substs - } - } - ::traits::VtableClosure(closure_data) => { - let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap(); - resolve_closure( - tcx, - closure_data.closure_def_id, - closure_data.substs, - trait_closure_kind, - ) - } - ::traits::VtableFnPointer(ref data) => { - ty::Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs, - } - } - ::traits::VtableObject(ref data) => { - let index = tcx.get_vtable_index_of_object_method(data, def_id); - ty::Instance { - def: ty::InstanceDef::Virtual(def_id, index), - substs: rcvr_substs, - } - } - ::traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items().clone_trait() => { - ty::Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), - substs: rcvr_substs - } - } - _ => bug!("static call to invalid vtable: {:?}", vtbl), - } -} - -pub fn def_ty<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, -) -> Ty<'tcx> { - let ty = tcx.type_of(def_id); - apply_param_substs(tcx, substs, &ty) -} - -/// Monomorphizes a type from the AST by first applying the in-scope -/// substitutions and then normalizing any associated types. -pub fn apply_param_substs<'a, 'tcx, T>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_substs: &Substs<'tcx>, - value: &T, -) -> T -where - T: ::infer::TransNormalize<'tcx>, -{ - debug!( - "apply_param_substs(param_substs={:?}, value={:?})", - param_substs, - value - ); - let substituted = value.subst(tcx, param_substs); - let substituted = tcx.erase_regions(&substituted); - AssociatedTypeNormalizer { tcx }.fold(&substituted) -} - - -struct AssociatedTypeNormalizer<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, -} - -impl<'a, 'tcx> AssociatedTypeNormalizer<'a, 'tcx> { - fn fold>(&mut self, value: &T) -> T { - if !value.has_projections() { - value.clone() - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'tcx> ::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNormalizer<'a, 'tcx> { - fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_projections() { - ty - } else { - self.tcx.normalize_associated_type(&ty) - } - } -} - -fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - // generics are weird, don't run this function on a generic - assert!(!ty.needs_subst()); - ty.is_sized(tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP) -} - pub fn resolve_drop_in_place<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, ) -> ty::Instance<'tcx> { let def_id = tcx.require_lang_item(::middle::lang_items::DropInPlaceFnLangItem); let substs = tcx.intern_substs(&[Kind::from(ty)]); - resolve(tcx, def_id, substs) + ty::Instance::resolve(tcx, ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap() } diff --git a/src/librustc/mir/interpret/lvalue.rs b/src/librustc/mir/interpret/lvalue.rs deleted file mode 100644 index e419061fb873f..0000000000000 --- a/src/librustc/mir/interpret/lvalue.rs +++ /dev/null @@ -1,506 +0,0 @@ -use mir; -use ty::layout::{Size, Align}; -use ty::{self, Ty}; -use rustc_data_structures::indexed_vec::Idx; - -use super::{EvalResult, EvalContext, MemoryPointer, PrimVal, Value, Pointer, Machine, PtrAndAlign, ValTy}; - -#[derive(Copy, Clone, Debug)] -pub enum Lvalue { - /// An lvalue referring to a value allocated in the `Memory` system. - Ptr { - /// An lvalue may have an invalid (integral or undef) pointer, - /// since it might be turned back into a reference - /// before ever being dereferenced. - ptr: PtrAndAlign, - extra: LvalueExtra, - }, - - /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with - /// a Mir local index. - Local { frame: usize, local: mir::Local }, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum LvalueExtra { - None, - Length(u64), - Vtable(MemoryPointer), - DowncastVariant(usize), -} - -/// Uniquely identifies a specific constant or static. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct GlobalId<'tcx> { - /// For a constant or static, the `Instance` of the item itself. - /// For a promoted global, the `Instance` of the function they belong to. - pub instance: ty::Instance<'tcx>, - - /// The index for promoted globals within their function's `Mir`. - pub promoted: Option, -} - -impl<'tcx> Lvalue { - /// Produces an Lvalue that will error if attempted to be read from - pub fn undef() -> Self { - Self::from_primval_ptr(PrimVal::Undef.into()) - } - - pub fn from_primval_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { - ptr: PtrAndAlign { ptr, aligned: true }, - extra: LvalueExtra::None, - } - } - - pub fn from_ptr(ptr: MemoryPointer) -> Self { - Self::from_primval_ptr(ptr.into()) - } - - pub(super) fn to_ptr_extra_aligned(self) -> (PtrAndAlign, LvalueExtra) { - match self { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), - - } - } - - pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { - let (ptr, extra) = self.to_ptr_extra_aligned(); - // At this point, we forget about the alignment information -- the lvalue has been turned into a reference, - // and no matter where it came from, it now must be aligned. - assert_eq!(extra, LvalueExtra::None); - ptr.to_ptr() - } - - pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { - match ty.sty { - ty::TyArray(elem, n) => (elem, n.val.to_const_int().unwrap().to_u64().unwrap() as u64), - - ty::TySlice(elem) => { - match self { - Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len), - _ => { - bug!( - "elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", - self - ) - } - } - } - - _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), - } - } -} - -impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { - /// Reads a value from the lvalue without going through the intermediate step of obtaining - /// a `miri::Lvalue` - pub fn try_read_lvalue( - &mut self, - lvalue: &mir::Lvalue<'tcx>, - ) -> EvalResult<'tcx, Option> { - use mir::Lvalue::*; - match *lvalue { - // Might allow this in the future, right now there's no way to do this from Rust code anyway - Local(mir::RETURN_POINTER) => err!(ReadFromReturnPointer), - // Directly reading a local will always succeed - Local(local) => self.frame().get_local(local).map(Some), - // Directly reading a static will always succeed - Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - let cid = GlobalId { - instance, - promoted: None, - }; - Ok(Some(Value::ByRef( - *self.globals.get(&cid).expect("global not cached"), - ))) - } - Projection(ref proj) => self.try_read_lvalue_projection(proj), - } - } - - fn try_read_lvalue_projection( - &mut self, - proj: &mir::LvalueProjection<'tcx>, - ) -> EvalResult<'tcx, Option> { - use mir::ProjectionElem::*; - let base = match self.try_read_lvalue(&proj.base)? { - Some(base) => base, - None => return Ok(None), - }; - let base_ty = self.lvalue_ty(&proj.base); - match proj.elem { - Field(field, _) => match (field.index(), base) { - // the only field of a struct - (0, Value::ByVal(val)) => Ok(Some(Value::ByVal(val))), - // split fat pointers, 2 element tuples, ... - (0...1, Value::ByValPair(a, b)) if self.get_field_count(base_ty)? == 2 => { - let val = [a, b][field.index()]; - Ok(Some(Value::ByVal(val))) - }, - // the only field of a struct is a fat pointer - (0, Value::ByValPair(..)) => Ok(Some(base)), - _ => Ok(None), - }, - // The NullablePointer cases should work fine, need to take care for normal enums - Downcast(..) | - Subslice { .. } | - // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized - ConstantIndex { .. } | Index(_) | - // No way to optimize this projection any better than the normal lvalue path - Deref => Ok(None), - } - } - - /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses. - pub(super) fn eval_and_read_lvalue( - &mut self, - lvalue: &mir::Lvalue<'tcx>, - ) -> EvalResult<'tcx, Value> { - // Shortcut for things like accessing a fat pointer's field, - // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory - // and returning an `Lvalue::Ptr` to it - if let Some(val) = self.try_read_lvalue(lvalue)? { - return Ok(val); - } - let lvalue = self.eval_lvalue(lvalue)?; - self.read_lvalue(lvalue) - } - - pub fn read_lvalue(&self, lvalue: Lvalue) -> EvalResult<'tcx, Value> { - match lvalue { - Lvalue::Ptr { ptr, extra } => { - assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) - } - Lvalue::Local { frame, local } => self.stack[frame].get_local(local), - } - } - - pub fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { - use mir::Lvalue::*; - let lvalue = match *mir_lvalue { - Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - Local(local) => Lvalue::Local { - frame: self.cur_frame(), - local, - }, - - Static(ref static_) => { - let instance = ty::Instance::mono(self.tcx, static_.def_id); - let gid = GlobalId { - instance, - promoted: None, - }; - Lvalue::Ptr { - ptr: *self.globals.get(&gid).expect("uncached global"), - extra: LvalueExtra::None, - } - } - - Projection(ref proj) => { - let ty = self.lvalue_ty(&proj.base); - let lvalue = self.eval_lvalue(&proj.base)?; - return self.eval_lvalue_projection(lvalue, ty, &proj.elem); - } - }; - - if log_enabled!(::log::LogLevel::Trace) { - self.dump_local(lvalue); - } - - Ok(lvalue) - } - - pub fn lvalue_field( - &mut self, - base: Lvalue, - field: mir::Field, - base_ty: Ty<'tcx>, - field_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, Lvalue> { - use ty::layout::Layout::*; - - let base_layout = self.type_layout(base_ty)?; - let field_index = field.index(); - let (offset, packed) = match *base_layout { - Univariant { ref variant, .. } => (variant.offsets[field_index], variant.packed), - - // mir optimizations treat single variant enums as structs - General { ref variants, .. } if variants.len() == 1 => { - (variants[0].offsets[field_index], variants[0].packed) - } - - General { ref variants, .. } => { - let (_, base_extra) = base.to_ptr_extra_aligned(); - if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { - // +1 for the discriminant, which is field 0 - assert!(!variants[variant_idx].packed); - (variants[variant_idx].offsets[field_index + 1], false) - } else { - bug!("field access on enum had no variant index"); - } - } - - RawNullablePointer { .. } => { - assert_eq!(field_index, 0); - return Ok(base); - } - - StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field_index], nonnull.packed) - } - - UntaggedUnion { .. } => return Ok(base), - - Vector { element, count } => { - let field = field_index as u64; - assert!(field < count); - let elem_size = element.size(&self.tcx.data_layout).bytes(); - (Size::from_bytes(field * elem_size), false) - } - - // We treat arrays + fixed sized indexing like field accesses - Array { .. } => { - let field = field_index as u64; - let elem_size = match base_ty.sty { - ty::TyArray(elem_ty, n) => { - assert!(field < n.val.to_const_int().unwrap().to_u64().unwrap() as u64); - self.type_size(elem_ty)?.expect("array elements are sized") as u64 - } - _ => { - bug!( - "lvalue_field: got Array layout but non-array type {:?}", - base_ty - ) - } - }; - (Size::from_bytes(field * elem_size), false) - } - - FatPointer { .. } => { - let bytes = field_index as u64 * self.memory.pointer_size(); - let offset = Size::from_bytes(bytes); - (offset, false) - } - - _ => bug!("field access on non-product type: {:?}", base_layout), - }; - - // Do not allocate in trivial cases - let (base_ptr, base_extra) = match base { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - Lvalue::Local { frame, local } => { - match self.stack[frame].get_local(local)? { - // in case the type has a single field, just return the value - Value::ByVal(_) - if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or( - false, - ) => { - assert_eq!( - offset.bytes(), - 0, - "ByVal can only have 1 non zst field with offset 0" - ); - return Ok(base); - } - Value::ByRef { .. } | - Value::ByValPair(..) | - Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), - } - } - }; - - let offset = match base_extra { - LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst( - base_ty, - base_ptr.ptr.to_value_with_vtable(tab), - )?; - offset - .abi_align(Align::from_bytes(align, align).unwrap()) - .bytes() - } - _ => offset.bytes(), - }; - - let mut ptr = base_ptr.offset(offset, &self)?; - // if we were unaligned, stay unaligned - // no matter what we were, if we are packed, we must not be aligned anymore - ptr.aligned &= !packed; - - let field_ty = self.monomorphize(field_ty, self.substs()); - - let extra = if self.type_is_sized(field_ty) { - LvalueExtra::None - } else { - match base_extra { - LvalueExtra::None => bug!("expected fat pointer"), - LvalueExtra::DowncastVariant(..) => { - bug!("Rust doesn't support unsized fields in enum variants") - } - LvalueExtra::Vtable(_) | - LvalueExtra::Length(_) => {} - } - base_extra - }; - - Ok(Lvalue::Ptr { ptr, extra }) - } - - pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue> { - Ok(match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => { - let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?; - Lvalue::Ptr { - ptr: PtrAndAlign { ptr, aligned: true }, - extra: LvalueExtra::Vtable(vtable), - } - } - ty::TyStr | ty::TySlice(_) => { - let (ptr, len) = val.into_slice(&self.memory)?; - Lvalue::Ptr { - ptr: PtrAndAlign { ptr, aligned: true }, - extra: LvalueExtra::Length(len), - } - } - _ => Lvalue::from_primval_ptr(val.into_ptr(&self.memory)?), - }) - } - - pub(super) fn lvalue_index( - &mut self, - base: Lvalue, - outer_ty: Ty<'tcx>, - n: u64, - ) -> EvalResult<'tcx, Lvalue> { - // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length. - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_extra_aligned(); - - let (elem_ty, len) = base.elem_ty_and_len(outer_ty); - let elem_size = self.type_size(elem_ty)?.expect( - "slice element must be sized", - ); - assert!( - n < len, - "Tried to access element {} of array/slice with length {}", - n, - len - ); - let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; - Ok(Lvalue::Ptr { - ptr, - extra: LvalueExtra::None, - }) - } - - pub(super) fn eval_lvalue_projection( - &mut self, - base: Lvalue, - base_ty: Ty<'tcx>, - proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>, - ) -> EvalResult<'tcx, Lvalue> { - use mir::ProjectionElem::*; - let (ptr, extra) = match *proj_elem { - Field(field, field_ty) => { - return self.lvalue_field(base, field, base_ty, field_ty); - } - - Downcast(_, variant) => { - let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_extra_aligned(); - - use ty::layout::Layout::*; - let extra = match *base_layout { - General { .. } => LvalueExtra::DowncastVariant(variant), - RawNullablePointer { .. } | - StructWrappedNullablePointer { .. } => base_extra, - _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), - }; - (base_ptr, extra) - } - - Deref => { - let val = self.read_lvalue(base)?; - - let pointee_type = match base_ty.sty { - ty::TyRawPtr(ref tam) | - ty::TyRef(_, ref tam) => tam.ty, - ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(), - _ => bug!("can only deref pointer types"), - }; - - trace!("deref to {} on {:?}", pointee_type, val); - - return self.val_to_lvalue(val, pointee_type); - } - - Index(local) => { - let value = self.frame().get_local(local)?; - let ty = self.tcx.types.usize; - let n = self.value_to_primval(ValTy { value, ty })?.to_u64()?; - return self.lvalue_index(base, base_ty, n); - } - - ConstantIndex { - offset, - min_length, - from_end, - } => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_extra_aligned(); - - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect( - "sequence element must be sized", - ); - assert!(n >= min_length as u64); - - let index = if from_end { - n - u64::from(offset) - } else { - u64::from(offset) - }; - - let ptr = base_ptr.offset(index * elem_size, &self)?; - (ptr, LvalueExtra::None) - } - - Subslice { from, to } => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_extra_aligned(); - - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect( - "slice element must be sized", - ); - assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?; - // sublicing arrays produces arrays - let extra = if self.type_is_sized(base_ty) { - LvalueExtra::None - } else { - LvalueExtra::Length(n - u64::from(to) - u64::from(from)) - }; - (ptr, extra) - } - }; - - Ok(Lvalue::Ptr { ptr, extra }) - } - - pub fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize( - lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), - self.substs(), - ) - } -} diff --git a/src/librustc/mir/interpret/machine.rs b/src/librustc/mir/interpret/machine.rs index 95d6fc9aeda4b..29620ad8c6214 100644 --- a/src/librustc/mir/interpret/machine.rs +++ b/src/librustc/mir/interpret/machine.rs @@ -2,7 +2,7 @@ //! This separation exists to ensure that no fancy miri features like //! interpreting common C functions leak into CTFE. -use super::{EvalResult, EvalContext, Lvalue, PrimVal, ValTy}; +use super::{EvalResult, EvalContext, Place, PrimVal, ValTy}; use {mir, ty}; use syntax::codemap::Span; @@ -20,6 +20,11 @@ pub trait Machine<'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones type MemoryKinds: ::std::fmt::Debug + PartialEq + Copy + Clone; + /// Produces the param env for this computation. + fn param_env<'a>( + ecx: &EvalContext<'a, 'tcx, Self>, + ) -> ty::ParamEnv<'tcx>; + /// Entry point to all function calls. /// /// Returns Ok(true) when the function was handled completely @@ -29,7 +34,7 @@ pub trait Machine<'tcx>: Sized { fn eval_fn_call<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, - destination: Option<(Lvalue, mir::BasicBlock)>, + destination: Option<(Place, mir::BasicBlock)>, args: &[ValTy<'tcx>], span: Span, sig: ty::FnSig<'tcx>, @@ -40,9 +45,8 @@ pub trait Machine<'tcx>: Sized { ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[ValTy<'tcx>], - dest: Lvalue, - dest_ty: ty::Ty<'tcx>, - dest_layout: &'tcx ty::layout::Layout, + dest: Place, + dest_layout: ty::layout::TyLayout<'tcx>, target: mir::BasicBlock, ) -> EvalResult<'tcx>; @@ -70,7 +74,7 @@ pub trait Machine<'tcx>: Sized { fn box_alloc<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, ty: ty::Ty<'tcx>, - dest: Lvalue, + dest: Place, ) -> EvalResult<'tcx>; /// Called when trying to access a global declared with a `linkage` attribute diff --git a/src/librustc/mir/interpret/memory.rs b/src/librustc/mir/interpret/memory.rs index 065b21727e2fd..77796638a7b94 100644 --- a/src/librustc/mir/interpret/memory.rs +++ b/src/librustc/mir/interpret/memory.rs @@ -3,13 +3,13 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr, mem, io}; use std::cell::Cell; -use ty::Instance; +use ty::{Instance, TyCtxt}; use ty::layout::{self, TargetDataLayout, HasDataLayout}; use syntax::ast::Mutability; use middle::region; use super::{EvalResult, EvalErrorKind, PrimVal, Pointer, EvalContext, DynamicLifetime, Machine, - RangeMap, AbsLvalue}; + RangeMap, AbsPlace}; //////////////////////////////////////////////////////////////////////////////// // Locks @@ -31,7 +31,7 @@ struct LockInfo<'tcx> { active: Lock, } -/// Write locks are identified by a stack frame and an "abstract" (untyped) lvalue. +/// Write locks are identified by a stack frame and an "abstract" (untyped) place. /// It may be tempting to use the lifetime as identifier, but that does not work /// for two reasons: /// * First of all, due to subtyping, the same lock may be referred to with different @@ -43,14 +43,15 @@ struct LockInfo<'tcx> { #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct WriteLockId<'tcx> { frame: usize, - path: AbsLvalue<'tcx>, + path: AbsPlace<'tcx>, } #[derive(Clone, Debug, PartialEq)] pub enum Lock { NoLock, WriteLock(DynamicLifetime), - ReadLock(Vec), // This should never be empty -- that would be a read lock held and nobody there to release it... + /// This should never be empty -- that would be a read lock held and nobody there to release it... + ReadLock(Vec), } use self::Lock::*; @@ -90,60 +91,17 @@ impl<'tcx> LockInfo<'tcx> { // Allocations and pointers //////////////////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] pub struct AllocId(u64); -#[derive(Debug)] -pub enum AllocIdKind { - /// We can't ever have more than `usize::max_value` functions at the same time - /// since we never "deallocate" functions - Function(usize), - /// Locals and heap allocations (also statics for now, but those will get their - /// own variant soonish). - Runtime(u64), -} - -impl AllocIdKind { - pub fn into_alloc_id(self) -> AllocId { - match self { - AllocIdKind::Function(n) => AllocId(n as u64), - AllocIdKind::Runtime(n) => AllocId((1 << 63) | n), - } - } -} - -impl AllocId { - /// Currently yields the top bit to discriminate the `AllocIdKind`s - fn discriminant(self) -> u64 { - self.0 >> 63 - } - /// Yields everything but the discriminant bits - pub fn index(self) -> u64 { - self.0 & ((1 << 63) - 1) - } - pub fn into_alloc_id_kind(self) -> AllocIdKind { - match self.discriminant() { - 0 => AllocIdKind::Function(self.index() as usize), - 1 => AllocIdKind::Runtime(self.index()), - n => bug!("got discriminant {} for AllocId", n), - } - } -} - impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.into_alloc_id_kind()) - } -} - -impl fmt::Debug for AllocId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.into_alloc_id_kind()) + write!(f, "{}", self.0) } } -#[derive(Debug)] -pub struct Allocation<'tcx, M> { +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer pub bytes: Vec, @@ -154,34 +112,18 @@ pub struct Allocation<'tcx, M> { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: u64, - /// Whether the allocation may be modified. - pub mutable: Mutability, - /// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this - /// allocation is modified or deallocated in the future. - /// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate` - pub kind: MemoryKind, - /// Memory regions that are locked by some function - locks: RangeMap>, } -impl<'tcx, M> Allocation<'tcx, M> { - fn check_locks( - &self, - frame: Option, - offset: u64, - len: u64, - access: AccessKind, - ) -> Result<(), LockInfo<'tcx>> { - if len == 0 { - return Ok(()); - } - for lock in self.locks.iter(offset, len) { - // Check if the lock is in conflict with the access. - if !lock.access_permitted(frame, access) { - return Err(lock.clone()); - } +impl Allocation { + pub fn from_bytes(slice: &[u8]) -> Self { + let mut undef_mask = UndefMask::new(0); + undef_mask.grow(slice.len() as u64, true); + Self { + bytes: slice.to_owned(), + relocations: BTreeMap::new(), + undef_mask, + align: 1, } - Ok(()) } } @@ -189,13 +131,8 @@ impl<'tcx, M> Allocation<'tcx, M> { pub enum MemoryKind { /// Error if deallocated except during a stack pop Stack, - /// Static in the process of being initialized. - /// The difference is important: An immutable static referring to a - /// mutable initialized static will freeze immutably and would not - /// be able to distinguish already initialized statics from uninitialized ones - UninitializedStatic, - /// May never be deallocated - Static, + /// A mutable Static. All the others are interned in the tcx + MutableStatic, // FIXME: move me into the machine, rustc const eval doesn't need them /// Additional memory kinds a machine wishes to distinguish from the builtin ones Machine(T), } @@ -247,15 +184,20 @@ impl<'tcx> MemoryPointer { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory<'a, 'tcx, M: Machine<'tcx>> { +pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> { /// Additional data required by the Machine pub data: M::MemoryData, + /// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate` + alloc_kind: HashMap>, + /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations). - alloc_map: HashMap>, + alloc_map: HashMap, - /// The AllocId to assign to the next new regular allocation. Always incremented, never gets smaller. - next_alloc_id: u64, + /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations). + /// + /// Stores statics while they are being processed, before they are interned and thus frozen + uninitialized_statics: HashMap, /// Number of virtual bytes allocated. memory_usage: u64, @@ -263,20 +205,6 @@ pub struct Memory<'a, 'tcx, M: Machine<'tcx>> { /// Maximum number of virtual bytes that may be allocated. memory_size: u64, - /// Function "allocations". They exist solely so pointers have something to point to, and - /// we can figure out what they point to. - functions: Vec>, - - /// Inverse map of `functions` so we don't allocate a new pointer every time we need one - function_alloc_cache: HashMap, AllocId>, - - /// Target machine data layout to emulate. - pub layout: &'a TargetDataLayout, - - /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate - /// allocations for string and bytestring literals. - literal_alloc_cache: HashMap, AllocId>, - /// To avoid having to pass flags to every single memory access, we have some global state saying whether /// alignment checking is currently enforced for read and/or write accesses. reads_are_aligned: Cell, @@ -284,73 +212,76 @@ pub struct Memory<'a, 'tcx, M: Machine<'tcx>> { /// The current stack frame. Used to check accesses against locks. pub(super) cur_frame: usize, + + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + + /// Memory regions that are locked by some function + /// + /// Only mutable (static mut, heap, stack) allocations have an entry in this map. + /// The entry is created when allocating the memory and deleted after deallocation. + locks: HashMap>>, +} + +impl<'tcx> RangeMap> { + fn check( + &self, + frame: Option, + offset: u64, + len: u64, + access: AccessKind, + ) -> Result<(), LockInfo<'tcx>> { + if len == 0 { + return Ok(()); + } + for lock in self.iter(offset, len) { + // Check if the lock is in conflict with the access. + if !lock.access_permitted(frame, access) { + return Err(lock.clone()); + } + } + Ok(()) + } } impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { - pub fn new(layout: &'a TargetDataLayout, max_memory: u64, data: M::MemoryData) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, max_memory: u64, data: M::MemoryData) -> Self { Memory { data, + alloc_kind: HashMap::new(), alloc_map: HashMap::new(), - functions: Vec::new(), - function_alloc_cache: HashMap::new(), - next_alloc_id: 0, - layout, + uninitialized_statics: HashMap::new(), + tcx, memory_size: max_memory, memory_usage: 0, - literal_alloc_cache: HashMap::new(), reads_are_aligned: Cell::new(true), writes_are_aligned: Cell::new(true), cur_frame: usize::max_value(), + locks: HashMap::new(), } } pub fn allocations<'x>( &'x self, - ) -> impl Iterator)> { - self.alloc_map.iter().map(|(&id, alloc)| { - (AllocIdKind::Runtime(id).into_alloc_id(), alloc) - }) + ) -> impl Iterator { + self.alloc_map.iter().map(|(&id, alloc)| (AllocId(id), alloc)) } pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> MemoryPointer { - if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { - return MemoryPointer::new(alloc_id, 0); - } - let id = self.functions.len(); - debug!("creating fn ptr: {}", id); - self.functions.push(instance); - let alloc_id = AllocIdKind::Function(id).into_alloc_id(); - self.function_alloc_cache.insert(instance, alloc_id); - MemoryPointer::new(alloc_id, 0) + let id = self.tcx.interpret_interner.borrow_mut().create_fn_alloc(instance); + MemoryPointer::new(AllocId(id), 0) } - pub fn allocate_cached(&mut self, bytes: &[u8]) -> EvalResult<'tcx, MemoryPointer> { - if let Some(&alloc_id) = self.literal_alloc_cache.get(bytes) { - return Ok(MemoryPointer::new(alloc_id, 0)); - } - - let ptr = self.allocate( - bytes.len() as u64, - 1, - MemoryKind::UninitializedStatic, - )?; - self.write_bytes(ptr.into(), bytes)?; - self.mark_static_initalized( - ptr.alloc_id, - Mutability::Immutable, - )?; - self.literal_alloc_cache.insert( - bytes.to_vec(), - ptr.alloc_id, - ); - Ok(ptr) + pub fn allocate_cached(&mut self, bytes: &[u8]) -> MemoryPointer { + let id = self.tcx.allocate_cached(bytes); + MemoryPointer::new(AllocId(id), 0) } + /// kind is `None` for statics pub fn allocate( &mut self, size: u64, align: u64, - kind: MemoryKind, + kind: Option>, ) -> EvalResult<'tcx, MemoryPointer> { assert_ne!(align, 0); assert!(align.is_power_of_two()); @@ -369,17 +300,21 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, - kind, - mutable: Mutability::Mutable, - locks: RangeMap::new(), }; - let id = self.next_alloc_id; - self.next_alloc_id += 1; - self.alloc_map.insert(id, alloc); - Ok(MemoryPointer::new( - AllocIdKind::Runtime(id).into_alloc_id(), - 0, - )) + let id = self.tcx.interpret_interner.borrow_mut().reserve(); + self.locks.insert(id, RangeMap::new()); + match kind { + Some(kind @ MemoryKind::Stack) | + Some(kind @ MemoryKind::Machine(_)) => { + self.alloc_map.insert(id, alloc); + self.alloc_kind.insert(id, kind); + }, + None => { + self.uninitialized_statics.insert(id, alloc); + }, + Some(MemoryKind::MutableStatic) => bug!("don't allocate mutable statics directly") + } + Ok(MemoryPointer::new(AllocId(id), 0)) } pub fn reallocate( @@ -396,17 +331,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { if ptr.offset != 0 { return err!(ReallocateNonBasePtr); } - if let Ok(alloc) = self.get(ptr.alloc_id) { - if alloc.kind != kind { + if self.alloc_map.contains_key(&ptr.alloc_id.0) { + let alloc_kind = self.alloc_kind[&ptr.alloc_id.0]; + if alloc_kind != kind { return err!(ReallocatedWrongMemoryKind( - format!("{:?}", alloc.kind), + format!("{:?}", alloc_kind), format!("{:?}", kind), )); } } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" - let new_ptr = self.allocate(new_size, new_align, kind)?; + let new_ptr = self.allocate(new_size, new_align, Some(kind))?; self.copy( ptr.into(), new_ptr.into(), @@ -420,6 +356,19 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(new_ptr) } + pub fn deallocate_local(&mut self, ptr: MemoryPointer) -> EvalResult<'tcx> { + match self.alloc_kind.get(&ptr.alloc_id.0).cloned() { + // for a constant like `const FOO: &i32 = &1;` the local containing + // the `1` is referred to by the global. We transitively marked everything + // the global refers to as static itself, so we don't free it here + Some(MemoryKind::MutableStatic) => Ok(()), + Some(MemoryKind::Stack) => self.deallocate(ptr, None, MemoryKind::Stack), + // Happens if the memory was interned into immutable memory + None => Ok(()), + other => bug!("local contained non-stack memory: {:?}", other), + } + } + pub fn deallocate( &mut self, ptr: MemoryPointer, @@ -430,28 +379,39 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { return err!(DeallocateNonBasePtr); } - let alloc_id = match ptr.alloc_id.into_alloc_id_kind() { - AllocIdKind::Function(_) => { + let alloc = match self.alloc_map.remove(&ptr.alloc_id.0) { + Some(alloc) => alloc, + None => if self.uninitialized_statics.contains_key(&ptr.alloc_id.0) { + return err!(DeallocatedWrongMemoryKind( + "uninitializedstatic".to_string(), + format!("{:?}", kind), + )) + } else if self.tcx.interpret_interner.borrow().get_fn(ptr.alloc_id.0).is_some() { return err!(DeallocatedWrongMemoryKind( "function".to_string(), format!("{:?}", kind), )) - } - AllocIdKind::Runtime(id) => id, + } else if self.tcx.interpret_interner.borrow().get_alloc(ptr.alloc_id.0).is_some() { + return err!(DeallocatedWrongMemoryKind( + "static".to_string(), + format!("{:?}", kind), + )) + } else { + return err!(DoubleFree) + }, }; - let alloc = match self.alloc_map.remove(&alloc_id) { - Some(alloc) => alloc, - None => return err!(DoubleFree), - }; + let alloc_kind = self.alloc_kind.remove(&ptr.alloc_id.0).expect("alloc_map out of sync with alloc_kind"); // It is okay for us to still holds locks on deallocation -- for example, we could store data we own // in a local, and the local could be deallocated (from StorageDead) before the function returns. // However, we should check *something*. For now, we make sure that there is no conflicting write // lock by another frame. We *have* to permit deallocation if we hold a read lock. // TODO: Figure out the exact rules here. - alloc - .check_locks( + self.locks + .remove(&ptr.alloc_id.0) + .expect("allocation has no corresponding locks") + .check( Some(self.cur_frame), 0, alloc.bytes.len() as u64, @@ -464,15 +424,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } })?; - if alloc.kind != kind { + if alloc_kind != kind { return err!(DeallocatedWrongMemoryKind( - format!("{:?}", alloc.kind), + format!("{:?}", alloc_kind), format!("{:?}", kind), )); } if let Some((size, align)) = size_and_align { if size != alloc.bytes.len() as u64 || align != alloc.align { - return err!(IncorrectAllocationInformation); + return err!(IncorrectAllocationInformation(size, alloc.bytes.len(), align, alloc.align)); } } @@ -483,11 +443,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } pub fn pointer_size(&self) -> u64 { - self.layout.pointer_size.bytes() + self.tcx.data_layout.pointer_size.bytes() } pub fn endianess(&self) -> layout::Endian { - self.layout.endian + self.tcx.data_layout.endian } /// Check that the pointer is aligned AND non-NULL. @@ -558,10 +518,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { if len == 0 { return Ok(()); } - let alloc = self.get(ptr.alloc_id)?; + let locks = match self.locks.get(&ptr.alloc_id.0) { + Some(locks) => locks, + // immutable static or other constant memory + None => return Ok(()), + }; let frame = self.cur_frame; - alloc - .check_locks(Some(frame), ptr.offset, len, access) + locks + .check(Some(frame), ptr.offset, len, access) .map_err(|lock| { EvalErrorKind::MemoryLockViolation { ptr, @@ -591,13 +555,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { len, region ); - self.check_bounds(ptr.offset(len, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - let alloc = self.get_mut_unchecked(ptr.alloc_id)?; + self.check_bounds(ptr.offset(len, &*self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + + let locks = match self.locks.get_mut(&ptr.alloc_id.0) { + Some(locks) => locks, + // immutable static or other constant memory + None => return Ok(()), + }; // Iterate over our range and acquire the lock. If the range is already split into pieces, // we have to manipulate all of them. let lifetime = DynamicLifetime { frame, region }; - for lock in alloc.locks.iter_mut(ptr.offset, len) { + for lock in locks.iter_mut(ptr.offset, len) { if !lock.access_permitted(None, kind) { return err!(MemoryAcquireConflict { ptr, @@ -632,14 +601,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { &mut self, ptr: MemoryPointer, len: u64, - lock_path: &AbsLvalue<'tcx>, + lock_path: &AbsPlace<'tcx>, suspend: Option, ) -> EvalResult<'tcx> { assert!(len > 0); let cur_frame = self.cur_frame; - let alloc = self.get_mut_unchecked(ptr.alloc_id)?; + let locks = match self.locks.get_mut(&ptr.alloc_id.0) { + Some(locks) => locks, + // immutable static or other constant memory + None => return Ok(()), + }; - 'locks: for lock in alloc.locks.iter_mut(ptr.offset, len) { + 'locks: for lock in locks.iter_mut(ptr.offset, len) { let is_our_lock = match lock.active { WriteLock(lft) => // Double-check that we are holding the lock. @@ -701,7 +674,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { &mut self, ptr: MemoryPointer, len: u64, - lock_path: &AbsLvalue<'tcx>, + lock_path: &AbsPlace<'tcx>, lock_region: Option, suspended_region: region::Scope, ) -> EvalResult<'tcx> { @@ -711,9 +684,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { frame: cur_frame, path: lock_path.clone(), }; - let alloc = self.get_mut_unchecked(ptr.alloc_id)?; + let locks = match self.locks.get_mut(&ptr.alloc_id.0) { + Some(locks) => locks, + // immutable static or other constant memory + None => return Ok(()), + }; - for lock in alloc.locks.iter_mut(ptr.offset, len) { + for lock in locks.iter_mut(ptr.offset, len) { // Check if we have a suspension here let (got_the_lock, remove_suspension) = match lock.suspended.get_mut(&lock_id) { None => { @@ -787,8 +764,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } }; - for alloc in self.alloc_map.values_mut() { - for lock in alloc.locks.iter_mut_all() { + for alloc_locks in self.locks.values_mut() { + for lock in alloc_locks.iter_mut_all() { // Delete everything that ends now -- i.e., keep only all the other lifetimes. let lock_ended = match lock.active { WriteLock(ref lft) => has_ended(lft), @@ -807,7 +784,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } // Clean up the map - alloc.locks.retain(|lock| match lock.active { + alloc_locks.retain(|lock| match lock.active { NoLock => lock.suspended.len() > 0, _ => true, }); @@ -817,39 +794,50 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { /// Allocation accessors impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { - pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<'tcx, M::MemoryKinds>> { - match id.into_alloc_id_kind() { - AllocIdKind::Function(_) => err!(DerefFunctionPointer), - AllocIdKind::Runtime(id) => { - match self.alloc_map.get(&id) { + pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { + // normal alloc? + match self.alloc_map.get(&id.0) { Some(alloc) => Ok(alloc), - None => err!(DanglingPointerDeref), - } - } + // uninitialized static alloc? + None => match self.uninitialized_statics.get(&id.0) { + Some(alloc) => Ok(alloc), + None => { + let int = self.tcx.interpret_interner.borrow(); + // static alloc? + int.get_alloc(id.0) + // no alloc? produce an error + .ok_or_else(|| if int.get_fn(id.0).is_some() { + EvalErrorKind::DerefFunctionPointer.into() + } else { + EvalErrorKind::DanglingPointerDeref.into() + }) + }, + }, } } - fn get_mut_unchecked( + fn get_mut( &mut self, id: AllocId, - ) -> EvalResult<'tcx, &mut Allocation<'tcx, M::MemoryKinds>> { - match id.into_alloc_id_kind() { - AllocIdKind::Function(_) => err!(DerefFunctionPointer), - AllocIdKind::Runtime(id) => { - match self.alloc_map.get_mut(&id) { - Some(alloc) => Ok(alloc), - None => err!(DanglingPointerDeref), - } - } - } - } - - fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation<'tcx, M::MemoryKinds>> { - let alloc = self.get_mut_unchecked(id)?; - if alloc.mutable == Mutability::Mutable { - Ok(alloc) - } else { - err!(ModifiedConstantMemory) + ) -> EvalResult<'tcx, &mut Allocation> { + // normal alloc? + match self.alloc_map.get_mut(&id.0) { + Some(alloc) => Ok(alloc), + // uninitialized static alloc? + None => match self.uninitialized_statics.get_mut(&id.0) { + Some(alloc) => Ok(alloc), + None => { + let int = self.tcx.interpret_interner.borrow(); + // no alloc or immutable alloc? produce an error + if int.get_alloc(id.0).is_some() { + err!(ModifiedConstantMemory) + } else if int.get_fn(id.0).is_some() { + err!(DerefFunctionPointer) + } else { + err!(DanglingPointerDeref) + } + }, + }, } } @@ -858,10 +846,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { return err!(InvalidFunctionPointer); } debug!("reading fn ptr: {}", ptr.alloc_id); - match ptr.alloc_id.into_alloc_id_kind() { - AllocIdKind::Function(id) => Ok(self.functions[id]), - AllocIdKind::Runtime(_) => err!(ExecuteMemory), - } + self.tcx + .interpret_interner + .borrow() + .get_fn(ptr.alloc_id.0) + .ok_or(EvalErrorKind::ExecuteMemory.into()) } /// For debugging, print an allocation and all allocations it points to, recursively. @@ -882,20 +871,32 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { let prefix_len = msg.len(); let mut relocations = vec![]; - let alloc = match id.into_alloc_id_kind() { - AllocIdKind::Function(id) => { - trace!("{} {}", msg, self.functions[id]); - continue; - } - AllocIdKind::Runtime(id) => { - match self.alloc_map.get(&id) { - Some(a) => a, + let (alloc, immutable) = + // normal alloc? + match self.alloc_map.get(&id.0) { + Some(a) => (a, match self.alloc_kind[&id.0] { + MemoryKind::Stack => " (stack)".to_owned(), + MemoryKind::Machine(m) => format!(" ({:?})", m), + MemoryKind::MutableStatic => " (static mut)".to_owned(), + }), + // uninitialized static alloc? + None => match self.uninitialized_statics.get(&id.0) { + Some(a) => (a, " (static in the process of initialization)".to_owned()), None => { + let int = self.tcx.interpret_interner.borrow(); + // static alloc? + match int.get_alloc(id.0) { + Some(a) => (a, "(immutable)".to_owned()), + None => if let Some(func) = int.get_fn(id.0) { + trace!("{} {}", msg, func); + continue; + } else { trace!("{} (deallocated)", msg); continue; - } - } + }, } + }, + }, }; for i in 0..(alloc.bytes.len() as u64) { @@ -913,15 +914,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } - let immutable = match (alloc.kind, alloc.mutable) { - (MemoryKind::UninitializedStatic, _) => { - " (static in the process of initialization)".to_owned() - } - (MemoryKind::Static, Mutability::Mutable) => " (static mut)".to_owned(), - (MemoryKind::Static, Mutability::Immutable) => " (immutable)".to_owned(), - (MemoryKind::Machine(m), _) => format!(" ({:?})", m), - (MemoryKind::Stack, _) => " (stack)".to_owned(), - }; trace!( "{}({} bytes, alignment {}){}", msg, @@ -950,10 +942,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { pub fn leak_report(&self) -> usize { trace!("### LEAK REPORT ###"); + let kinds = &self.alloc_kind; let leaks: Vec<_> = self.alloc_map - .iter() - .filter_map(|(&key, val)| if val.kind != MemoryKind::Static { - Some(AllocIdKind::Runtime(key).into_alloc_id()) + .keys() + .filter_map(|key| if kinds[key] != MemoryKind::MutableStatic { + Some(AllocId(*key)) } else { None }) @@ -998,7 +991,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { return Ok(&mut []); } self.check_locks(ptr, size, AccessKind::Write)?; - self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, &*self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -1036,14 +1029,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { alloc: AllocId, mutability: Mutability, ) -> EvalResult<'tcx> { - // relocations into other statics are not "inner allocations" - if self.get(alloc).ok().map_or(false, |alloc| { - alloc.kind != MemoryKind::UninitializedStatic - }) - { - self.mark_static_initalized(alloc, mutability)?; + match self.alloc_kind.get(&alloc.0) { + // do not go into immutable statics + None | + // or mutable statics + Some(&MemoryKind::MutableStatic) => Ok(()), + // just locals and machine allocs + Some(_) => self.mark_static_initalized(alloc, mutability), } - Ok(()) } /// mark an allocation as static and initialized, either mutable or not @@ -1057,33 +1050,45 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { alloc_id, mutability ); + if mutability == Mutability::Immutable { + let alloc = self.alloc_map.remove(&alloc_id.0); + let kind = self.alloc_kind.remove(&alloc_id.0); + assert_ne!(kind, Some(MemoryKind::MutableStatic)); + let uninit = self.uninitialized_statics.remove(&alloc_id.0); + if let Some(alloc) = alloc.or(uninit) { + let alloc = self.tcx.intern_const_alloc(alloc); + self.tcx.interpret_interner.borrow_mut().intern_at_reserved(alloc_id.0, alloc); + // recurse into inner allocations + for &alloc in alloc.relocations.values() { + self.mark_inner_allocation_initialized(alloc, mutability)?; + } + } + return Ok(()); + } + // We are marking the static as initialized, so move it out of the uninit map + if let Some(uninit) = self.uninitialized_statics.remove(&alloc_id.0) { + self.alloc_map.insert(alloc_id.0, uninit); + } // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) - let alloc_id = match alloc_id.into_alloc_id_kind() { - AllocIdKind::Function(_) => return Ok(()), - AllocIdKind::Runtime(id) => id, - }; - let relocations = match self.alloc_map.get_mut(&alloc_id) { + let relocations = match self.alloc_map.get_mut(&alloc_id.0) { Some(&mut Allocation { ref mut relocations, - ref mut kind, - ref mut mutable, .. }) => { - match *kind { + match self.alloc_kind.get(&alloc_id.0) { // const eval results can refer to "locals". // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` - MemoryKind::Stack | - // The entire point of this function - MemoryKind::UninitializedStatic => {}, - MemoryKind::Machine(m) => M::mark_static_initialized(m)?, - MemoryKind::Static => { + None | + Some(&MemoryKind::Stack) => {}, + Some(&MemoryKind::Machine(m)) => M::mark_static_initialized(m)?, + Some(&MemoryKind::MutableStatic) => { trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); return Ok(()); }, } - *kind = MemoryKind::Static; - *mutable = mutability; + // overwrite or insert + self.alloc_kind.insert(alloc_id.0, MemoryKind::MutableStatic); // take out the relocations vector to free the borrow on self, so we can call // mark recursively mem::replace(relocations, Default::default()) @@ -1096,7 +1101,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } // put back the relocations self.alloc_map - .get_mut(&alloc_id) + .get_mut(&alloc_id.0) .expect("checked above") .relocations = relocations; Ok(()) @@ -1309,11 +1314,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { // We assume pointer-sized integers have the same alignment as pointers. // We also assume signed and unsigned integers of the same size have the same alignment. match size { - 1 => self.layout.i8_align.abi(), - 2 => self.layout.i16_align.abi(), - 4 => self.layout.i32_align.abi(), - 8 => self.layout.i64_align.abi(), - 16 => self.layout.i128_align.abi(), + 1 => self.tcx.data_layout.i8_align.abi(), + 2 => self.tcx.data_layout.i16_align.abi(), + 4 => self.tcx.data_layout.i32_align.abi(), + 8 => self.tcx.data_layout.i64_align.abi(), + 16 => self.tcx.data_layout.i128_align.abi(), _ => bug!("bad integer size: {}", size), } } @@ -1365,7 +1370,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { fn check_relocation_edges(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size, self)?, 0)?.count(); if overlapping_start + overlapping_end != 0 { return err!(ReadPointerAsBytes); } @@ -1479,7 +1484,7 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result, len: u64, @@ -1563,7 +1568,7 @@ fn bit_index(bits: u64) -> (usize, usize) { // Unaligned accesses //////////////////////////////////////////////////////////////////////////////// -pub trait HasMemory<'a, 'tcx, M: Machine<'tcx>> { +pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M>; fn memory(&self) -> &Memory<'a, 'tcx, M>; @@ -1681,20 +1686,6 @@ impl PointerArithmetic for T {} impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { - self.layout - } -} -impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a EvalContext<'a, 'tcx, M> { - #[inline] - fn data_layout(&self) -> &TargetDataLayout { - self.memory().layout - } -} - -impl<'c, 'b, 'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout - for &'c &'b mut EvalContext<'a, 'tcx, M> { - #[inline] - fn data_layout(&self) -> &TargetDataLayout { - self.memory().layout + &self.tcx.data_layout } } diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index f2a2dc3115f66..d3b742e0030d0 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -9,7 +9,7 @@ mod cast; mod const_eval; mod error; mod eval_context; -mod lvalue; +mod place; mod validation; mod machine; mod memory; @@ -25,9 +25,9 @@ pub use self::error::{EvalError, EvalResult, EvalErrorKind}; pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup, DynamicLifetime, TyAndPacked, PtrAndAlign, ValTy}; -pub use self::lvalue::{Lvalue, LvalueExtra, GlobalId}; +pub use self::place::{Place, PlaceExtra, GlobalId}; -pub use self::memory::{AllocId, Memory, MemoryPointer, MemoryKind, HasMemory, AccessKind, AllocIdKind}; +pub use self::memory::{AllocId, Memory, MemoryPointer, MemoryKind, HasMemory, AccessKind, Allocation}; use self::memory::{PointerArithmetic, Lock}; @@ -35,8 +35,8 @@ use self::range_map::RangeMap; pub use self::value::{PrimVal, PrimValKind, Value, Pointer}; -pub use self::const_eval::{eval_body_as_integer, eval_body_as_primval}; +pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeFunctionEvaluator}; pub use self::machine::Machine; -pub use self::validation::{ValidationQuery, AbsLvalue}; +pub use self::validation::{ValidationQuery, AbsPlace}; diff --git a/src/librustc/mir/interpret/operator.rs b/src/librustc/mir/interpret/operator.rs index 2981d21929d1a..6058ac7e72958 100644 --- a/src/librustc/mir/interpret/operator.rs +++ b/src/librustc/mir/interpret/operator.rs @@ -4,10 +4,9 @@ use rustc_const_math::ConstFloat; use syntax::ast::FloatTy; use std::cmp::Ordering; -use super::{EvalResult, EvalContext, Lvalue, Machine, ValTy}; +use super::{EvalResult, EvalContext, Place, Machine, ValTy}; -use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64, f32_to_bytes, - f64_to_bytes}; +use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { fn binop_with_overflow( @@ -28,7 +27,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { op: mir::BinOp, left: ValTy<'tcx>, right: ValTy<'tcx>, - dest: Lvalue, + dest: Place, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; @@ -47,7 +46,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { op: mir::BinOp, left: ValTy<'tcx>, right: ValTy<'tcx>, - dest: Lvalue, + dest: Place, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, bool> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; @@ -255,8 +254,8 @@ pub fn unary_op<'tcx>( (Neg, I64) => -(bytes as i64) as u128, (Neg, I128) => -(bytes as i128) as u128, - (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)), - (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)), + (Neg, F32) => (-bytes_to_f32(bytes)).bits, + (Neg, F64) => (-bytes_to_f64(bytes)).bits, _ => { let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); diff --git a/src/librustc/mir/interpret/place.rs b/src/librustc/mir/interpret/place.rs new file mode 100644 index 0000000000000..e8591b2e805e9 --- /dev/null +++ b/src/librustc/mir/interpret/place.rs @@ -0,0 +1,442 @@ +use mir; +use ty::{self, Ty}; +use ty::layout::TyLayout; +use rustc_data_structures::indexed_vec::Idx; + +use super::{EvalResult, EvalContext, MemoryPointer, PrimVal, Value, Pointer, Machine, PtrAndAlign, ValTy}; + +#[derive(Copy, Clone, Debug)] +pub enum Place { + /// An place referring to a value allocated in the `Memory` system. + Ptr { + /// An place may have an invalid (integral or undef) pointer, + /// since it might be turned back into a reference + /// before ever being dereferenced. + ptr: PtrAndAlign, + extra: PlaceExtra, + }, + + /// An place referring to a value on the stack. Represented by a stack frame index paired with + /// a Mir local index. + Local { frame: usize, local: mir::Local }, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PlaceExtra { + None, + Length(u64), + Vtable(MemoryPointer), + DowncastVariant(usize), +} + +/// Uniquely identifies a specific constant or static. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct GlobalId<'tcx> { + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub instance: ty::Instance<'tcx>, + + /// The index for promoted globals within their function's `Mir`. + pub promoted: Option, +} + +impl<'tcx> Place { + /// Produces an Place that will error if attempted to be read from + pub fn undef() -> Self { + Self::from_primval_ptr(PrimVal::Undef.into()) + } + + pub fn from_primval_ptr(ptr: Pointer) -> Self { + Place::Ptr { + ptr: PtrAndAlign { ptr, aligned: true }, + extra: PlaceExtra::None, + } + } + + pub fn from_ptr(ptr: MemoryPointer) -> Self { + Self::from_primval_ptr(ptr.into()) + } + + pub fn to_ptr_extra_aligned(self) -> (PtrAndAlign, PlaceExtra) { + match self { + Place::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("to_ptr_and_extra: expected Place::Ptr, got {:?}", self), + + } + } + + pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { + let (ptr, extra) = self.to_ptr_extra_aligned(); + // At this point, we forget about the alignment information -- the place has been turned into a reference, + // and no matter where it came from, it now must be aligned. + assert_eq!(extra, PlaceExtra::None); + ptr.to_ptr() + } + + pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { + match ty.sty { + ty::TyArray(elem, n) => (elem, n.val.to_const_int().unwrap().to_u64().unwrap() as u64), + + ty::TySlice(elem) => { + match self { + Place::Ptr { extra: PlaceExtra::Length(len), .. } => (elem, len), + _ => { + bug!( + "elem_ty_and_len of a TySlice given non-slice place: {:?}", + self + ) + } + } + } + + _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), + } + } +} + +impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { + /// Reads a value from the place without going through the intermediate step of obtaining + /// a `miri::Place` + pub fn try_read_place( + &mut self, + place: &mir::Place<'tcx>, + ) -> EvalResult<'tcx, Option> { + use mir::Place::*; + match *place { + // Might allow this in the future, right now there's no way to do this from Rust code anyway + Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer), + // Directly reading a local will always succeed + Local(local) => self.frame().get_local(local).map(Some), + // Directly reading a static will always succeed + Static(ref static_) => { + let instance = ty::Instance::mono(self.tcx, static_.def_id); + let cid = GlobalId { + instance, + promoted: None, + }; + Ok(Some(Value::ByRef( + self.tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached"), + ))) + } + Projection(ref proj) => self.try_read_place_projection(proj), + } + } + + fn try_read_place_projection( + &mut self, + proj: &mir::PlaceProjection<'tcx>, + ) -> EvalResult<'tcx, Option> { + use mir::ProjectionElem::*; + let base = match self.try_read_place(&proj.base)? { + Some(base) => base, + None => return Ok(None), + }; + let base_ty = self.place_ty(&proj.base); + match proj.elem { + Field(field, _) => { + let base_layout = self.type_layout(base_ty)?; + let field_index = field.index(); + let field = base_layout.field(&self, field_index)?; + let offset = base_layout.fields.offset(field_index); + match base { + // the field covers the entire type + Value::ByValPair(..) | + Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some(base)), + // split fat pointers, 2 element tuples, ... + Value::ByValPair(a, b) if base_layout.fields.count() == 2 => { + let val = [a, b][field_index]; + Ok(Some(Value::ByVal(val))) + }, + _ => Ok(None), + } + }, + // The NullablePointer cases should work fine, need to take care for normal enums + Downcast(..) | + Subslice { .. } | + // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized + ConstantIndex { .. } | Index(_) | + // No way to optimize this projection any better than the normal place path + Deref => Ok(None), + } + } + + /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses. + pub(super) fn eval_and_read_place( + &mut self, + place: &mir::Place<'tcx>, + ) -> EvalResult<'tcx, Value> { + // Shortcut for things like accessing a fat pointer's field, + // which would otherwise (in the `eval_place` path) require moving a `ByValPair` to memory + // and returning an `Place::Ptr` to it + if let Some(val) = self.try_read_place(place)? { + return Ok(val); + } + let place = self.eval_place(place)?; + self.read_place(place) + } + + pub fn read_place(&self, place: Place) -> EvalResult<'tcx, Value> { + match place { + Place::Ptr { ptr, extra } => { + assert_eq!(extra, PlaceExtra::None); + Ok(Value::ByRef(ptr)) + } + Place::Local { frame, local } => self.stack[frame].get_local(local), + } + } + + pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> { + use mir::Place::*; + let place = match *mir_place { + Local(mir::RETURN_PLACE) => self.frame().return_place, + Local(local) => Place::Local { + frame: self.cur_frame(), + local, + }, + + Static(ref static_) => { + let instance = ty::Instance::mono(self.tcx, static_.def_id); + let gid = GlobalId { + instance, + promoted: None, + }; + Place::Ptr { + ptr: self.tcx.interpret_interner.borrow().get_cached(gid).expect("uncached global"), + extra: PlaceExtra::None, + } + } + + Projection(ref proj) => { + let ty = self.place_ty(&proj.base); + let place = self.eval_place(&proj.base)?; + return self.eval_place_projection(place, ty, &proj.elem); + } + }; + + if log_enabled!(::log::LogLevel::Trace) { + self.dump_local(place); + } + + Ok(place) + } + + pub fn place_field( + &mut self, + base: Place, + field: mir::Field, + mut base_layout: TyLayout<'tcx>, + ) -> EvalResult<'tcx, (Place, TyLayout<'tcx>)> { + match base { + Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => { + base_layout = base_layout.for_variant(&self, variant_index); + } + _ => {} + } + let field_index = field.index(); + let field = base_layout.field(&self, field_index)?; + let offset = base_layout.fields.offset(field_index); + + // Do not allocate in trivial cases + let (base_ptr, base_extra) = match base { + Place::Ptr { ptr, extra } => (ptr, extra), + Place::Local { frame, local } => { + match self.stack[frame].get_local(local)? { + // in case the field covers the entire type, just return the value + Value::ByVal(_) if offset.bytes() == 0 && + field.size == base_layout.size => { + return Ok((base, field)); + } + Value::ByRef { .. } | + Value::ByValPair(..) | + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), + } + } + }; + + let offset = match base_extra { + PlaceExtra::Vtable(tab) => { + let (_, align) = self.size_and_align_of_dst( + base_layout.ty, + base_ptr.ptr.to_value_with_vtable(tab), + )?; + offset.abi_align(align).bytes() + } + _ => offset.bytes(), + }; + + let mut ptr = base_ptr.offset(offset, &self)?; + // if we were unaligned, stay unaligned + // no matter what we were, if we are packed, we must not be aligned anymore + ptr.aligned &= !base_layout.is_packed(); + + let extra = if !field.is_unsized() { + PlaceExtra::None + } else { + match base_extra { + PlaceExtra::None => bug!("expected fat pointer"), + PlaceExtra::DowncastVariant(..) => { + bug!("Rust doesn't support unsized fields in enum variants") + } + PlaceExtra::Vtable(_) | + PlaceExtra::Length(_) => {} + } + base_extra + }; + + Ok((Place::Ptr { ptr, extra }, field)) + } + + pub(super) fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> { + Ok(match self.tcx.struct_tail(ty).sty { + ty::TyDynamic(..) => { + let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?; + Place::Ptr { + ptr: PtrAndAlign { ptr, aligned: true }, + extra: PlaceExtra::Vtable(vtable), + } + } + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.into_slice(&self.memory)?; + Place::Ptr { + ptr: PtrAndAlign { ptr, aligned: true }, + extra: PlaceExtra::Length(len), + } + } + _ => Place::from_primval_ptr(val.into_ptr(&self.memory)?), + }) + } + + pub(super) fn place_index( + &mut self, + base: Place, + outer_ty: Ty<'tcx>, + n: u64, + ) -> EvalResult<'tcx, Place> { + // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length. + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_extra_aligned(); + + let (elem_ty, len) = base.elem_ty_and_len(outer_ty); + let elem_size = self.type_size(elem_ty)?.expect( + "slice element must be sized", + ); + assert!( + n < len, + "Tried to access element {} of array/slice with length {}", + n, + len + ); + let ptr = base_ptr.offset(n * elem_size, &*self)?; + Ok(Place::Ptr { + ptr, + extra: PlaceExtra::None, + }) + } + + pub(super) fn place_downcast( + &mut self, + base: Place, + variant: usize, + ) -> EvalResult<'tcx, Place> { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (ptr, _) = base.to_ptr_extra_aligned(); + let extra = PlaceExtra::DowncastVariant(variant); + Ok(Place::Ptr { ptr, extra }) + } + + pub(super) fn eval_place_projection( + &mut self, + base: Place, + base_ty: Ty<'tcx>, + proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>, + ) -> EvalResult<'tcx, Place> { + use mir::ProjectionElem::*; + let (ptr, extra) = match *proj_elem { + Field(field, _) => { + let layout = self.type_layout(base_ty)?; + return Ok(self.place_field(base, field, layout)?.0); + } + + Downcast(_, variant) => { + return self.place_downcast(base, variant); + } + + Deref => { + let val = self.read_place(base)?; + + let pointee_type = match base_ty.sty { + ty::TyRawPtr(ref tam) | + ty::TyRef(_, ref tam) => tam.ty, + ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(), + _ => bug!("can only deref pointer types"), + }; + + trace!("deref to {} on {:?}", pointee_type, val); + + return self.val_to_place(val, pointee_type); + } + + Index(local) => { + let value = self.frame().get_local(local)?; + let ty = self.tcx.types.usize; + let n = self.value_to_primval(ValTy { value, ty })?.to_u64()?; + return self.place_index(base, base_ty, n); + } + + ConstantIndex { + offset, + min_length, + from_end, + } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_extra_aligned(); + + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty)?.expect( + "sequence element must be sized", + ); + assert!(n >= min_length as u64); + + let index = if from_end { + n - u64::from(offset) + } else { + u64::from(offset) + }; + + let ptr = base_ptr.offset(index * elem_size, &self)?; + (ptr, PlaceExtra::None) + } + + Subslice { from, to } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_extra_aligned(); + + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty)?.expect( + "slice element must be sized", + ); + assert!(u64::from(from) <= n - u64::from(to)); + let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?; + // sublicing arrays produces arrays + let extra = if self.type_is_sized(base_ty) { + PlaceExtra::None + } else { + PlaceExtra::Length(n - u64::from(to) - u64::from(from)) + }; + (ptr, extra) + } + }; + + Ok(Place::Ptr { ptr, extra }) + } + + pub fn place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { + self.monomorphize( + place.ty(self.mir(), self.tcx).to_ty(self.tcx), + self.substs(), + ) + } +} diff --git a/src/librustc/mir/interpret/step.rs b/src/librustc/mir/interpret/step.rs index f6dbec91cce54..b5936c7e3b6ab 100644 --- a/src/librustc/mir/interpret/step.rs +++ b/src/librustc/mir/interpret/step.rs @@ -2,18 +2,15 @@ //! //! The main entry point is the `step` method. -use hir::def_id::DefId; use hir; -use mir::visit::{Visitor, LvalueContext}; +use mir::visit::{Visitor, PlaceContext}; use mir; -use traits::Reveal; -use ty; -use ty::layout::Layout; +use ty::{self, Instance}; use ty::subst::Substs; use middle::const_val::ConstVal; -use super::{EvalResult, EvalContext, StackPopCleanup, PtrAndAlign, GlobalId, Lvalue, - MemoryKind, Machine, PrimVal}; +use super::{EvalResult, EvalContext, StackPopCleanup, PtrAndAlign, GlobalId, Place, + Machine, EvalErrorKind}; use syntax::codemap::Span; use syntax::ast::Mutability; @@ -40,14 +37,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let mir = self.mir(); let basic_block = &mir.basic_blocks()[block]; + let old_frames = self.cur_frame(); + if let Some(stmt) = basic_block.statements.get(stmt_id) { - let mut new = Ok(0); + let mut new = Ok(false); ConstantExtractor { span: stmt.source_info.span, instance: self.frame().instance, ecx: self, mir, - new_constants: &mut new, + new_constant: &mut new, }.visit_statement( block, stmt, @@ -56,22 +55,23 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { statement_index: stmt_id, }, ); - // if ConstantExtractor added new frames, we don't execute anything here + // if ConstantExtractor added a new frame, we don't execute anything here // but await the next call to step - if new? == 0 { + if !new? { + assert_eq!(old_frames, self.cur_frame()); self.statement(stmt)?; } return Ok(true); } let terminator = basic_block.terminator(); - let mut new = Ok(0); + let mut new = Ok(false); ConstantExtractor { span: terminator.source_info.span, instance: self.frame().instance, ecx: self, mir, - new_constants: &mut new, + new_constant: &mut new, }.visit_terminator( block, terminator, @@ -80,9 +80,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { statement_index: stmt_id, }, ); - // if ConstantExtractor added new frames, we don't execute anything here + // if ConstantExtractor added a new frame, we don't execute anything here // but await the next call to step - if new? == 0 { + if !new? { + assert_eq!(old_frames, self.cur_frame()); self.terminator(terminator)?; } Ok(true) @@ -98,57 +99,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let frame_idx = self.cur_frame(); match stmt.kind { - Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, + Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?, SetDiscriminant { - ref lvalue, + ref place, variant_index, } => { - let dest = self.eval_lvalue(lvalue)?; - let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty)?; - - match *dest_layout { - Layout::General { discr, .. } => { - let discr_size = discr.size().bytes(); - let dest_ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.write_primval( - dest_ptr, - PrimVal::Bytes(variant_index as u128), - discr_size, - false - )? - } - - Layout::RawNullablePointer { nndiscr, .. } => { - if variant_index as u64 != nndiscr { - self.write_null(dest, dest_ty)?; - } - } - - Layout::StructWrappedNullablePointer { - nndiscr, - ref discrfield_source, - .. - } => { - if variant_index as u64 != nndiscr { - self.write_struct_wrapped_null_pointer( - dest_ty, - nndiscr, - discrfield_source, - dest, - )?; - } - } - - _ => { - bug!( - "SetDiscriminant on {} represented as {:#?}", - dest_ty, - dest_layout - ) - } - } + let dest = self.eval_place(place)?; + let dest_ty = self.place_ty(place); + self.write_discriminant_value(dest_ty, dest, variant_index)?; } // Mark locals as alive @@ -164,8 +123,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } // Validity checks. - Validate(op, ref lvalues) => { - for operand in lvalues { + Validate(op, ref places) => { + for operand in places { self.validation_op(op, operand)?; } } @@ -196,44 +155,41 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { /// returns `true` if a stackframe was pushed fn global_item( &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, + instance: Instance<'tcx>, span: Span, mutability: Mutability, + orig_substs: &'tcx Substs<'tcx>, ) -> EvalResult<'tcx, bool> { - let instance = self.resolve_associated_const(def_id, substs); + debug!("global_item: {:?}", instance); let cid = GlobalId { instance, promoted: None, }; - if self.globals.contains_key(&cid) { + if self.tcx.interpret_interner.borrow().get_cached(cid).is_some() { return Ok(false); } - if self.tcx.has_attr(def_id, "linkage") { + if self.tcx.has_attr(instance.def_id(), "linkage") { M::global_item_with_linkage(self, cid.instance, mutability)?; return Ok(false); } let mir = self.load_mir(instance.def)?; - let size = self.type_size_with_substs(mir.return_ty, substs)?.expect( - "unsized global", - ); - let align = self.type_align_with_substs(mir.return_ty, substs)?; + let layout = self.type_layout_with_substs(mir.return_ty(), orig_substs)?; + assert!(!layout.is_unsized()); let ptr = self.memory.allocate( - size, - align, - MemoryKind::UninitializedStatic, + layout.size.bytes(), + layout.align.abi(), + None, )?; - let aligned = !self.is_packed(mir.return_ty)?; - self.globals.insert( + self.tcx.interpret_interner.borrow_mut().cache( cid, PtrAndAlign { ptr: ptr.into(), - aligned, + aligned: !layout.is_packed(), }, ); - let internally_mutable = !mir.return_ty.is_freeze( + let internally_mutable = !layout.ty.is_freeze( self.tcx, - ty::ParamEnv::empty(Reveal::All), + M::param_env(self), span, ); let mutability = if mutability == Mutability::Mutable || internally_mutable { @@ -242,45 +198,37 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Mutability::Immutable }; let cleanup = StackPopCleanup::MarkStatic(mutability); - let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); trace!("pushing stack frame for global: {}", name); self.push_stack_frame( instance, span, mir, - Lvalue::from_ptr(ptr), + Place::from_ptr(ptr), cleanup, )?; Ok(true) } } -// WARNING: This code pushes new stack frames. Make sure that any methods implemented on this -// type don't ever access ecx.stack[ecx.cur_frame()], as that will change. This includes, e.g., -// using the current stack frame's substitution. -// Basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`. struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b, M: Machine<'tcx> + 'a> { span: Span, ecx: &'a mut EvalContext<'b, 'tcx, M>, mir: &'tcx mir::Mir<'tcx>, instance: ty::Instance<'tcx>, - new_constants: &'a mut EvalResult<'tcx, u64>, + // Whether a stackframe for a new constant has been pushed + new_constant: &'a mut EvalResult<'tcx, bool>, } impl<'a, 'b, 'tcx, M: Machine<'tcx>> ConstantExtractor<'a, 'b, 'tcx, M> { fn try EvalResult<'tcx, bool>>(&mut self, f: F) { - // previous constant errored - let n = match *self.new_constants { - Ok(n) => n, - Err(_) => return, - }; - match f(self) { - // everything ok + a new stackframe - Ok(true) => *self.new_constants = Ok(n + 1), - // constant correctly evaluated, but no new stackframe - Ok(false) => {} - // constant eval errored - Err(err) => *self.new_constants = Err(err), + match *self.new_constant { + // already computed a constant, don't do more than one per iteration + Ok(true) => {}, + // no constants computed yet + Ok(false) => *self.new_constant = f(self), + // error happened, abort the visitor traversing + Err(_) => {}, } } } @@ -288,47 +236,51 @@ impl<'a, 'b, 'tcx, M: Machine<'tcx>> ConstantExtractor<'a, 'b, 'tcx, M> { impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx, M> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) { self.super_constant(constant, location); - match constant.literal { - // already computed by rustc - mir::Literal::Value { value: &ty::Const { val: ConstVal::Unevaluated(def_id, substs), .. } } => { - self.try(|this| { - this.ecx.global_item( + self.try(|this| { + match constant.literal { + // already computed by rustc + mir::Literal::Value { value: &ty::Const { val: ConstVal::Unevaluated(def_id, substs), .. } } => { + debug!("global_item: {:?}, {:#?}", def_id, substs); + let substs = this.ecx.tcx.trans_apply_param_substs(this.instance.substs, &substs); + debug!("global_item_new_substs: {:#?}", substs); + debug!("global_item_param_env: {:#?}", M::param_env(this.ecx)); + let instance = Instance::resolve( + this.ecx.tcx, + M::param_env(this.ecx), def_id, substs, + ).ok_or(EvalErrorKind::TypeckError)?; // turn error prop into a panic to expose associated type in const issue + this.ecx.global_item( + instance, constant.span, Mutability::Immutable, + this.instance.substs, ) - }); - } - mir::Literal::Value { .. } => {} - mir::Literal::Promoted { index } => { - let cid = GlobalId { - instance: self.instance, - promoted: Some(index), - }; - if self.ecx.globals.contains_key(&cid) { - return; } - let mir = &self.mir.promoted[index]; - self.try(|this| { - let size = this.ecx - .type_size_with_substs(mir.return_ty, this.instance.substs)? - .expect("unsized global"); - let align = this.ecx.type_align_with_substs( - mir.return_ty, - this.instance.substs, - )?; + mir::Literal::Value { .. } => Ok(false), + mir::Literal::Promoted { index } => { + let cid = GlobalId { + instance: this.instance, + promoted: Some(index), + }; + if this.ecx.tcx.interpret_interner.borrow().get_cached(cid).is_some() { + return Ok(false); + } + let mir = &this.mir.promoted[index]; + let layout = this.ecx.type_layout_with_substs( + mir.return_ty(), + this.instance.substs)?; + assert!(!layout.is_unsized()); let ptr = this.ecx.memory.allocate( - size, - align, - MemoryKind::UninitializedStatic, + layout.size.bytes(), + layout.align.abi(), + None, )?; - let aligned = !this.ecx.is_packed(mir.return_ty)?; - this.ecx.globals.insert( + this.ecx.tcx.interpret_interner.borrow_mut().cache( cid, PtrAndAlign { ptr: ptr.into(), - aligned, + aligned: !layout.is_packed(), }, ); trace!("pushing stack frame for {:?}", index); @@ -336,67 +288,67 @@ impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b, this.instance, constant.span, mir, - Lvalue::from_ptr(ptr), + Place::from_ptr(ptr), StackPopCleanup::MarkStatic(Mutability::Immutable), )?; Ok(true) - }); + } } - } + }); } - fn visit_lvalue( + fn visit_place( &mut self, - lvalue: &mir::Lvalue<'tcx>, - context: LvalueContext<'tcx>, + place: &mir::Place<'tcx>, + context: PlaceContext<'tcx>, location: mir::Location, ) { - self.super_lvalue(lvalue, context, location); - if let mir::Lvalue::Static(ref static_) = *lvalue { - let def_id = static_.def_id; - let substs = self.ecx.tcx.intern_substs(&[]); - let span = self.span; - if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) { - if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { - if let hir::ItemStatic(_, m, _) = *node { - self.try(|this| { + self.super_place(place, context, location); + self.try(|this| { + if let mir::Place::Static(ref static_) = *place { + let def_id = static_.def_id; + let span = this.span; + if let Some(node_item) = this.ecx.tcx.hir.get_if_local(def_id) { + if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { + if let hir::ItemStatic(_, m, _) = *node { + let instance = Instance::mono(this.ecx.tcx, def_id); this.ecx.global_item( - def_id, - substs, + instance, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable }, + this.instance.substs, ) - }); - return; + } else { + bug!("static def id doesn't point to static"); + } } else { - bug!("static def id doesn't point to static"); + bug!("static def id doesn't point to item"); } } else { - bug!("static def id doesn't point to item"); - } - } else { - let def = self.ecx.tcx.describe_def(def_id).expect("static not found"); - if let hir::def::Def::Static(_, mutable) = def { - self.try(|this| { + let def = this.ecx.tcx.describe_def(def_id).expect("static not found"); + if let hir::def::Def::Static(_, mutable) = def { + let instance = Instance::mono(this.ecx.tcx, def_id); this.ecx.global_item( - def_id, - substs, + instance, span, if mutable { Mutability::Mutable } else { Mutability::Immutable }, + this.instance.substs, ) - }); - } else { - bug!("static found but isn't a static: {:?}", def); + } else { + bug!("static found but isn't a static: {:?}", def); + } } + } else { + Ok(false) } - } + }); } } diff --git a/src/librustc/mir/interpret/terminator/drop.rs b/src/librustc/mir/interpret/terminator/drop.rs index 4cb1ad77474c8..c24f18d42b0c3 100644 --- a/src/librustc/mir/interpret/terminator/drop.rs +++ b/src/librustc/mir/interpret/terminator/drop.rs @@ -2,34 +2,34 @@ use mir::BasicBlock; use ty::{self, Ty}; use syntax::codemap::Span; -use mir::interpret::{EvalResult, EvalContext, Lvalue, LvalueExtra, PrimVal, Value, +use mir::interpret::{EvalResult, EvalContext, Place, PlaceExtra, PrimVal, Value, Machine, ValTy}; impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { - pub(crate) fn drop_lvalue( + pub(crate) fn drop_place( &mut self, - lval: Lvalue, + place: Place, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span, target: BasicBlock, ) -> EvalResult<'tcx> { - trace!("drop_lvalue: {:#?}", lval); + trace!("drop_place: {:#?}", place); // We take the address of the object. This may well be unaligned, which is fine for us here. // However, unaligned accesses will probably make the actual drop implementation fail -- a problem shared // by rustc. - let val = match self.force_allocation(lval)? { - Lvalue::Ptr { + let val = match self.force_allocation(place)? { + Place::Ptr { ptr, - extra: LvalueExtra::Vtable(vtable), + extra: PlaceExtra::Vtable(vtable), } => ptr.ptr.to_value_with_vtable(vtable), - Lvalue::Ptr { + Place::Ptr { ptr, - extra: LvalueExtra::Length(len), + extra: PlaceExtra::Length(len), } => ptr.ptr.to_value_with_len(len), - Lvalue::Ptr { + Place::Ptr { ptr, - extra: LvalueExtra::None, + extra: PlaceExtra::None, } => ptr.ptr.to_value(), _ => bug!("force_allocation broken"), }; @@ -74,7 +74,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.eval_fn_call( instance, - Some((Lvalue::undef(), target)), + Some((Place::undef(), target)), &vec![valty], span, fn_sig, diff --git a/src/librustc/mir/interpret/terminator/mod.rs b/src/librustc/mir/interpret/terminator/mod.rs index 6402db7934f2d..bd7dfc0b36722 100644 --- a/src/librustc/mir/interpret/terminator/mod.rs +++ b/src/librustc/mir/interpret/terminator/mod.rs @@ -1,11 +1,10 @@ use mir; use ty::{self, TypeVariants}; -use ty::layout::Layout; use syntax::codemap::Span; use syntax::abi::Abi; use super::{EvalResult, EvalContext, eval_context, - PtrAndAlign, Lvalue, PrimVal, Value, Machine, ValTy}; + PtrAndAlign, Place, PrimVal, Value, Machine, ValTy}; use rustc_data_structures::indexed_vec::Idx; @@ -24,7 +23,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { use mir::TerminatorKind::*; match terminator.kind { Return => { - self.dump_local(self.frame().return_lvalue); + self.dump_local(self.frame().return_place); self.pop_stack_frame()? } @@ -61,7 +60,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { .. } => { let destination = match *destination { - Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)), + Some((ref lv, target)) => Some((self.eval_place(lv)?, target)), None => None, }; @@ -86,7 +85,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { (instance, sig) } ty::TyFnDef(def_id, substs) => ( - eval_context::resolve(self.tcx, def_id, substs), + self.resolve(def_id, substs)?, func_ty.fn_sig(self.tcx), ), _ => { @@ -111,14 +110,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { .. } => { // FIXME(CTFE): forbid drop in const eval - let lval = self.eval_lvalue(location)?; - let ty = self.lvalue_ty(location); - let ty = eval_context::apply_param_substs(self.tcx, self.substs(), &ty); + let place = self.eval_place(location)?; + let ty = self.place_ty(location); + let ty = self.tcx.trans_apply_param_substs(self.substs(), &ty); trace!("TerminatorKind::drop: {:?}, type {}", location, ty); let instance = eval_context::resolve_drop_in_place(self.tcx, ty); - self.drop_lvalue( - lval, + self.drop_place( + place, instance, ty, terminator.source_info.span, @@ -162,6 +161,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { GeneratorDrop => unimplemented!(), DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), + FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), Unreachable => return err!(Unreachable), } @@ -214,9 +214,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => { // First argument of real_sig must be a ZST let fst_ty = real_sig.inputs_and_output[0]; - let layout = self.type_layout(fst_ty)?; - let size = layout.size(&self.tcx.data_layout).bytes(); - if size == 0 { + if self.type_layout(fst_ty)?.is_zst() { // Second argument must be a tuple matching the argument list of sig let snd_ty = real_sig.inputs_and_output[1]; match snd_ty.sty { @@ -238,7 +236,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { fn eval_fn_call( &mut self, instance: ty::Instance<'tcx>, - destination: Option<(Lvalue, mir::BasicBlock)>, + destination: Option<(Place, mir::BasicBlock)>, args: &[ValTy<'tcx>], span: Span, sig: ty::FnSig<'tcx>, @@ -252,7 +250,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { }; let ty = sig.output(); let layout = self.type_layout(ty)?; - M::call_intrinsic(self, instance, args, ret, ty, layout, target)?; + M::call_intrinsic(self, instance, args, ret, layout, target)?; self.dump_local(ret); Ok(()) } @@ -266,7 +264,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // closure as closure once Abi::RustCall => { for (arg_local, &valty) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let dest = self.eval_place(&mir::Place::Local(arg_local))?; self.write_value(valty, dest)?; } } @@ -281,7 +279,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { trace!("args: {:?}", args); let local = arg_locals.nth(1).unwrap(); for (i, &valty) in args.into_iter().enumerate() { - let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field( + let dest = self.eval_place(&mir::Place::Local(local).field( mir::Field::new(i), valty.ty, ))?; @@ -316,52 +314,59 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { { // write first argument let first_local = arg_locals.next().unwrap(); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; + let dest = self.eval_place(&mir::Place::Local(first_local))?; self.write_value(args[0], dest)?; } // unpack and write all other args let layout = self.type_layout(args[1].ty)?; - if let (&ty::TyTuple(fields, _), - &Layout::Univariant { ref variant, .. }) = (&args[1].ty.sty, layout) - { - trace!("fields: {:?}", fields); - if self.frame().mir.args_iter().count() == fields.len() + 1 { - let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let ty::TyTuple(..) = args[1].ty.sty { + if self.frame().mir.args_iter().count() == layout.fields.count() + 1 { match args[1].value { Value::ByRef(PtrAndAlign { ptr, aligned }) => { assert!( aligned, "Unaligned ByRef-values cannot occur as function arguments" ); - for ((offset, ty), arg_local) in - offsets.zip(fields).zip(arg_locals) - { + for (i, arg_local) in arg_locals.enumerate() { + let field = layout.field(&self, i)?; + let offset = layout.fields.offset(i).bytes(); let arg = Value::by_ref(ptr.offset(offset, &self)?); let dest = - self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.eval_place(&mir::Place::Local(arg_local))?; trace!( "writing arg {:?} to {:?} (type: {})", arg, dest, - ty + field.ty ); let valty = ValTy { value: arg, - ty, + ty: field.ty, }; self.write_value(valty, dest)?; } } Value::ByVal(PrimVal::Undef) => {} other => { - assert_eq!(fields.len(), 1); - let dest = self.eval_lvalue(&mir::Lvalue::Local( + trace!("{:#?}, {:#?}", other, layout); + let mut layout = layout; + 'outer: loop { + for i in 0..layout.fields.count() { + let field = layout.field(&self, i)?; + if layout.fields.offset(i).bytes() == 0 && layout.size == field.size { + layout = field; + continue 'outer; + } + } + break; + } + let dest = self.eval_place(&mir::Place::Local( arg_locals.next().unwrap(), ))?; let valty = ValTy { value: other, - ty: fields[0], + ty: layout.ty, }; self.write_value(valty, dest)?; } @@ -369,8 +374,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } else { trace!("manual impl of rust-call ABI"); // called a manual impl of a rust-call function - let dest = self.eval_lvalue( - &mir::Lvalue::Local(arg_locals.next().unwrap()), + let dest = self.eval_place( + &mir::Place::Local(arg_locals.next().unwrap()), )?; self.write_value(args[1], dest)?; } @@ -384,7 +389,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } _ => { for (arg_local, &valty) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let dest = self.eval_place(&mir::Place::Local(arg_local))?; self.write_value(valty, dest)?; } } diff --git a/src/librustc/mir/interpret/traits.rs b/src/librustc/mir/interpret/traits.rs index a884bd7472e12..215148b58b1ef 100644 --- a/src/librustc/mir/interpret/traits.rs +++ b/src/librustc/mir/interpret/traits.rs @@ -1,40 +1,11 @@ -use traits::{self, Reveal}; -use hir::def_id::DefId; -use ty::subst::Substs; use ty::{self, Ty}; -use syntax::codemap::DUMMY_SP; -use syntax::ast::{self, Mutability}; +use ty::layout::{Size, Align}; +use syntax::ast::Mutability; -use super::{EvalResult, EvalContext, eval_context, MemoryPointer, MemoryKind, Value, PrimVal, +use super::{EvalResult, EvalContext, eval_context, MemoryPointer, Value, PrimVal, Machine}; impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { - pub(crate) fn fulfill_obligation( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> traits::Vtable<'tcx, ()> { - // Do the initial selection for the obligation. This yields the shallow result we are - // looking for -- that is, what specific impl. - self.tcx.infer_ctxt().enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - ty::ParamEnv::empty(Reveal::All), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // Currently, we use a fulfillment context to completely resolve all nested obligations. - // This is because they can inform the inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) - }) - } - /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -54,11 +25,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let align = self.type_align(trait_ref.self_ty())?; let ptr_size = self.memory.pointer_size(); - let methods = ::traits::get_vtable_methods(self.tcx, trait_ref); + let methods = self.tcx.vtable_methods(trait_ref); let vtable = self.memory.allocate( - ptr_size * (3 + methods.count() as u64), + ptr_size * (3 + methods.len() as u64), ptr_size, - MemoryKind::UninitializedStatic, + None, )?; let drop = eval_context::resolve_drop_in_place(self.tcx, ty); @@ -70,9 +41,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let align_ptr = vtable.offset(ptr_size * 2, &self)?; self.memory.write_ptr_sized_unsigned(align_ptr, PrimVal::Bytes(align as u128))?; - for (i, method) in ::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { - if let Some((def_id, substs)) = method { - let instance = eval_context::resolve(self.tcx, def_id, substs); + for (i, method) in methods.iter().enumerate() { + if let Some((def_id, substs)) = *method { + let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance); let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?; self.memory.write_ptr_sized_unsigned(method_ptr, PrimVal::Ptr(fn_ptr))?; @@ -103,35 +74,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub fn read_size_and_align_from_vtable( &self, vtable: MemoryPointer, - ) -> EvalResult<'tcx, (u64, u64)> { + ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_ptr_sized_unsigned(vtable.offset(pointer_size, self)?)?.to_bytes()? as u64; let align = self.memory.read_ptr_sized_unsigned( vtable.offset(pointer_size * 2, self)? )?.to_bytes()? as u64; - Ok((size, align)) - } - - pub(crate) fn resolve_associated_const( - &self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - ) -> ty::Instance<'tcx> { - if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); - let vtable = self.fulfill_obligation(trait_ref); - if let traits::VtableImpl(vtable_impl) = vtable { - let name = self.tcx.item_name(def_id); - let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id).find( - |item| { - item.kind == ty::AssociatedKind::Const && item.name == name - }, - ); - if let Some(assoc_const) = assoc_const_opt { - return ty::Instance::new(assoc_const.def_id, vtable_impl.substs); - } - } - } - ty::Instance::new(def_id, substs) + Ok((Size::from_bytes(size), Align::from_bytes(align, align).unwrap())) } } diff --git a/src/librustc/mir/interpret/validation.rs b/src/librustc/mir/interpret/validation.rs index 3f1d7a644caf9..086fb957abbda 100644 --- a/src/librustc/mir/interpret/validation.rs +++ b/src/librustc/mir/interpret/validation.rs @@ -10,9 +10,9 @@ use middle::region; use rustc_data_structures::indexed_vec::Idx; use super::{EvalError, EvalResult, EvalErrorKind, EvalContext, DynamicLifetime, AccessKind, Value, - Lvalue, LvalueExtra, Machine, ValTy}; + Place, PlaceExtra, Machine, ValTy}; -pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsLvalue<'tcx>, Lvalue)>; +pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>; #[derive(Copy, Clone, Debug, PartialEq)] enum ValidationMode { @@ -32,36 +32,36 @@ impl ValidationMode { } } -// Abstract lvalues +// Abstract places #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum AbsLvalue<'tcx> { +pub enum AbsPlace<'tcx> { Local(mir::Local), Static(hir::def_id::DefId), - Projection(Box>), + Projection(Box>), } -type AbsLvalueProjection<'tcx> = mir::Projection<'tcx, AbsLvalue<'tcx>, u64, ()>; -type AbsLvalueElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>; +type AbsPlaceProjection<'tcx> = mir::Projection<'tcx, AbsPlace<'tcx>, u64, ()>; +type AbsPlaceElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>; -impl<'tcx> AbsLvalue<'tcx> { - pub fn field(self, f: mir::Field) -> AbsLvalue<'tcx> { +impl<'tcx> AbsPlace<'tcx> { + pub fn field(self, f: mir::Field) -> AbsPlace<'tcx> { self.elem(mir::ProjectionElem::Field(f, ())) } - pub fn deref(self) -> AbsLvalue<'tcx> { + pub fn deref(self) -> AbsPlace<'tcx> { self.elem(mir::ProjectionElem::Deref) } - pub fn downcast(self, adt_def: &'tcx ty::AdtDef, variant_index: usize) -> AbsLvalue<'tcx> { + pub fn downcast(self, adt_def: &'tcx ty::AdtDef, variant_index: usize) -> AbsPlace<'tcx> { self.elem(mir::ProjectionElem::Downcast(adt_def, variant_index)) } - pub fn index(self, index: u64) -> AbsLvalue<'tcx> { + pub fn index(self, index: u64) -> AbsPlace<'tcx> { self.elem(mir::ProjectionElem::Index(index)) } - fn elem(self, elem: AbsLvalueElem<'tcx>) -> AbsLvalue<'tcx> { - AbsLvalue::Projection(Box::new(AbsLvalueProjection { + fn elem(self, elem: AbsPlaceElem<'tcx>) -> AbsPlace<'tcx> { + AbsPlace::Projection(Box::new(AbsPlaceProjection { base: self, elem, })) @@ -69,7 +69,7 @@ impl<'tcx> AbsLvalue<'tcx> { } impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { - fn abstract_lvalue_projection(&self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, AbsLvalueProjection<'tcx>> { + fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>> { use self::mir::ProjectionElem::*; let elem = match proj.elem { @@ -87,18 +87,18 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Subslice { from, to }, Downcast(adt, sz) => Downcast(adt, sz), }; - Ok(AbsLvalueProjection { - base: self.abstract_lvalue(&proj.base)?, + Ok(AbsPlaceProjection { + base: self.abstract_place(&proj.base)?, elem }) } - fn abstract_lvalue(&self, lval: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, AbsLvalue<'tcx>> { - Ok(match lval { - &mir::Lvalue::Local(l) => AbsLvalue::Local(l), - &mir::Lvalue::Static(ref s) => AbsLvalue::Static(s.def_id), - &mir::Lvalue::Projection(ref p) => - AbsLvalue::Projection(Box::new(self.abstract_lvalue_projection(&*p)?)), + fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>> { + Ok(match place { + &mir::Place::Local(l) => AbsPlace::Local(l), + &mir::Place::Static(ref s) => AbsPlace::Static(s.def_id), + &mir::Place::Projection(ref p) => + AbsPlace::Projection(Box::new(self.abstract_place_projection(&*p)?)), }) } @@ -106,7 +106,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { pub(crate) fn validation_op( &mut self, op: ValidationOp, - operand: &ValidationOperand<'tcx, mir::Lvalue<'tcx>>, + operand: &ValidationOperand<'tcx, mir::Place<'tcx>>, ) -> EvalResult<'tcx> { // If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands // because other crates may have been compiled with mir-emit-validate > 0. Ignore those @@ -146,11 +146,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } // We need to monomorphize ty *without* erasing lifetimes + trace!("validation_op1: {:?}", operand.ty.sty); let ty = operand.ty.subst(self.tcx, self.substs()); - let lval = self.eval_lvalue(&operand.lval)?; - let abs_lval = self.abstract_lvalue(&operand.lval)?; + trace!("validation_op2: {:?}", operand.ty.sty); + let place = self.eval_place(&operand.place)?; + let abs_place = self.abstract_place(&operand.place)?; let query = ValidationQuery { - lval: (abs_lval, lval), + place: (abs_place, place), ty, re: operand.re, mutbl: operand.mutbl, @@ -184,7 +186,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.locks_lifetime_ended(scope); match scope { Some(scope) => { - // Recover suspended lvals + // Recover suspended places let lft = DynamicLifetime { frame: self.cur_frame(), region: Some(scope), @@ -326,34 +328,139 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } - fn validate_variant( + // This is a copy of `Layout::field` + // + // FIXME: remove once validation does not depend on lifetimes + fn field_with_lifetimes( + &mut self, + base: Place, + mut layout: ty::layout::TyLayout<'tcx>, + i: usize, + ) -> EvalResult<'tcx, ty::Ty<'tcx>> { + match base { + Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => { + layout = layout.for_variant(&self, variant_index); + } + _ => {} + } + let tcx = self.tcx; + Ok(match layout.ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) | + ty::TyFnPtr(_) | + ty::TyNever | + ty::TyFnDef(..) | + ty::TyDynamic(..) | + ty::TyForeign(..) => { + bug!("TyLayout::field_type({:?}): not applicable", layout) + } + + // Potentially-fat pointers. + ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < 2); + + // Reuse the fat *T type as its own thin pointer data field. + // This provides information about e.g. DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldPlacement` is checked by users. + if i == 0 { + return Ok(layout.ty); + } + + match tcx.struct_tail(pointee).sty { + ty::TySlice(_) | + ty::TyStr => tcx.types.usize, + ty::TyDynamic(..) => { + // FIXME(eddyb) use an usize/fn() array with + // the correct number of vtables slots. + tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil()) + } + _ => bug!("TyLayout::field_type({:?}): not applicable", layout) + } + } + + // Arrays and slices. + ty::TyArray(element, _) | + ty::TySlice(element) => element, + ty::TyStr => tcx.types.u8, + + // Tuples, generators and closures. + ty::TyClosure(def_id, ref substs) => { + substs.upvar_tys(def_id, tcx).nth(i).unwrap() + } + + ty::TyGenerator(def_id, ref substs, _) => { + substs.field_tys(def_id, tcx).nth(i).unwrap() + } + + ty::TyTuple(tys, _) => tys[i], + + // SIMD vector types. + ty::TyAdt(def, ..) if def.repr.simd() => { + layout.ty.simd_type(tcx) + } + + // ADTs. + ty::TyAdt(def, substs) => { + use ty::layout::Variants; + match layout.variants { + Variants::Single { index } => { + def.variants[index].fields[i].ty(tcx, substs) + } + + // Discriminant field for enums (where applicable). + Variants::Tagged { ref discr, .. } | + Variants::NicheFilling { niche: ref discr, .. } => { + assert_eq!(i, 0); + return Ok(discr.value.to_ty(tcx)) + } + } + } + + ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) | + ty::TyInfer(_) | ty::TyError => { + bug!("TyLayout::field_type: unexpected type `{}`", layout.ty) + } + }) + } + + fn validate_fields( &mut self, query: ValidationQuery<'tcx>, - variant: &ty::VariantDef, - subst: &ty::subst::Substs<'tcx>, mode: ValidationMode, ) -> EvalResult<'tcx> { + let mut layout = self.type_layout(query.ty)?; + layout.ty = query.ty; + // TODO: Maybe take visibility/privacy into account. - for (idx, field_def) in variant.fields.iter().enumerate() { - let field_ty = field_def.ty(self.tcx, subst); + for idx in 0..layout.fields.count() { let field = mir::Field::new(idx); - let field_lvalue = self.lvalue_field(query.lval.1, field, query.ty, field_ty)?; + let (field_place, field_layout) = + self.place_field(query.place.1, field, layout)?; + // layout stuff erases lifetimes, get the field ourselves + let field_ty = self.field_with_lifetimes(query.place.1, layout, idx)?; + trace!("assuming \n{:?}\n == \n{:?}\n except for lifetimes", field_layout.ty, field_ty); self.validate( ValidationQuery { - lval: (query.lval.0.clone().field(field), field_lvalue), + place: (query.place.0.clone().field(field), field_place), ty: field_ty, ..query }, mode, )?; } + Ok(()) } fn validate_ptr( &mut self, val: Value, - abs_lval: AbsLvalue<'tcx>, + abs_place: AbsPlace<'tcx>, pointee_ty: Ty<'tcx>, re: Option, mutbl: Mutability, @@ -362,13 +469,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Check alignment and non-NULLness let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?; let ptr = val.into_ptr(&self.memory)?; - self.memory.check_align(ptr, align, None)?; + self.memory.check_align(ptr, align.abi(), None)?; // Recurse - let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?; + let pointee_place = self.val_to_place(val, pointee_ty)?; self.validate( ValidationQuery { - lval: (abs_lval.deref(), pointee_lvalue), + place: (abs_place.deref(), pointee_place), ty: pointee_ty, re, mutbl, @@ -377,7 +484,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ) } - /// Validate the lvalue at the given type. If `acquire` is false, just do a release of all write locks + /// Validate the place at the given type. If `acquire` is false, just do a release of all write locks fn validate( &mut self, mut query: ValidationQuery<'tcx>, @@ -399,7 +506,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } query.ty = self.normalize_type_unerased(&query.ty); - trace!("{:?} on {:?}", mode, query); + trace!("{:?} on {:#?}", mode, query); + trace!("{:#?}", query.ty.sty); // Decide whether this type *owns* the memory it covers (like integers), or whether it // just assembles pieces (that each own their memory) together to a larger whole. @@ -409,7 +517,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true, TyAdt(adt, _) if adt.is_box() => true, TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) | - TyDynamic(..) | TyGenerator(..) => false, + TyDynamic(..) | TyGenerator(..) | TyForeign(_) => false, TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => { bug!("I got an incomplete/unnormalized type for validation") } @@ -419,12 +527,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // Tracking the same state for locals not backed by memory would just duplicate too // much machinery. // FIXME: We ignore alignment. - let (ptr, extra) = self.force_allocation(query.lval.1)?.to_ptr_extra_aligned(); + let (ptr, extra) = self.force_allocation(query.place.1)?.to_ptr_extra_aligned(); // Determine the size - // FIXME: Can we reuse size_and_align_of_dst for Lvalues? + // FIXME: Can we reuse size_and_align_of_dst for Places? let len = match self.type_size(query.ty)? { Some(size) => { - assert_eq!(extra, LvalueExtra::None, "Got a fat ptr to a sized type"); + assert_eq!(extra, PlaceExtra::None, "Got a fat ptr to a sized type"); size } None => { @@ -436,7 +544,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ); // The extra must be the length, in bytes. match extra { - LvalueExtra::Length(len) => len, + PlaceExtra::Length(len) => len, _ => bug!("TyStr must have a length as extra"), } } @@ -470,7 +578,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.recover_write_lock( ptr, len, - &query.lval.0, + &query.place.0, query.re, ending_ce, )? @@ -479,7 +587,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { self.memory.suspend_write_lock( ptr, len, - &query.lval.0, + &query.place.0, suspended_ce, )? } @@ -494,7 +602,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { TyInt(_) | TyUint(_) | TyRawPtr(_) => { if mode.acquiring() { // Make sure we can read this. - let val = self.read_lvalue(query.lval.1)?; + let val = self.read_place(query.place.1)?; self.follow_by_ref_value(val, query.ty)?; // FIXME: It would be great to rule out Undef here, but that doesn't actually work. // Passing around undef data is a thing that e.g. Vec::extend_with does. @@ -503,7 +611,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } TyBool | TyFloat(_) | TyChar => { if mode.acquiring() { - let val = self.read_lvalue(query.lval.1)?; + let val = self.read_place(query.place.1)?; let val = self.value_to_primval(ValTy { value: val, ty: query.ty })?; val.to_bytes()?; // TODO: Check if these are valid bool/float/codepoint/UTF-8 @@ -516,7 +624,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ty: pointee_ty, mutbl, }) => { - let val = self.read_lvalue(query.lval.1)?; + let val = self.read_place(query.place.1)?; // Sharing restricts our context if mutbl == MutImmutable { query.mutbl = MutImmutable; @@ -531,14 +639,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { _ => {} } } - self.validate_ptr(val, query.lval.0, pointee_ty, query.re, query.mutbl, mode) + self.validate_ptr(val, query.place.0, pointee_ty, query.re, query.mutbl, mode) } TyAdt(adt, _) if adt.is_box() => { - let val = self.read_lvalue(query.lval.1)?; - self.validate_ptr(val, query.lval.0, query.ty.boxed_ty(), query.re, query.mutbl, mode) + let val = self.read_place(query.place.1)?; + self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode) } TyFnPtr(_sig) => { - let ptr = self.read_lvalue(query.lval.1)? + let ptr = self.read_place(query.place.1)? .into_ptr(&self.memory)? .to_ptr()?; self.memory.get_fn(ptr)?; @@ -557,20 +665,20 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok(()) } TySlice(elem_ty) => { - let len = match query.lval.1 { - Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len, + let len = match query.place.1 { + Place::Ptr { extra: PlaceExtra::Length(len), .. } => len, _ => { bug!( - "acquire_valid of a TySlice given non-slice lvalue: {:?}", - query.lval + "acquire_valid of a TySlice given non-slice place: {:?}", + query.place ) } }; for i in 0..len { - let inner_lvalue = self.lvalue_index(query.lval.1, query.ty, i)?; + let inner_place = self.place_index(query.place.1, query.ty, i)?; self.validate( ValidationQuery { - lval: (query.lval.0.clone().index(i), inner_lvalue), + place: (query.place.0.clone().index(i), inner_place), ty: elem_ty, ..query }, @@ -582,10 +690,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { TyArray(elem_ty, len) => { let len = len.val.to_const_int().unwrap().to_u64().unwrap(); for i in 0..len { - let inner_lvalue = self.lvalue_index(query.lval.1, query.ty, i as u64)?; + let inner_place = self.place_index(query.place.1, query.ty, i as u64)?; self.validate( ValidationQuery { - lval: (query.lval.0.clone().index(i as u64), inner_lvalue), + place: (query.place.0.clone().index(i as u64), inner_place), ty: elem_ty, ..query }, @@ -596,12 +704,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } TyDynamic(_data, _region) => { // Check that this is a valid vtable - let vtable = match query.lval.1 { - Lvalue::Ptr { extra: LvalueExtra::Vtable(vtable), .. } => vtable, + let vtable = match query.place.1 { + Place::Ptr { extra: PlaceExtra::Vtable(vtable), .. } => vtable, _ => { bug!( - "acquire_valid of a TyDynamic given non-trait-object lvalue: {:?}", - query.lval + "acquire_valid of a TyDynamic given non-trait-object place: {:?}", + query.place ) } }; @@ -613,7 +721,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { // their return values. So, it doesn't seem like there's anything else to do. Ok(()) } - TyAdt(adt, subst) => { + TyAdt(adt, _) => { if Some(adt.did) == self.tcx.lang_items().unsafe_cell_type() && query.mutbl == MutImmutable { @@ -623,9 +731,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { match adt.adt_kind() { AdtKind::Enum => { - // TODO: Can we get the discriminant without forcing an allocation? - let ptr = self.force_allocation(query.lval.1)?.to_ptr()?; - let discr = self.read_discriminant_value(ptr, query.ty)?; + let discr = self.read_discriminant_value(query.place.1, query.ty)?; // Get variant index for discriminant let variant_idx = adt.discriminants(self.tcx).position(|variant_discr| { @@ -639,24 +745,22 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { if variant.fields.len() > 0 { // Downcast to this variant, if needed - let lval = if adt.variants.len() > 1 { + let place = if adt.is_enum() { ( - query.lval.0.downcast(adt, variant_idx), - self.eval_lvalue_projection( - query.lval.1, + query.place.0.downcast(adt, variant_idx), + self.eval_place_projection( + query.place.1, query.ty, &mir::ProjectionElem::Downcast(adt, variant_idx), )?, ) } else { - query.lval + query.place }; // Recursively validate the fields - self.validate_variant( - ValidationQuery { lval, ..query }, - variant, - subst, + self.validate_fields( + ValidationQuery { place, ..query }, mode, ) } else { @@ -665,7 +769,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } AdtKind::Struct => { - self.validate_variant(query, adt.struct_variant(), subst, mode) + self.validate_fields(query, mode) } AdtKind::Union => { // No guarantees are provided for union types. @@ -674,37 +778,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } } } - TyTuple(ref types, _) => { - for (idx, field_ty) in types.iter().enumerate() { - let field = mir::Field::new(idx); - let field_lvalue = self.lvalue_field(query.lval.1, field, query.ty, field_ty)?; - self.validate( - ValidationQuery { - lval: (query.lval.0.clone().field(field), field_lvalue), - ty: field_ty, - ..query - }, - mode, - )?; - } - Ok(()) - } - TyClosure(def_id, ref closure_substs) => { - for (idx, field_ty) in closure_substs.upvar_tys(def_id, self.tcx).enumerate() { - let field = mir::Field::new(idx); - let field_lvalue = self.lvalue_field(query.lval.1, field, query.ty, field_ty)?; - self.validate( - ValidationQuery { - lval: (query.lval.0.clone().field(field), field_lvalue), - ty: field_ty, - ..query - }, - mode, - )?; - } - // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?). + TyTuple(..) | + TyClosure(..) => { + // TODO: Check if the signature matches for `TyClosure` + // (should be the same check as what terminator/mod.rs already does on call?). // Is there other things we can/should check? Like vtable pointers? - Ok(()) + self.validate_fields(query, mode) } // FIXME: generators aren't validated right now TyGenerator(..) => Ok(()), diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 86b72220dc31f..89dad0052d880 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -3,28 +3,28 @@ use ty::layout::HasDataLayout; use super::{EvalResult, Memory, MemoryPointer, HasMemory, PointerArithmetic, Machine, PtrAndAlign}; +use syntax::ast::FloatTy; +use rustc_const_math::ConstFloat; -pub(super) fn bytes_to_f32(bytes: u128) -> f32 { - f32::from_bits(bytes as u32) -} - -pub(super) fn bytes_to_f64(bytes: u128) -> f64 { - f64::from_bits(bytes as u64) -} - -pub(super) fn f32_to_bytes(f: f32) -> u128 { - f.to_bits() as u128 +pub(super) fn bytes_to_f32(bits: u128) -> ConstFloat { + ConstFloat { + bits, + ty: FloatTy::F32, + } } -pub(super) fn f64_to_bytes(f: f64) -> u128 { - f.to_bits() as u128 +pub(super) fn bytes_to_f64(bits: u128) -> ConstFloat { + ConstFloat { + bits, + ty: FloatTy::F64, + } } /// A `Value` represents a single self-contained Rust value. /// /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve /// value held directly, outside of any allocation (`ByVal`). For `ByRef`-values, we remember -/// whether the pointer is supposed to be aligned or not (also see Lvalue). +/// whether the pointer is supposed to be aligned or not (also see Place). /// /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary @@ -198,7 +198,7 @@ impl<'a, 'tcx: 'a> Value { mem.read_maybe_aligned(aligned, |mem| { let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into(); let vtable = mem.read_ptr_sized_unsigned( - ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?, + ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, )?.to_ptr()?; Ok((ptr, vtable)) }) @@ -224,7 +224,7 @@ impl<'a, 'tcx: 'a> Value { mem.read_maybe_aligned(aligned, |mem| { let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into(); let len = mem.read_ptr_sized_unsigned( - ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?, + ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?, )?.to_bytes()? as u64; Ok((ptr, len)) }) @@ -249,12 +249,8 @@ impl<'tcx> PrimVal { PrimVal::Bytes(n as u128) } - pub fn from_f32(f: f32) -> Self { - PrimVal::Bytes(f32_to_bytes(f)) - } - - pub fn from_f64(f: f64) -> Self { - PrimVal::Bytes(f64_to_bytes(f)) + pub fn from_float(f: ConstFloat) -> Self { + PrimVal::Bytes(f.bits) } pub fn from_bool(b: bool) -> Self { @@ -331,11 +327,11 @@ impl<'tcx> PrimVal { }) } - pub fn to_f32(self) -> EvalResult<'tcx, f32> { + pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> { self.to_bytes().map(bytes_to_f32) } - pub fn to_f64(self) -> EvalResult<'tcx, f64> { + pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> { self.to_bytes().map(bytes_to_f64) } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index c7a3aa6ea05f7..b6014fdc8d8dd 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -25,7 +25,7 @@ use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use util::ppaux; -use rustc_back::slice; +use std::slice; use hir::{self, InlineAsm}; use std::ascii; use std::borrow::{Cow}; @@ -33,41 +33,18 @@ use std::cell::Ref; use std::fmt::{self, Debug, Formatter, Write}; use std::{iter, u32}; use std::ops::{Index, IndexMut}; +use std::rc::Rc; use std::vec::IntoIter; use syntax::ast::{self, Name}; +use syntax::symbol::InternedString; use syntax_pos::Span; mod cache; pub mod tcx; pub mod visit; -pub mod transform; pub mod traversal; pub mod interpret; -macro_rules! newtype_index { - ($name:ident, $debug_name:expr) => ( - #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, - RustcEncodable, RustcDecodable)] - pub struct $name(u32); - - impl Idx for $name { - fn new(value: usize) -> Self { - assert!(value < (u32::MAX) as usize); - $name(value as u32) - } - fn index(self) -> usize { - self.0 as usize - } - } - - impl Debug for $name { - fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "{}{}", $debug_name, self.0) - } - } - ) -} - /// Types for locals type LocalDecls<'tcx> = IndexVec>; @@ -100,16 +77,13 @@ pub struct Mir<'tcx> { /// Crate-local information for each visibility scope, that can't (and /// needn't) be tracked across crates. - pub visibility_scope_info: ClearOnDecode>, + pub visibility_scope_info: ClearCrossCrate>, /// Rvalues promoted from this function, such as borrows of constants. /// Each of them is the Mir of a constant with the fn's type parameters /// in scope, but a separate set of locals. pub promoted: IndexVec>, - /// Return type of the function. - pub return_ty: Ty<'tcx>, - /// Yield type of the function, if it is a generator. pub yield_ty: Option>, @@ -157,27 +131,24 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0); impl<'tcx> Mir<'tcx> { pub fn new(basic_blocks: IndexVec>, visibility_scopes: IndexVec, - visibility_scope_info: ClearOnDecode>, + visibility_scope_info: ClearCrossCrate>, promoted: IndexVec>, - return_ty: Ty<'tcx>, yield_ty: Option>, local_decls: IndexVec>, arg_count: usize, upvar_decls: Vec, span: Span) -> Self { - // We need `arg_count` locals, and one for the return pointer + // We need `arg_count` locals, and one for the return place assert!(local_decls.len() >= arg_count + 1, "expected at least {} locals, got {}", arg_count + 1, local_decls.len()); - assert_eq!(local_decls[RETURN_POINTER].ty, return_ty); Mir { basic_blocks, visibility_scopes, visibility_scope_info, promoted, - return_ty, yield_ty, generator_drop: None, generator_layout: None, @@ -201,6 +172,15 @@ impl<'tcx> Mir<'tcx> { &mut self.basic_blocks } + #[inline] + pub fn basic_blocks_and_local_decls_mut(&mut self) -> ( + &mut IndexVec>, + &mut LocalDecls<'tcx>, + ) { + self.cache.invalidate(); + (&mut self.basic_blocks, &mut self.local_decls) + } + #[inline] pub fn predecessors(&self) -> Ref>> { self.cache.predecessors(self) @@ -221,7 +201,7 @@ impl<'tcx> Mir<'tcx> { let index = local.0 as usize; if index == 0 { debug_assert!(self.local_decls[local].mutability == Mutability::Mut, - "return pointer should be mutable"); + "return place should be mutable"); LocalKind::ReturnPointer } else if index < self.arg_count + 1 { @@ -270,7 +250,7 @@ impl<'tcx> Mir<'tcx> { } /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all - /// locals that are neither arguments nor the return pointer). + /// locals that are neither arguments nor the return place). #[inline] pub fn vars_and_temps_iter(&self) -> impl Iterator { let arg_count = self.arg_count; @@ -285,9 +265,27 @@ impl<'tcx> Mir<'tcx> { debug_assert!(location.statement_index < block.statements.len()); block.statements[location.statement_index].make_nop() } + + /// Returns the source info associated with `location`. + pub fn source_info(&self, location: Location) -> &SourceInfo { + let block = &self[location.block]; + let stmts = &block.statements; + let idx = location.statement_index; + if idx < stmts.len() { + &stmts[idx].source_info + } else { + assert!(idx == stmts.len()); + &block.terminator().source_info + } + } + + /// Return the return type, it always return first element from `local_decls` array + pub fn return_ty(&self) -> Ty<'tcx> { + self.local_decls[RETURN_PLACE].ty + } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VisibilityScopeInfo { /// A NodeId with lint levels equivalent to this scope's lint levels. pub lint_root: ast::NodeId, @@ -295,7 +293,7 @@ pub struct VisibilityScopeInfo { pub safety: Safety, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] pub enum Safety { Safe, /// Unsafe because of a PushUnsafeBlock @@ -311,7 +309,6 @@ impl_stable_hash_for!(struct Mir<'tcx> { visibility_scopes, visibility_scope_info, promoted, - return_ty, yield_ty, generator_drop, generator_layout, @@ -340,22 +337,13 @@ impl<'tcx> IndexMut for Mir<'tcx> { } #[derive(Clone, Debug)] -pub enum ClearOnDecode { +pub enum ClearCrossCrate { Clear, Set(T) } -impl serialize::Encodable for ClearOnDecode { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - serialize::Encodable::encode(&(), s) - } -} - -impl serialize::Decodable for ClearOnDecode { - fn decode(d: &mut D) -> Result { - serialize::Decodable::decode(d).map(|()| ClearOnDecode::Clear) - } -} +impl serialize::UseSpecializedEncodable for ClearCrossCrate {} +impl serialize::UseSpecializedDecodable for ClearCrossCrate {} /// Grouped information about the source code origin of a MIR entity. /// Intended to be inspected by diagnostics and debuginfo. @@ -427,9 +415,11 @@ pub enum BorrowKind { /////////////////////////////////////////////////////////////////////////// // Variables and temps -newtype_index!(Local, "_"); - -pub const RETURN_POINTER: Local = Local(0); +newtype_index!(Local + { + DEBUG_FORMAT = "_{}", + const RETURN_PLACE = 0, + }); /// Classifies locals into categories. See `Mir::local_kind`. #[derive(PartialEq, Eq, Debug)] @@ -447,12 +437,12 @@ pub enum LocalKind { /// A MIR local. /// /// This can be a binding declared by the user, a temporary inserted by the compiler, a function -/// argument, or the return pointer. +/// argument, or the return place. #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct LocalDecl<'tcx> { /// `let mut x` vs `let x`. /// - /// Temporaries and the return pointer are always mutable. + /// Temporaries and the return place are always mutable. pub mutability: Mutability, /// True if this corresponds to a user-declared local variable. @@ -531,11 +521,11 @@ impl<'tcx> LocalDecl<'tcx> { } } - /// Builds a `LocalDecl` for the return pointer. + /// Builds a `LocalDecl` for the return place. /// /// This must be inserted into the `local_decls` list as the first local. #[inline] - pub fn new_return_pointer(return_ty: Ty, span: Span) -> LocalDecl { + pub fn new_return_place(return_ty: Ty, span: Span) -> LocalDecl { LocalDecl { mutability: Mutability::Mut, ty: return_ty, @@ -557,13 +547,24 @@ pub struct UpvarDecl { pub debug_name: Name, /// If true, the capture is behind a reference. - pub by_ref: bool + pub by_ref: bool, + + pub mutability: Mutability, } /////////////////////////////////////////////////////////////////////////// // BasicBlock -newtype_index!(BasicBlock, "bb"); +newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" }); + +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { + block: self, + statement_index: 0, + } + } +} /////////////////////////////////////////////////////////////////////////// // BasicBlockData and Terminator @@ -634,23 +635,48 @@ pub enum TerminatorKind<'tcx> { /// continue. Emitted by build::scope::diverge_cleanup. Resume, - /// Indicates a normal return. The return pointer lvalue should - /// have been filled in by now. This should occur at most once. + /// Indicates a normal return. The return place should have + /// been filled in by now. This should occur at most once. Return, /// Indicates a terminator that can never be reached. Unreachable, - /// Drop the Lvalue + /// Drop the Place Drop { - location: Lvalue<'tcx>, + location: Place<'tcx>, target: BasicBlock, unwind: Option }, - /// Drop the Lvalue and assign the new value over it + /// Drop the Place and assign the new value over it. This ensures + /// that the assignment to LV occurs *even if* the destructor for + /// place unwinds. Its semantics are best explained by by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(LV <- RV, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(LV, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // LV is now unitialized + /// LV <- RV + /// } + /// BB2 { + /// // LV is now unitialized -- its dtor panicked + /// LV <- RV + /// } + /// ``` DropAndReplace { - location: Lvalue<'tcx>, + location: Place<'tcx>, value: Operand<'tcx>, target: BasicBlock, unwind: Option, @@ -660,10 +686,13 @@ pub enum TerminatorKind<'tcx> { Call { /// The function that’s being called func: Operand<'tcx>, - /// Arguments the function is called with + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. args: Vec>, /// Destination for the return value. If some, the call is converging. - destination: Option<(Lvalue<'tcx>, BasicBlock)>, + destination: Option<(Place<'tcx>, BasicBlock)>, /// Cleanups to be done if the call unwinds. cleanup: Option }, @@ -690,6 +719,11 @@ pub enum TerminatorKind<'tcx> { /// Indicates the end of the dropping of a generator GeneratorDrop, + + FalseEdges { + real_target: BasicBlock, + imaginary_targets: Vec + }, } impl<'tcx> Terminator<'tcx> { @@ -700,6 +734,10 @@ impl<'tcx> Terminator<'tcx> { pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { self.kind.successors_mut() } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + self.kind.unwind_mut() + } } impl<'tcx> TerminatorKind<'tcx> { @@ -717,28 +755,33 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn successors(&self) -> Cow<[BasicBlock]> { use self::TerminatorKind::*; match *self { - Goto { target: ref b } => slice::ref_slice(b).into_cow(), + Goto { target: ref b } => slice::from_ref(b).into_cow(), SwitchInt { targets: ref b, .. } => b[..].into_cow(), Resume | GeneratorDrop => (&[]).into_cow(), Return => (&[]).into_cow(), Unreachable => (&[]).into_cow(), Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(), Call { destination: Some((_, ref t)), cleanup: None, .. } => - slice::ref_slice(t).into_cow(), - Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(), + slice::from_ref(t).into_cow(), + Call { destination: None, cleanup: Some(ref c), .. } => slice::from_ref(c).into_cow(), Call { destination: None, cleanup: None, .. } => (&[]).into_cow(), Yield { resume: t, drop: Some(c), .. } => vec![t, c].into_cow(), - Yield { resume: ref t, drop: None, .. } => slice::ref_slice(t).into_cow(), + Yield { resume: ref t, drop: None, .. } => slice::from_ref(t).into_cow(), DropAndReplace { target, unwind: Some(unwind), .. } | Drop { target, unwind: Some(unwind), .. } => { vec![target, unwind].into_cow() } DropAndReplace { ref target, unwind: None, .. } | Drop { ref target, unwind: None, .. } => { - slice::ref_slice(target).into_cow() + slice::from_ref(target).into_cow() } Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(), - Assert { ref target, .. } => slice::ref_slice(target).into_cow(), + Assert { ref target, .. } => slice::from_ref(target).into_cow(), + FalseEdges { ref real_target, ref imaginary_targets } => { + let mut s = vec![*real_target]; + s.extend_from_slice(imaginary_targets); + s.into_cow() + } } } @@ -765,7 +808,33 @@ impl<'tcx> TerminatorKind<'tcx> { vec![target] } Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind], - Assert { ref mut target, .. } => vec![target] + Assert { ref mut target, .. } => vec![target], + FalseEdges { ref mut real_target, ref mut imaginary_targets } => { + let mut s = vec![real_target]; + s.extend(imaginary_targets.iter_mut()); + s + } + } + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + match *self { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::Unreachable | + TerminatorKind::GeneratorDrop | + TerminatorKind::Yield { .. } | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::FalseEdges { .. } => { + None + }, + TerminatorKind::Call { cleanup: ref mut unwind, .. } | + TerminatorKind::Assert { cleanup: ref mut unwind, .. } | + TerminatorKind::DropAndReplace { ref mut unwind, .. } | + TerminatorKind::Drop { ref mut unwind, .. } => { + Some(unwind) + } } } } @@ -835,7 +904,7 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match *self { Goto { .. } => write!(fmt, "goto"), - SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), + SwitchInt { discr: ref place, .. } => write!(fmt, "switchInt({:?})", place), Return => write!(fmt, "return"), GeneratorDrop => write!(fmt, "generator_drop"), Resume => write!(fmt, "resume"), @@ -882,7 +951,8 @@ impl<'tcx> TerminatorKind<'tcx> { } write!(fmt, ")") - } + }, + FalseEdges { .. } => write!(fmt, "falseEdges") } } @@ -918,7 +988,12 @@ impl<'tcx> TerminatorKind<'tcx> { } Assert { cleanup: None, .. } => vec!["".into()], Assert { .. } => - vec!["success".into_cow(), "unwind".into_cow()] + vec!["success".into_cow(), "unwind".into_cow()], + FalseEdges { ref imaginary_targets, .. } => { + let mut l = vec!["real".into()]; + l.resize(imaginary_targets.len() + 1, "imaginary".into()); + l + } } } } @@ -953,11 +1028,11 @@ impl<'tcx> Statement<'tcx> { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum StatementKind<'tcx> { - /// Write the RHS Rvalue to the LHS Lvalue. - Assign(Lvalue<'tcx>, Rvalue<'tcx>), + /// Write the RHS Rvalue to the LHS Place. + Assign(Place<'tcx>, Rvalue<'tcx>), - /// Write the discriminant for a variant to the enum Lvalue. - SetDiscriminant { lvalue: Lvalue<'tcx>, variant_index: usize }, + /// Write the discriminant for a variant to the enum Place. + SetDiscriminant { place: Place<'tcx>, variant_index: usize }, /// Start a live range for the storage of the local. StorageLive(Local), @@ -968,14 +1043,14 @@ pub enum StatementKind<'tcx> { /// Execute a piece of inline Assembly. InlineAsm { asm: Box, - outputs: Vec>, + outputs: Vec>, inputs: Vec> }, - /// Assert the given lvalues to be valid inhabitants of their type. These statements are + /// Assert the given places to be valid inhabitants of their type. These statements are /// currently only interpreted by miri and only generated when "-Z mir-emit-validate" is passed. /// See for more details. - Validate(ValidationOp, Vec>>), + Validate(ValidationOp, Vec>>), /// Mark one terminating point of a region scope (i.e. static region). /// (The starting point(s) arise implicitly from borrows.) @@ -989,9 +1064,9 @@ pub enum StatementKind<'tcx> { /// `Validate` statement. #[derive(Copy, Clone, RustcEncodable, RustcDecodable, PartialEq, Eq)] pub enum ValidationOp { - /// Recursively traverse the lvalue following the type and validate that all type + /// Recursively traverse the place following the type and validate that all type /// invariants are maintained. Furthermore, acquire exclusive/read-only access to the - /// memory reachable from the lvalue. + /// memory reachable from the place. Acquire, /// Recursive traverse the *mutable* part of the type and relinquish all exclusive /// access. @@ -1016,7 +1091,7 @@ impl Debug for ValidationOp { // This is generic so that it can be reused by miri #[derive(Clone, RustcEncodable, RustcDecodable)] pub struct ValidationOperand<'tcx, T> { - pub lval: T, + pub place: T, pub ty: Ty<'tcx>, pub re: Option, pub mutbl: hir::Mutability, @@ -1024,7 +1099,7 @@ pub struct ValidationOperand<'tcx, T> { impl<'tcx, T: Debug> Debug for ValidationOperand<'tcx, T> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "{:?}: {:?}", self.lval, self.ty)?; + write!(fmt, "{:?}: {:?}", self.place, self.ty)?; if let Some(ce) = self.re { // (reuse lifetime rendering policy from ppaux.) write!(fmt, "/{}", ty::ReScope(ce))?; @@ -1040,14 +1115,14 @@ impl<'tcx> Debug for Statement<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::StatementKind::*; match self.kind { - Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), + Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv), // (reuse lifetime rendering policy from ppaux.) EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)), - Validate(ref op, ref lvalues) => write!(fmt, "Validate({:?}, {:?})", op, lvalues), - StorageLive(ref lv) => write!(fmt, "StorageLive({:?})", lv), - StorageDead(ref lv) => write!(fmt, "StorageDead({:?})", lv), - SetDiscriminant{lvalue: ref lv, variant_index: index} => { - write!(fmt, "discriminant({:?}) = {:?}", lv, index) + Validate(ref op, ref places) => write!(fmt, "Validate({:?}, {:?})", op, places), + StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), + StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), + SetDiscriminant { ref place, variant_index } => { + write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) }, InlineAsm { ref asm, ref outputs, ref inputs } => { write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs) @@ -1058,20 +1133,20 @@ impl<'tcx> Debug for Statement<'tcx> { } /////////////////////////////////////////////////////////////////////////// -// Lvalues +// Places /// A path to a value; something that can be evaluated without /// changing or disturbing program state. #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] -pub enum Lvalue<'tcx> { +pub enum Place<'tcx> { /// local variable Local(Local), /// static or static mut variable Static(Box>), - /// projection out of an lvalue (access a field, deref a pointer, etc) - Projection(Box>), + /// projection out of a place (access a field, deref a pointer, etc) + Projection(Box>), } /// The def-id of a static, along with its normalized type (which is @@ -1089,8 +1164,8 @@ impl_stable_hash_for!(struct Static<'tcx> { /// The `Projection` data structure defines things of the form `B.x` /// or `*B` or `B[index]`. Note that it is parameterized because it is -/// shared between `Constant` and `Lvalue`. See the aliases -/// `LvalueProjection` etc below. +/// shared between `Constant` and `Place`. See the aliases +/// `PlaceProjection` etc below. #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct Projection<'tcx, B, V, T> { pub base: B, @@ -1135,44 +1210,44 @@ pub enum ProjectionElem<'tcx, V, T> { Downcast(&'tcx AdtDef, usize), } -/// Alias for projections as they appear in lvalues, where the base is an lvalue +/// Alias for projections as they appear in places, where the base is a place /// and the index is a local. -pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Local, Ty<'tcx>>; +pub type PlaceProjection<'tcx> = Projection<'tcx, Place<'tcx>, Local, Ty<'tcx>>; -/// Alias for projections as they appear in lvalues, where the base is an lvalue +/// Alias for projections as they appear in places, where the base is a place /// and the index is a local. -pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>; +pub type PlaceElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>; -newtype_index!(Field, "field"); +newtype_index!(Field { DEBUG_FORMAT = "field[{}]" }); -impl<'tcx> Lvalue<'tcx> { - pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> { +impl<'tcx> Place<'tcx> { + pub fn field(self, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { self.elem(ProjectionElem::Field(f, ty)) } - pub fn deref(self) -> Lvalue<'tcx> { + pub fn deref(self) -> Place<'tcx> { self.elem(ProjectionElem::Deref) } - pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Lvalue<'tcx> { + pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Place<'tcx> { self.elem(ProjectionElem::Downcast(adt_def, variant_index)) } - pub fn index(self, index: Local) -> Lvalue<'tcx> { + pub fn index(self, index: Local) -> Place<'tcx> { self.elem(ProjectionElem::Index(index)) } - pub fn elem(self, elem: LvalueElem<'tcx>) -> Lvalue<'tcx> { - Lvalue::Projection(Box::new(LvalueProjection { + pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> { + Place::Projection(Box::new(PlaceProjection { base: self, elem, })) } } -impl<'tcx> Debug for Lvalue<'tcx> { +impl<'tcx> Debug for Place<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - use self::Lvalue::*; + use self::Place::*; match *self { Local(id) => write!(fmt, "{:?}", id), @@ -1208,8 +1283,11 @@ impl<'tcx> Debug for Lvalue<'tcx> { /////////////////////////////////////////////////////////////////////////// // Scopes -newtype_index!(VisibilityScope, "scope"); -pub const ARGUMENT_VISIBILITY_SCOPE : VisibilityScope = VisibilityScope(0); +newtype_index!(VisibilityScope + { + DEBUG_FORMAT = "scope[{}]", + const ARGUMENT_VISIBILITY_SCOPE = 0, + }); #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VisibilityScopeData { @@ -1221,11 +1299,21 @@ pub struct VisibilityScopeData { // Operands /// These are values that can appear inside an rvalue (or an index -/// lvalue). They are intentionally limited to prevent rvalues from +/// place). They are intentionally limited to prevent rvalues from /// being nested in one another. #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub enum Operand<'tcx> { - Consume(Lvalue<'tcx>), + /// Copy: The value must be available for use afterwards. + /// + /// This implies that the type of the place must be `Copy`; this is true + /// by construction during build, but also checked by the MIR type checker. + Copy(Place<'tcx>), + /// Move: The value (including old borrows of it) will not be used again. + /// + /// Safe for values of all types (modulo future developments towards `?Move`). + /// Correct usage patterns are enforced by the borrow checker for safe code. + /// `Copy` may be converted to `Move` to enable "last-use" optimizations. + Move(Place<'tcx>), Constant(Box>), } @@ -1234,7 +1322,8 @@ impl<'tcx> Debug for Operand<'tcx> { use self::Operand::*; match *self { Constant(ref a) => write!(fmt, "{:?}", a), - Consume(ref lv) => write!(fmt, "{:?}", lv), + Copy(ref place) => write!(fmt, "{:?}", place), + Move(ref place) => write!(fmt, "move {:?}", place), } } } @@ -1273,10 +1362,10 @@ pub enum Rvalue<'tcx> { Repeat(Operand<'tcx>, ConstUsize), /// &x or &mut x - Ref(Region<'tcx>, BorrowKind, Lvalue<'tcx>), + Ref(Region<'tcx>, BorrowKind, Place<'tcx>), /// length of a [X] or [X;n] value - Len(Lvalue<'tcx>), + Len(Place<'tcx>), Cast(CastKind, Operand<'tcx>, Ty<'tcx>), @@ -1290,7 +1379,7 @@ pub enum Rvalue<'tcx> { /// /// Undefined (i.e. no effort is made to make it defined, but there’s no reason why it cannot /// be defined to return, say, a 0) if ADT is not an enum. - Discriminant(Lvalue<'tcx>), + Discriminant(Place<'tcx>), /// Create an aggregate value, like a tuple or struct. This is /// only needed because we want to distinguish `dest = Foo { x: @@ -1326,10 +1415,14 @@ pub enum AggregateKind<'tcx> { /// The type is of the element Array(Ty<'tcx>), Tuple, - /// The second field is variant number (discriminant), it's equal to 0 - /// for struct and union expressions. The fourth field is active field - /// number and is present only for union expressions. + + /// The second field is variant number (discriminant), it's equal + /// to 0 for struct and union expressions. The fourth field is + /// active field number and is present only for union expressions + /// -- e.g. for a union expression `SomeUnion { c: .. }`, the + /// active field index would identity the field `c` Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option), + Closure(DefId, ClosureSubsts<'tcx>), Generator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>), } @@ -1403,18 +1496,20 @@ impl<'tcx> Debug for Rvalue<'tcx> { use self::Rvalue::*; match *self { - Use(ref lvalue) => write!(fmt, "{:?}", lvalue), + Use(ref place) => write!(fmt, "{:?}", place), Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b), Len(ref a) => write!(fmt, "Len({:?})", a), - Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind), + Cast(ref kind, ref place, ref ty) => { + write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) + } BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), CheckedBinaryOp(ref op, ref a, ref b) => { write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) } UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), - Discriminant(ref lval) => write!(fmt, "discriminant({:?})", lval), + Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), - Ref(region, borrow_kind, ref lv) => { + Ref(region, borrow_kind, ref place) => { let kind_str = match borrow_kind { BorrowKind::Shared => "", BorrowKind::Mut | BorrowKind::Unique => "mut ", @@ -1429,26 +1524,26 @@ impl<'tcx> Debug for Rvalue<'tcx> { // Do not even print 'static "".to_owned() }; - write!(fmt, "&{}{}{:?}", region, kind_str, lv) + write!(fmt, "&{}{}{:?}", region, kind_str, place) } - Aggregate(ref kind, ref lvs) => { - fn fmt_tuple(fmt: &mut Formatter, lvs: &[Operand]) -> fmt::Result { + Aggregate(ref kind, ref places) => { + fn fmt_tuple(fmt: &mut Formatter, places: &[Operand]) -> fmt::Result { let mut tuple_fmt = fmt.debug_tuple(""); - for lv in lvs { - tuple_fmt.field(lv); + for place in places { + tuple_fmt.field(place); } tuple_fmt.finish() } match **kind { - AggregateKind::Array(_) => write!(fmt, "{:?}", lvs), + AggregateKind::Array(_) => write!(fmt, "{:?}", places), AggregateKind::Tuple => { - match lvs.len() { + match places.len() { 0 => write!(fmt, "()"), - 1 => write!(fmt, "({:?},)", lvs[0]), - _ => fmt_tuple(fmt, lvs), + 1 => write!(fmt, "({:?},)", places[0]), + _ => fmt_tuple(fmt, places), } } @@ -1459,11 +1554,11 @@ impl<'tcx> Debug for Rvalue<'tcx> { match variant_def.ctor_kind { CtorKind::Const => Ok(()), - CtorKind::Fn => fmt_tuple(fmt, lvs), + CtorKind::Fn => fmt_tuple(fmt, places), CtorKind::Fictive => { let mut struct_fmt = fmt.debug_struct(""); - for (field, lv) in variant_def.fields.iter().zip(lvs) { - struct_fmt.field(&field.name.as_str(), lv); + for (field, place) in variant_def.fields.iter().zip(places) { + struct_fmt.field(&field.name.as_str(), place); } struct_fmt.finish() } @@ -1480,9 +1575,9 @@ impl<'tcx> Debug for Rvalue<'tcx> { let mut struct_fmt = fmt.debug_struct(&name); tcx.with_freevars(node_id, |freevars| { - for (freevar, lv) in freevars.iter().zip(lvs) { + for (freevar, place) in freevars.iter().zip(places) { let var_name = tcx.hir.name(freevar.var_id()); - struct_fmt.field(&var_name.as_str(), lv); + struct_fmt.field(&var_name.as_str(), place); } }); @@ -1498,14 +1593,14 @@ impl<'tcx> Debug for Rvalue<'tcx> { let mut struct_fmt = fmt.debug_struct(&name); tcx.with_freevars(node_id, |freevars| { - for (freevar, lv) in freevars.iter().zip(lvs) { + for (freevar, place) in freevars.iter().zip(places) { let var_name = tcx.hir.name(freevar.var_id()); - struct_fmt.field(&var_name.as_str(), lv); + struct_fmt.field(&var_name.as_str(), place); } - struct_fmt.field("$state", &lvs[freevars.len()]); - for i in (freevars.len() + 1)..lvs.len() { + struct_fmt.field("$state", &places[freevars.len()]); + for i in (freevars.len() + 1)..places.len() { struct_fmt.field(&format!("${}", i - freevars.len() - 1), - &lvs[i]); + &places[i]); } }); @@ -1534,7 +1629,8 @@ pub struct Constant<'tcx> { pub literal: Literal<'tcx>, } -newtype_index!(Promoted, "promoted"); +newtype_index!(Promoted { DEBUG_FORMAT = "promoted[{}]" }); + #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum Literal<'tcx> { @@ -1642,6 +1738,14 @@ impl fmt::Debug for Location { } impl Location { + /// Returns the location immediately after this one within the enclosing block. + /// + /// Note that if this location represents a terminator, then the + /// resulting location would be out of bounds and invalid. + pub fn successor_within_block(&self) -> Location { + Location { block: self.block, statement_index: self.statement_index + 1 } + } + pub fn dominates(&self, other: &Location, dominators: &Dominators) -> bool { if self.block == other.block { self.statement_index <= other.statement_index @@ -1651,11 +1755,27 @@ impl Location { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub enum UnsafetyViolationKind { + General, + ExternStatic(ast::NodeId), + BorrowPacked(ast::NodeId), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct UnsafetyViolation { pub source_info: SourceInfo, - pub description: &'static str, - pub lint_node_id: Option, + pub description: InternedString, + pub kind: UnsafetyViolationKind, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub struct UnsafetyCheckResult { + /// Violations that are propagated *upwards* from this function + pub violations: Rc<[UnsafetyViolation]>, + /// unsafe blocks in this function, along with whether they are used. This is + /// used for the "unused_unsafe" lint. + pub unsafe_blocks: Rc<[(ast::NodeId, bool)]>, } /// The layout of generator state @@ -1675,7 +1795,6 @@ impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> { visibility_scopes: self.visibility_scopes.clone(), visibility_scope_info: self.visibility_scope_info.clone(), promoted: self.promoted.fold_with(folder), - return_ty: self.return_ty.fold_with(folder), yield_ty: self.yield_ty.fold_with(folder), generator_drop: self.generator_drop.fold_with(folder), generator_layout: self.generator_layout.fold_with(folder), @@ -1694,7 +1813,6 @@ impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> { self.generator_layout.visit_with(visitor) || self.yield_ty.visit_with(visitor) || self.promoted.visit_with(visitor) || - self.return_ty.visit_with(visitor) || self.local_decls.visit_with(visitor) } } @@ -1738,10 +1856,10 @@ impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Lvalue<'tcx>> { +impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { ValidationOperand { - lval: self.lval.fold_with(folder), + place: self.place.fold_with(folder), ty: self.ty.fold_with(folder), re: self.re, mutbl: self.mutbl, @@ -1749,7 +1867,7 @@ impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Lvalue<'tcx>> { } fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.lval.visit_with(visitor) || self.ty.visit_with(visitor) + self.place.visit_with(visitor) || self.ty.visit_with(visitor) } } @@ -1758,9 +1876,9 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { use mir::StatementKind::*; let kind = match self.kind { - Assign(ref lval, ref rval) => Assign(lval.fold_with(folder), rval.fold_with(folder)), - SetDiscriminant { ref lvalue, variant_index } => SetDiscriminant { - lvalue: lvalue.fold_with(folder), + Assign(ref place, ref rval) => Assign(place.fold_with(folder), rval.fold_with(folder)), + SetDiscriminant { ref place, variant_index } => SetDiscriminant { + place: place.fold_with(folder), variant_index, }, StorageLive(ref local) => StorageLive(local.fold_with(folder)), @@ -1777,9 +1895,9 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { // trait with a `fn fold_scope`. EndRegion(ref region_scope) => EndRegion(region_scope.clone()), - Validate(ref op, ref lvals) => + Validate(ref op, ref places) => Validate(op.clone(), - lvals.iter().map(|operand| operand.fold_with(folder)).collect()), + places.iter().map(|operand| operand.fold_with(folder)).collect()), Nop => Nop, }; @@ -1793,8 +1911,8 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { use mir::StatementKind::*; match self.kind { - Assign(ref lval, ref rval) => { lval.visit_with(visitor) || rval.visit_with(visitor) } - SetDiscriminant { ref lvalue, .. } => lvalue.visit_with(visitor), + Assign(ref place, ref rval) => { place.visit_with(visitor) || rval.visit_with(visitor) } + SetDiscriminant { ref place, .. } => place.visit_with(visitor), StorageLive(ref local) | StorageDead(ref local) => local.visit_with(visitor), InlineAsm { ref outputs, ref inputs, .. } => @@ -1806,8 +1924,8 @@ impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { // trait with a `fn visit_scope`. EndRegion(ref _scope) => false, - Validate(ref _op, ref lvalues) => - lvalues.iter().any(|ty_and_lvalue| ty_and_lvalue.visit_with(visitor)), + Validate(ref _op, ref places) => + places.iter().any(|ty_and_place| ty_and_place.visit_with(visitor)), Nop => false, } @@ -1875,6 +1993,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Resume => Resume, Return => Return, Unreachable => Unreachable, + FalseEdges { real_target, ref imaginary_targets } => + FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() } }; Terminator { source_info: self.source_info, @@ -1914,21 +2034,22 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Resume | Return | GeneratorDrop | - Unreachable => false + Unreachable | + FalseEdges { .. } => false } } } -impl<'tcx> TypeFoldable<'tcx> for Lvalue<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match self { - &Lvalue::Projection(ref p) => Lvalue::Projection(p.fold_with(folder)), + &Place::Projection(ref p) => Place::Projection(p.fold_with(folder)), _ => self.clone() } } fn super_visit_with>(&self, visitor: &mut V) -> bool { - if let &Lvalue::Projection(ref p) = self { + if let &Place::Projection(ref p) = self { p.visit_with(visitor) } else { false @@ -1942,15 +2063,16 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { match *self { Use(ref op) => Use(op.fold_with(folder)), Repeat(ref op, len) => Repeat(op.fold_with(folder), len), - Ref(region, bk, ref lval) => Ref(region.fold_with(folder), bk, lval.fold_with(folder)), - Len(ref lval) => Len(lval.fold_with(folder)), + Ref(region, bk, ref place) => + Ref(region.fold_with(folder), bk, place.fold_with(folder)), + Len(ref place) => Len(place.fold_with(folder)), Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)), BinaryOp(op, ref rhs, ref lhs) => BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)), CheckedBinaryOp(op, ref rhs, ref lhs) => CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)), UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)), - Discriminant(ref lval) => Discriminant(lval.fold_with(folder)), + Discriminant(ref place) => Discriminant(place.fold_with(folder)), NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)), Aggregate(ref kind, ref fields) => { let kind = box match **kind { @@ -1975,14 +2097,14 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { match *self { Use(ref op) => op.visit_with(visitor), Repeat(ref op, _) => op.visit_with(visitor), - Ref(region, _, ref lval) => region.visit_with(visitor) || lval.visit_with(visitor), - Len(ref lval) => lval.visit_with(visitor), + Ref(region, _, ref place) => region.visit_with(visitor) || place.visit_with(visitor), + Len(ref place) => place.visit_with(visitor), Cast(_, ref op, ty) => op.visit_with(visitor) || ty.visit_with(visitor), BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => rhs.visit_with(visitor) || lhs.visit_with(visitor), UnaryOp(_, ref val) => val.visit_with(visitor), - Discriminant(ref lval) => lval.visit_with(visitor), + Discriminant(ref place) => place.visit_with(visitor), NullaryOp(_, ty) => ty.visit_with(visitor), Aggregate(ref kind, ref fields) => { (match **kind { @@ -2001,14 +2123,16 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { - Operand::Consume(ref lval) => Operand::Consume(lval.fold_with(folder)), + Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)), + Operand::Move(ref place) => Operand::Move(place.fold_with(folder)), Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)), } } fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - Operand::Consume(ref lval) => lval.visit_with(visitor), + Operand::Copy(ref place) | + Operand::Move(ref place) => place.visit_with(visitor), Operand::Constant(ref c) => c.visit_with(visitor) } } diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index d645a00e15781..23f360d5c3922 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -21,7 +21,7 @@ use hir; use ty::util::IntTypeExt; #[derive(Copy, Clone, Debug)] -pub enum LvalueTy<'tcx> { +pub enum PlaceTy<'tcx> { /// Normal type. Ty { ty: Ty<'tcx> }, @@ -31,23 +31,23 @@ pub enum LvalueTy<'tcx> { variant_index: usize }, } -impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> { - pub fn from_ty(ty: Ty<'tcx>) -> LvalueTy<'tcx> { - LvalueTy::Ty { ty: ty } +impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { + pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> { + PlaceTy::Ty { ty: ty } } pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { - LvalueTy::Ty { ty } => + PlaceTy::Ty { ty } => ty, - LvalueTy::Downcast { adt_def, substs, variant_index: _ } => + PlaceTy::Downcast { adt_def, substs, variant_index: _ } => tcx.mk_adt(adt_def, substs), } } pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - elem: &LvalueElem<'tcx>) - -> LvalueTy<'tcx> + elem: &PlaceElem<'tcx>) + -> PlaceTy<'tcx> { match *elem { ProjectionElem::Deref => { @@ -57,17 +57,17 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> { bug!("deref projection of non-dereferencable ty {:?}", self) }) .ty; - LvalueTy::Ty { + PlaceTy::Ty { ty, } } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => - LvalueTy::Ty { + PlaceTy::Ty { ty: self.to_ty(tcx).builtin_index().unwrap() }, ProjectionElem::Subslice { from, to } => { let ty = self.to_ty(tcx); - LvalueTy::Ty { + PlaceTy::Ty { ty: match ty.sty { ty::TyArray(inner, size) => { let size = size.val.to_const_int().unwrap().to_u64().unwrap(); @@ -87,7 +87,7 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> { assert!(adt_def.is_enum()); assert!(index < adt_def.variants.len()); assert_eq!(adt_def, adt_def1); - LvalueTy::Downcast { adt_def, + PlaceTy::Downcast { adt_def, substs, variant_index: index } } @@ -95,17 +95,17 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> { bug!("cannot downcast non-ADT type: `{:?}`", self) } }, - ProjectionElem::Field(_, fty) => LvalueTy::Ty { ty: fty } + ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty } } } } -impl<'tcx> TypeFoldable<'tcx> for LvalueTy<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { - LvalueTy::Ty { ty } => LvalueTy::Ty { ty: ty.fold_with(folder) }, - LvalueTy::Downcast { adt_def, substs, variant_index } => { - LvalueTy::Downcast { + PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.fold_with(folder) }, + PlaceTy::Downcast { adt_def, substs, variant_index } => { + PlaceTy::Downcast { adt_def, substs: substs.fold_with(folder), variant_index, @@ -116,22 +116,22 @@ impl<'tcx> TypeFoldable<'tcx> for LvalueTy<'tcx> { fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - LvalueTy::Ty { ty } => ty.visit_with(visitor), - LvalueTy::Downcast { substs, .. } => substs.visit_with(visitor) + PlaceTy::Ty { ty } => ty.visit_with(visitor), + PlaceTy::Downcast { substs, .. } => substs.visit_with(visitor) } } } -impl<'tcx> Lvalue<'tcx> { - pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> LvalueTy<'tcx> +impl<'tcx> Place<'tcx> { + pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> PlaceTy<'tcx> where D: HasLocalDecls<'tcx> { match *self { - Lvalue::Local(index) => - LvalueTy::Ty { ty: local_decls.local_decls()[index].ty }, - Lvalue::Static(ref data) => - LvalueTy::Ty { ty: data.ty }, - Lvalue::Projection(ref proj) => + Place::Local(index) => + PlaceTy::Ty { ty: local_decls.local_decls()[index].ty }, + Place::Static(ref data) => + PlaceTy::Ty { ty: data.ty }, + Place::Projection(ref proj) => proj.base.ty(local_decls, tcx).projection_ty(tcx, &proj.elem), } } @@ -151,11 +151,11 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::Repeat(ref operand, count) => { tcx.mk_array_const_usize(operand.ty(local_decls, tcx), count) } - Rvalue::Ref(reg, bk, ref lv) => { - let lv_ty = lv.ty(local_decls, tcx).to_ty(tcx); + Rvalue::Ref(reg, bk, ref place) => { + let place_ty = place.ty(local_decls, tcx).to_ty(tcx); tcx.mk_ref(reg, ty::TypeAndMut { - ty: lv_ty, + ty: place_ty, mutbl: bk.to_mutbl_lossy() } ) @@ -177,14 +177,14 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::UnaryOp(UnOp::Neg, ref operand) => { operand.ty(local_decls, tcx) } - Rvalue::Discriminant(ref lval) => { - let ty = lval.ty(local_decls, tcx).to_ty(tcx); + Rvalue::Discriminant(ref place) => { + let ty = place.ty(local_decls, tcx).to_ty(tcx); if let ty::TyAdt(adt_def, _) = ty.sty { adt_def.repr.discr_type().to_ty(tcx) } else { // Undefined behaviour, bug for now; may want to return something for // the `discriminant` intrinsic later. - bug!("Rvalue::Discriminant on Lvalue of type {:?}", ty); + bug!("Rvalue::Discriminant on Place of type {:?}", ty); } } Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t), @@ -230,7 +230,8 @@ impl<'tcx> Operand<'tcx> { where D: HasLocalDecls<'tcx> { match self { - &Operand::Consume(ref l) => l.ty(local_decls, tcx).to_ty(tcx), + &Operand::Copy(ref l) | + &Operand::Move(ref l) => l.ty(local_decls, tcx).to_ty(tcx), &Operand::Constant(ref c) => c.ty, } } diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs deleted file mode 100644 index f29405e665051..0000000000000 --- a/src/librustc/mir/transform.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! See [the README](README.md) for details on writing your own pass. - -use hir; -use hir::def_id::DefId; -use hir::map::DefPathData; -use mir::{Mir, Promoted}; -use ty::TyCtxt; -use std::rc::Rc; -use syntax::ast::NodeId; - -use std::borrow::Cow; - -/// Where a specific Mir comes from. -#[derive(Debug, Copy, Clone)] -pub enum MirSource { - /// Functions and methods. - Fn(NodeId), - - /// Constants and associated constants. - Const(NodeId), - - /// Initializer of a `static` item. - Static(NodeId, hir::Mutability), - - /// Promoted rvalues within a function. - Promoted(NodeId, Promoted), - - /// Drop glue for a generator. - GeneratorDrop(NodeId), -} - -impl<'a, 'tcx> MirSource { - pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource { - let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id"); - Self::from_node(tcx, id) - } - - pub fn from_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> MirSource { - use hir::*; - - // Handle constants in enum discriminants, types, and repeat expressions. - let def_id = tcx.hir.local_def_id(id); - let def_key = tcx.def_key(def_id); - if def_key.disambiguated_data.data == DefPathData::Initializer { - return MirSource::Const(id); - } - - match tcx.hir.get(id) { - map::NodeItem(&Item { node: ItemConst(..), .. }) | - map::NodeTraitItem(&TraitItem { node: TraitItemKind::Const(..), .. }) | - map::NodeImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) => { - MirSource::Const(id) - } - map::NodeItem(&Item { node: ItemStatic(_, m, _), .. }) => { - MirSource::Static(id, m) - } - // Default to function if it's not a constant or static. - _ => MirSource::Fn(id) - } - } - - pub fn item_id(&self) -> NodeId { - match *self { - MirSource::Fn(id) | - MirSource::Const(id) | - MirSource::GeneratorDrop(id) | - MirSource::Static(id, _) | - MirSource::Promoted(id, _) => id - } - } -} - -/// Generates a default name for the pass based on the name of the -/// type `T`. -pub fn default_name() -> Cow<'static, str> { - let name = unsafe { ::std::intrinsics::type_name::() }; - if let Some(tail) = name.rfind(":") { - Cow::from(&name[tail+1..]) - } else { - Cow::from(name) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct MirSuite(pub usize); - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct MirPassIndex(pub usize); - -/// A pass hook is invoked both before and after each pass executes. -/// This is primarily used to dump MIR for debugging. -/// -/// You can tell whether this is before or after by inspecting the -/// `mir` parameter -- before the pass executes, it will be `None` (in -/// which case you can inspect the MIR from previous pass by executing -/// `mir_cx.read_previous_mir()`); after the pass executes, it will be -/// `Some()` with the result of the pass (in which case the output -/// from the previous pass is most likely stolen, so you would not -/// want to try and access it). If the pass is interprocedural, then -/// the hook will be invoked once per output. -pub trait PassHook { - fn on_mir_pass<'a, 'tcx: 'a>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - suite: MirSuite, - pass_num: MirPassIndex, - pass_name: &str, - source: MirSource, - mir: &Mir<'tcx>, - is_after: bool); -} - -/// The full suite of types that identifies a particular -/// application of a pass to a def-id. -pub type PassId = (MirSuite, MirPassIndex, DefId); - -/// A streamlined trait that you can implement to create a pass; the -/// pass will be named after the type, and it will consist of a main -/// loop that goes over each available MIR and applies `run_pass`. -pub trait MirPass { - fn name<'a>(&'a self) -> Cow<'a, str> { - default_name::() - } - - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - source: MirSource, - mir: &mut Mir<'tcx>); -} - -/// A manager for MIR passes. -/// -/// FIXME(#41712) -- it is unclear whether we should have this struct. -#[derive(Clone)] -pub struct Passes { - pass_hooks: Vec>, - suites: Vec>>, -} - -/// The number of "pass suites" that we have: -/// -/// - ready for constant evaluation -/// - unopt -/// - optimized -pub const MIR_SUITES: usize = 3; - -/// Run the passes we need to do constant qualification and evaluation. -pub const MIR_CONST: MirSuite = MirSuite(0); - -/// Run the passes we need to consider the MIR validated and ready for borrowck etc. -pub const MIR_VALIDATED: MirSuite = MirSuite(1); - -/// Run the passes we need to consider the MIR *optimized*. -pub const MIR_OPTIMIZED: MirSuite = MirSuite(2); - -impl<'a, 'tcx> Passes { - pub fn new() -> Passes { - Passes { - pass_hooks: Vec::new(), - suites: (0..MIR_SUITES).map(|_| Vec::new()).collect(), - } - } - - /// Pushes a built-in pass. - pub fn push_pass(&mut self, suite: MirSuite, pass: T) { - self.suites[suite.0].push(Rc::new(pass)); - } - - /// Pushes a pass hook. - pub fn push_hook(&mut self, hook: T) { - self.pass_hooks.push(Rc::new(hook)); - } - - pub fn passes(&self, suite: MirSuite) -> &[Rc] { - &self.suites[suite.0] - } - - pub fn hooks(&self) -> &[Rc] { - &self.pass_hooks - } -} diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 63652980f9b4b..d90bf1b61a7d3 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -107,10 +107,10 @@ macro_rules! make_mir_visitor { fn visit_assign(&mut self, block: BasicBlock, - lvalue: & $($mutability)* Lvalue<'tcx>, + place: & $($mutability)* Place<'tcx>, rvalue: & $($mutability)* Rvalue<'tcx>, location: Location) { - self.super_assign(block, lvalue, rvalue, location); + self.super_assign(block, place, rvalue, location); } fn visit_terminator(&mut self, @@ -145,32 +145,32 @@ macro_rules! make_mir_visitor { self.super_operand(operand, location); } - fn visit_lvalue(&mut self, - lvalue: & $($mutability)* Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn visit_place(&mut self, + place: & $($mutability)* Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - self.super_lvalue(lvalue, context, location); + self.super_place(place, context, location); } fn visit_static(&mut self, static_: & $($mutability)* Static<'tcx>, - context: LvalueContext<'tcx>, + context: PlaceContext<'tcx>, location: Location) { self.super_static(static_, context, location); } fn visit_projection(&mut self, - lvalue: & $($mutability)* LvalueProjection<'tcx>, - context: LvalueContext<'tcx>, + place: & $($mutability)* PlaceProjection<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - self.super_projection(lvalue, context, location); + self.super_projection(place, context, location); } fn visit_projection_elem(&mut self, - lvalue: & $($mutability)* LvalueElem<'tcx>, - context: LvalueContext<'tcx>, + place: & $($mutability)* PlaceElem<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - self.super_projection_elem(lvalue, context, location); + self.super_projection_elem(place, context, location); } fn visit_branch(&mut self, @@ -209,7 +209,7 @@ macro_rules! make_mir_visitor { fn visit_ty(&mut self, ty: & $($mutability)* Ty<'tcx>, - _: Lookup) { + _: TyContext) { self.super_ty(ty); } @@ -256,13 +256,14 @@ macro_rules! make_mir_visitor { } fn visit_local_decl(&mut self, + local: Local, local_decl: & $($mutability)* LocalDecl<'tcx>) { - self.super_local_decl(local_decl); + self.super_local_decl(local, local_decl); } fn visit_local(&mut self, _local: & $($mutability)* Local, - _context: LvalueContext<'tcx>, + _context: PlaceContext<'tcx>, _location: Location) { } @@ -291,14 +292,13 @@ macro_rules! make_mir_visitor { self.visit_visibility_scope_data(scope); } - let lookup = Lookup::Src(SourceInfo { + self.visit_ty(&$($mutability)* mir.return_ty(), TyContext::ReturnTy(SourceInfo { span: mir.span, scope: ARGUMENT_VISIBILITY_SCOPE, - }); - self.visit_ty(&$($mutability)* mir.return_ty, lookup); + })); - for local_decl in &$($mutability)* mir.local_decls { - self.visit_local_decl(local_decl); + for local in mir.local_decls.indices() { + self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]); } self.visit_span(&$($mutability)* mir.span); @@ -350,32 +350,33 @@ macro_rules! make_mir_visitor { self.visit_source_info(source_info); match *kind { - StatementKind::Assign(ref $($mutability)* lvalue, + StatementKind::Assign(ref $($mutability)* place, ref $($mutability)* rvalue) => { - self.visit_assign(block, lvalue, rvalue, location); + self.visit_assign(block, place, rvalue, location); } StatementKind::EndRegion(_) => {} - StatementKind::Validate(_, ref $($mutability)* lvalues) => { - for operand in lvalues { - self.visit_lvalue(& $($mutability)* operand.lval, - LvalueContext::Validate, location); - self.visit_ty(& $($mutability)* operand.ty, Lookup::Loc(location)); + StatementKind::Validate(_, ref $($mutability)* places) => { + for operand in places { + self.visit_place(& $($mutability)* operand.place, + PlaceContext::Validate, location); + self.visit_ty(& $($mutability)* operand.ty, + TyContext::Location(location)); } } - StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => { - self.visit_lvalue(lvalue, LvalueContext::Store, location); + StatementKind::SetDiscriminant{ ref $($mutability)* place, .. } => { + self.visit_place(place, PlaceContext::Store, location); } StatementKind::StorageLive(ref $($mutability)* local) => { - self.visit_local(local, LvalueContext::StorageLive, location); + self.visit_local(local, PlaceContext::StorageLive, location); } StatementKind::StorageDead(ref $($mutability)* local) => { - self.visit_local(local, LvalueContext::StorageDead, location); + self.visit_local(local, PlaceContext::StorageDead, location); } StatementKind::InlineAsm { ref $($mutability)* outputs, ref $($mutability)* inputs, asm: _ } => { for output in & $($mutability)* outputs[..] { - self.visit_lvalue(output, LvalueContext::Store, location); + self.visit_place(output, PlaceContext::Store, location); } for input in & $($mutability)* inputs[..] { self.visit_operand(input, location); @@ -387,10 +388,10 @@ macro_rules! make_mir_visitor { fn super_assign(&mut self, _block: BasicBlock, - lvalue: &$($mutability)* Lvalue<'tcx>, + place: &$($mutability)* Place<'tcx>, rvalue: &$($mutability)* Rvalue<'tcx>, location: Location) { - self.visit_lvalue(lvalue, LvalueContext::Store, location); + self.visit_place(place, PlaceContext::Store, location); self.visit_rvalue(rvalue, location); } @@ -421,7 +422,7 @@ macro_rules! make_mir_visitor { ref values, ref targets } => { self.visit_operand(discr, source_location); - self.visit_ty(switch_ty, Lookup::Loc(source_location)); + self.visit_ty(switch_ty, TyContext::Location(source_location)); for value in &values[..] { self.visit_const_int(value, source_location); } @@ -439,7 +440,7 @@ macro_rules! make_mir_visitor { TerminatorKind::Drop { ref $($mutability)* location, target, unwind } => { - self.visit_lvalue(location, LvalueContext::Drop, source_location); + self.visit_place(location, PlaceContext::Drop, source_location); self.visit_branch(block, target); unwind.map(|t| self.visit_branch(block, t)); } @@ -448,7 +449,7 @@ macro_rules! make_mir_visitor { ref $($mutability)* value, target, unwind } => { - self.visit_lvalue(location, LvalueContext::Drop, source_location); + self.visit_place(location, PlaceContext::Drop, source_location); self.visit_operand(value, source_location); self.visit_branch(block, target); unwind.map(|t| self.visit_branch(block, t)); @@ -463,7 +464,7 @@ macro_rules! make_mir_visitor { self.visit_operand(arg, source_location); } if let Some((ref $($mutability)* destination, target)) = *destination { - self.visit_lvalue(destination, LvalueContext::Call, source_location); + self.visit_place(destination, PlaceContext::Call, source_location); self.visit_branch(block, target); } cleanup.map(|t| self.visit_branch(block, t)); @@ -486,8 +487,15 @@ macro_rules! make_mir_visitor { self.visit_operand(value, source_location); self.visit_branch(block, resume); drop.map(|t| self.visit_branch(block, t)); + } + TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { + self.visit_branch(block, real_target); + for target in imaginary_targets { + self.visit_branch(block, *target); + } + } } } @@ -524,21 +532,21 @@ macro_rules! make_mir_visitor { Rvalue::Ref(ref $($mutability)* r, bk, ref $($mutability)* path) => { self.visit_region(r, location); - self.visit_lvalue(path, LvalueContext::Borrow { + self.visit_place(path, PlaceContext::Borrow { region: *r, kind: bk }, location); } Rvalue::Len(ref $($mutability)* path) => { - self.visit_lvalue(path, LvalueContext::Inspect, location); + self.visit_place(path, PlaceContext::Inspect, location); } Rvalue::Cast(_cast_kind, ref $($mutability)* operand, ref $($mutability)* ty) => { self.visit_operand(operand, location); - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); } Rvalue::BinaryOp(_bin_op, @@ -555,12 +563,12 @@ macro_rules! make_mir_visitor { self.visit_operand(op, location); } - Rvalue::Discriminant(ref $($mutability)* lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Inspect, location); + Rvalue::Discriminant(ref $($mutability)* place) => { + self.visit_place(place, PlaceContext::Inspect, location); } Rvalue::NullaryOp(_op, ref $($mutability)* ty) => { - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); } Rvalue::Aggregate(ref $($mutability)* kind, @@ -568,7 +576,7 @@ macro_rules! make_mir_visitor { let kind = &$($mutability)* **kind; match *kind { AggregateKind::Array(ref $($mutability)* ty) => { - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); } AggregateKind::Tuple => { } @@ -603,8 +611,11 @@ macro_rules! make_mir_visitor { operand: & $($mutability)* Operand<'tcx>, location: Location) { match *operand { - Operand::Consume(ref $($mutability)* lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Consume, location); + Operand::Copy(ref $($mutability)* place) => { + self.visit_place(place, PlaceContext::Copy, location); + } + Operand::Move(ref $($mutability)* place) => { + self.visit_place(place, PlaceContext::Move, location); } Operand::Constant(ref $($mutability)* constant) => { self.visit_constant(constant, location); @@ -612,18 +623,18 @@ macro_rules! make_mir_visitor { } } - fn super_lvalue(&mut self, - lvalue: & $($mutability)* Lvalue<'tcx>, - context: LvalueContext<'tcx>, + fn super_place(&mut self, + place: & $($mutability)* Place<'tcx>, + context: PlaceContext<'tcx>, location: Location) { - match *lvalue { - Lvalue::Local(ref $($mutability)* local) => { + match *place { + Place::Local(ref $($mutability)* local) => { self.visit_local(local, context, location); } - Lvalue::Static(ref $($mutability)* static_) => { + Place::Static(ref $($mutability)* static_) => { self.visit_static(static_, context, location); } - Lvalue::Projection(ref $($mutability)* proj) => { + Place::Projection(ref $($mutability)* proj) => { self.visit_projection(proj, context, location); } } @@ -631,36 +642,36 @@ macro_rules! make_mir_visitor { fn super_static(&mut self, static_: & $($mutability)* Static<'tcx>, - _context: LvalueContext<'tcx>, + _context: PlaceContext<'tcx>, location: Location) { let Static { ref $($mutability)* def_id, ref $($mutability)* ty, } = *static_; self.visit_def_id(def_id, location); - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); } fn super_projection(&mut self, - proj: & $($mutability)* LvalueProjection<'tcx>, - context: LvalueContext<'tcx>, + proj: & $($mutability)* PlaceProjection<'tcx>, + context: PlaceContext<'tcx>, location: Location) { let Projection { ref $($mutability)* base, ref $($mutability)* elem, } = *proj; let context = if context.is_mutating_use() { - LvalueContext::Projection(Mutability::Mut) + PlaceContext::Projection(Mutability::Mut) } else { - LvalueContext::Projection(Mutability::Not) + PlaceContext::Projection(Mutability::Not) }; - self.visit_lvalue(base, context, location); + self.visit_place(base, context, location); self.visit_projection_elem(elem, context, location); } fn super_projection_elem(&mut self, - proj: & $($mutability)* LvalueElem<'tcx>, - _context: LvalueContext<'tcx>, + proj: & $($mutability)* PlaceElem<'tcx>, + _context: PlaceContext<'tcx>, location: Location) { match *proj { ProjectionElem::Deref => { @@ -668,10 +679,10 @@ macro_rules! make_mir_visitor { ProjectionElem::Subslice { from: _, to: _ } => { } ProjectionElem::Field(_field, ref $($mutability)* ty) => { - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(ref $($mutability)* local) => { - self.visit_local(local, LvalueContext::Consume, location); + self.visit_local(local, PlaceContext::Copy, location); } ProjectionElem::ConstantIndex { offset: _, min_length: _, @@ -683,6 +694,7 @@ macro_rules! make_mir_visitor { } fn super_local_decl(&mut self, + local: Local, local_decl: & $($mutability)* LocalDecl<'tcx>) { let LocalDecl { mutability: _, @@ -694,7 +706,10 @@ macro_rules! make_mir_visitor { is_user_variable: _, } = *local_decl; - self.visit_ty(ty, Lookup::Src(*source_info)); + self.visit_ty(ty, TyContext::LocalDecl { + local, + source_info: *source_info, + }); self.visit_source_info(source_info); self.visit_visibility_scope(lexical_scope); } @@ -718,7 +733,7 @@ macro_rules! make_mir_visitor { } = *constant; self.visit_span(span); - self.visit_ty(ty, Lookup::Loc(location)); + self.visit_ty(ty, TyContext::Location(location)); self.visit_literal(literal, location); } @@ -796,14 +811,27 @@ macro_rules! make_mir_visitor { make_mir_visitor!(Visitor,); make_mir_visitor!(MutVisitor,mut); -#[derive(Copy, Clone, Debug)] -pub enum Lookup { - Loc(Location), - Src(SourceInfo), +/// Extra information passed to `visit_ty` and friends to give context +/// about where the type etc appears. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum TyContext { + LocalDecl { + /// The index of the local variable we are visiting. + local: Local, + + /// The source location where this local variable was declared. + source_info: SourceInfo, + }, + + /// The return type of the function. + ReturnTy(SourceInfo), + + /// A type found at some location. + Location(Location), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum LvalueContext<'tcx> { +pub enum PlaceContext<'tcx> { // Appears as LHS of an assignment Store, @@ -819,10 +847,10 @@ pub enum LvalueContext<'tcx> { // Being borrowed Borrow { region: Region<'tcx>, kind: BorrowKind }, - // Used as base for another lvalue, e.g. `x` in `x.y`. + // Used as base for another place, e.g. `x` in `x.y`. // // The `Mutability` argument specifies whether the projection is being performed in order to - // (potentially) mutate the lvalue. For example, the projection `x.y` is marked as a mutation + // (potentially) mutate the place. For example, the projection `x.y` is marked as a mutation // in these cases: // // x.y = ...; @@ -835,7 +863,8 @@ pub enum LvalueContext<'tcx> { Projection(Mutability), // Consumed as part of an operand - Consume, + Copy, + Move, // Starting and ending a storage live range StorageLive, @@ -845,65 +874,67 @@ pub enum LvalueContext<'tcx> { Validate, } -impl<'tcx> LvalueContext<'tcx> { - /// Returns true if this lvalue context represents a drop. +impl<'tcx> PlaceContext<'tcx> { + /// Returns true if this place context represents a drop. pub fn is_drop(&self) -> bool { match *self { - LvalueContext::Drop => true, + PlaceContext::Drop => true, _ => false, } } - /// Returns true if this lvalue context represents a storage live or storage dead marker. + /// Returns true if this place context represents a storage live or storage dead marker. pub fn is_storage_marker(&self) -> bool { match *self { - LvalueContext::StorageLive | LvalueContext::StorageDead => true, + PlaceContext::StorageLive | PlaceContext::StorageDead => true, _ => false, } } - /// Returns true if this lvalue context represents a storage live marker. + /// Returns true if this place context represents a storage live marker. pub fn is_storage_live_marker(&self) -> bool { match *self { - LvalueContext::StorageLive => true, + PlaceContext::StorageLive => true, _ => false, } } - /// Returns true if this lvalue context represents a storage dead marker. + /// Returns true if this place context represents a storage dead marker. pub fn is_storage_dead_marker(&self) -> bool { match *self { - LvalueContext::StorageDead => true, + PlaceContext::StorageDead => true, _ => false, } } - /// Returns true if this lvalue context represents a use that potentially changes the value. + /// Returns true if this place context represents a use that potentially changes the value. pub fn is_mutating_use(&self) -> bool { match *self { - LvalueContext::Store | LvalueContext::Call | - LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | - LvalueContext::Projection(Mutability::Mut) | - LvalueContext::Drop => true, - LvalueContext::Inspect | - LvalueContext::Borrow { kind: BorrowKind::Shared, .. } | - LvalueContext::Borrow { kind: BorrowKind::Unique, .. } | - LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume | - LvalueContext::StorageLive | LvalueContext::StorageDead | - LvalueContext::Validate => false, + PlaceContext::Store | PlaceContext::Call | + PlaceContext::Borrow { kind: BorrowKind::Mut, .. } | + PlaceContext::Projection(Mutability::Mut) | + PlaceContext::Drop => true, + PlaceContext::Inspect | + PlaceContext::Borrow { kind: BorrowKind::Shared, .. } | + PlaceContext::Borrow { kind: BorrowKind::Unique, .. } | + PlaceContext::Projection(Mutability::Not) | + PlaceContext::Copy | PlaceContext::Move | + PlaceContext::StorageLive | PlaceContext::StorageDead | + PlaceContext::Validate => false, } } - /// Returns true if this lvalue context represents a use that does not change the value. + /// Returns true if this place context represents a use that does not change the value. pub fn is_nonmutating_use(&self) -> bool { match *self { - LvalueContext::Inspect | LvalueContext::Borrow { kind: BorrowKind::Shared, .. } | - LvalueContext::Borrow { kind: BorrowKind::Unique, .. } | - LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true, - LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store | - LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) | - LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead | - LvalueContext::Validate => false, + PlaceContext::Inspect | PlaceContext::Borrow { kind: BorrowKind::Shared, .. } | + PlaceContext::Borrow { kind: BorrowKind::Unique, .. } | + PlaceContext::Projection(Mutability::Not) | + PlaceContext::Copy | PlaceContext::Move => true, + PlaceContext::Borrow { kind: BorrowKind::Mut, .. } | PlaceContext::Store | + PlaceContext::Call | PlaceContext::Projection(Mutability::Mut) | + PlaceContext::Drop | PlaceContext::StorageLive | PlaceContext::StorageDead | + PlaceContext::Validate => false, } } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b1bf893cfd8be..81e18fe536d6e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -138,6 +138,34 @@ impl OutputType { } } + fn from_shorthand(shorthand: &str) -> Option { + Some(match shorthand { + "asm" => OutputType::Assembly, + "llvm-ir" => OutputType::LlvmAssembly, + "mir" => OutputType::Mir, + "llvm-bc" => OutputType::Bitcode, + "obj" => OutputType::Object, + "metadata" => OutputType::Metadata, + "link" => OutputType::Exe, + "dep-info" => OutputType::DepInfo, + _ => return None, + }) + } + + fn shorthands_display() -> String { + format!( + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + OutputType::Bitcode.shorthand(), + OutputType::Assembly.shorthand(), + OutputType::LlvmAssembly.shorthand(), + OutputType::Mir.shorthand(), + OutputType::Object.shorthand(), + OutputType::Metadata.shorthand(), + OutputType::Exe.shorthand(), + OutputType::DepInfo.shorthand(), + ) + } + pub fn extension(&self) -> &'static str { match *self { OutputType::Bitcode => "bc", @@ -155,7 +183,8 @@ impl OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { HumanReadable(ColorConfig), - Json, + Json(bool), + Short(ColorConfig), } impl Default for ErrorOutputType { @@ -333,6 +362,9 @@ top_level_options!( debugging_opts: DebuggingOptions [TRACKED], prints: Vec [UNTRACKED], + // Determines which borrow checker(s) to run. This is the parsed, sanitized + // version of `debugging_opts.borrowck`, which is just a plain string. + borrowck_mode: BorrowckMode [UNTRACKED], cg: CodegenOptions [TRACKED], // FIXME(mw): We track this for now but it actually doesn't make too // much sense: The value of this option can stay the same @@ -350,6 +382,14 @@ top_level_options!( // is currently just a hack and will be removed eventually, so please // try to not rely on this too much. actually_rustdoc: bool [TRACKED], + + // Specifications of codegen units / ThinLTO which are forced as a + // result of parsing command line options. These are not necessarily + // what rustc was invoked with, but massaged a bit to agree with + // commands like `--emit llvm-ir` which they're often incompatible with + // if we otherwise use the defaults of rustc. + cli_forced_codegen_units: Option [UNTRACKED], + cli_forced_thinlto: Option [UNTRACKED], } ); @@ -364,10 +404,37 @@ pub enum PrintRequest { TargetFeatures, RelocationModels, CodeModels, + TlsModels, TargetSpec, NativeStaticLibs, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum BorrowckMode { + Ast, + Mir, + Compare, +} + +impl BorrowckMode { + /// Should we emit the AST-based borrow checker errors? + pub fn use_ast(self) -> bool { + match self { + BorrowckMode::Ast => true, + BorrowckMode::Compare => true, + BorrowckMode::Mir => false, + } + } + /// Should we emit the MIR-based borrow checker errors? + pub fn use_mir(self) -> bool { + match self { + BorrowckMode::Ast => false, + BorrowckMode::Compare => true, + BorrowckMode::Mir => true, + } + } +} + pub enum Input { /// Load source from file File(PathBuf), @@ -406,9 +473,7 @@ impl_stable_hash_for!(struct self::OutputFilenames { outputs }); -/// Codegen unit names generated by the numbered naming scheme will contain this -/// marker right before the index of the codegen unit. -pub const NUMBERED_CODEGEN_UNIT_MARKER: &'static str = ".cgu-"; +pub const RUST_CGU_EXT: &str = "rcgu"; impl OutputFilenames { pub fn path(&self, flavor: OutputType) -> PathBuf { @@ -439,22 +504,14 @@ impl OutputFilenames { let mut extension = String::new(); if let Some(codegen_unit_name) = codegen_unit_name { - if codegen_unit_name.contains(NUMBERED_CODEGEN_UNIT_MARKER) { - // If we use the numbered naming scheme for modules, we don't want - // the files to look like ... - // but simply .. - let marker_offset = codegen_unit_name.rfind(NUMBERED_CODEGEN_UNIT_MARKER) - .unwrap(); - let index_offset = marker_offset + NUMBERED_CODEGEN_UNIT_MARKER.len(); - extension.push_str(&codegen_unit_name[index_offset .. ]); - } else { - extension.push_str(codegen_unit_name); - }; + extension.push_str(codegen_unit_name); } if !ext.is_empty() { if !extension.is_empty() { extension.push_str("."); + extension.push_str(RUST_CGU_EXT); + extension.push_str("."); } extension.push_str(ext); @@ -503,6 +560,7 @@ pub fn basic_options() -> Options { incremental: None, debugging_opts: basic_debugging_options(), prints: Vec::new(), + borrowck_mode: BorrowckMode::Ast, cg: basic_codegen_options(), error_format: ErrorOutputType::default(), externs: Externs(BTreeMap::new()), @@ -512,6 +570,8 @@ pub fn basic_options() -> Options { unstable_features: UnstableFeatures::Disallow, debug_assertions: true, actually_rustdoc: false, + cli_forced_codegen_units: None, + cli_forced_thinlto: None, } } @@ -529,11 +589,6 @@ impl Options { (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) } - pub fn single_codegen_unit(&self) -> bool { - self.incremental.is_none() || - self.cg.codegen_units == 1 - } - pub fn file_path_mapping(&self) -> FilePathMapping { FilePathMapping::new( self.debugging_opts.remap_path_prefix_from.iter().zip( @@ -791,7 +846,7 @@ macro_rules! options { fn parse_opt_uint(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok(); slot.is_some() } - None => { *slot = None; true } + None => { *slot = None; false } } } @@ -875,7 +930,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, build_codegen_options, "C", "codegen", CG_OPTIONS, cg_type_desc, cgsetters, ar: Option = (None, parse_opt_string, [UNTRACKED], - "tool to assemble archives with"), + "this option is deprecated and does nothing"), linker: Option = (None, parse_opt_string, [UNTRACKED], "system linker to link outputs with"), link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], @@ -924,7 +979,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "metadata to mangle symbol names with"), extra_filename: String = ("".to_string(), parse_string, [UNTRACKED], "extra data to put in each output filename"), - codegen_units: usize = (1, parse_uint, [UNTRACKED], + codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], "divide crate into N units to optimize in parallel"), remark: Passes = (SomePasses(Vec::new()), parse_passes, [UNTRACKED], "print remarks for these optimization passes (space separated, or \"all\")"), @@ -954,8 +1009,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "make unnamed regions display as '# (where # is some non-ident unique id)"), emit_end_regions: bool = (false, parse_bool, [UNTRACKED], "emit EndRegion as part of MIR; enable transforms that solely process EndRegion"), - borrowck_mir: bool = (false, parse_bool, [UNTRACKED], - "implicitly treat functions as if they have `#[rustc_mir_borrowck]` attribute"), + borrowck: Option = (None, parse_opt_string, [UNTRACKED], + "select which borrowck is used (`ast`, `mir`, or `compare`)"), time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass"), count_llvm_insns: bool = (false, parse_bool, @@ -998,8 +1053,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, save_analysis: bool = (false, parse_bool, [UNTRACKED], "write syntax and type analysis (in JSON format) information, in \ addition to normal output"), - print_move_fragments: bool = (false, parse_bool, [UNTRACKED], - "print out move-fragment data for every fn"), flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED], "include loan analysis data in --unpretty flowgraph output"), flowgraph_print_moves: bool = (false, parse_bool, [UNTRACKED], @@ -1017,16 +1070,20 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run all passes except translation; no output"), treat_err_as_bug: bool = (false, parse_bool, [TRACKED], "treat all errors that occur as bugs"), + external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show macro backtraces even for non-local macros"), continue_parse_after_error: bool = (false, parse_bool, [TRACKED], "attempt to recover from parse errors (experimental)"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation (experimental)"), - incremental_cc: bool = (false, parse_bool, [UNTRACKED], - "enable cross-crate incremental compilation (even more experimental)"), + incremental_queries: bool = (true, parse_bool, [UNTRACKED], + "enable incremental compilation support for queries (experimental)"), incremental_info: bool = (false, parse_bool, [UNTRACKED], "print high-level information about incremental reuse (or the lack thereof)"), incremental_dump_hash: bool = (false, parse_bool, [UNTRACKED], "dump hash information in textual format to stdout"), + incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], + "verify incr. comp. hashes of green query instances"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], @@ -1061,10 +1118,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the translation item collection pass"), mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), + mutable_noalias: bool = (false, parse_bool, [UNTRACKED], + "emit noalias metadata for mutable references"), dump_mir: Option = (None, parse_opt_string, [UNTRACKED], "dump MIR state at various points in translation"), dump_mir_dir: Option = (None, parse_opt_string, [UNTRACKED], "the directory the MIR is dumped into"), + dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files"), dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], "if set, exclude the pass number when dumping MIR (used in tests)"), mir_emit_validate: usize = (0, parse_uint, [TRACKED], @@ -1106,6 +1167,19 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run the non-lexical lifetimes MIR pass"), trans_time_graph: bool = (false, parse_bool, [UNTRACKED], "generate a graphical HTML report of time spent in trans and LLVM"), + thinlto: Option = (None, parse_opt_bool, [TRACKED], + "enable ThinLTO when possible"), + inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], + "control whether #[inline] functions are in all cgus"), + tls_model: Option = (None, parse_opt_string, [TRACKED], + "choose the TLS model to use (rustc --print tls-models for details)"), + saturating_float_casts: bool = (false, parse_bool, [TRACKED], + "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ + the max/min integer respectively, and NaN is mapped to 0"), + lower_128bit_ops: Option = (None, parse_opt_bool, [TRACKED], + "rewrite operators on i128 and u128 into lang item calls (typically provided \ + by compiler-builtins) so translation doesn't need to support them, + overriding the default for the current target"), } pub fn default_lib_output() -> CrateType { @@ -1332,7 +1406,7 @@ pub fn rustc_short_optgroups() -> Vec { print on stdout", "[crate-name|file-names|sysroot|cfg|target-list|\ target-cpus|target-features|relocation-models|\ - code-models|target-spec-json|native-static-libs]"), + code-models|tls-models|target-spec-json|native-static-libs]"), 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"), @@ -1367,7 +1441,7 @@ pub fn rustc_optgroups() -> Vec { opt::multi("Z", "", "Set internal debugging options", "FLAG"), opt::opt_s("", "error-format", "How errors and other messages are produced", - "human|json"), + "human|json|short"), opt::opt_s("", "color", "Configure coloring of output: auto = colorize, if output goes to a tty (default); always = always colorize output; @@ -1434,15 +1508,24 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) // opt_present because the latter will panic. let error_format = if matches.opts_present(&["error-format".to_owned()]) { match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { - Some("human") => ErrorOutputType::HumanReadable(color), - Some("json") => ErrorOutputType::Json, - + Some("human") => ErrorOutputType::HumanReadable(color), + Some("json") => ErrorOutputType::Json(false), + Some("pretty-json") => ErrorOutputType::Json(true), + Some("short") => { + if nightly_options::is_unstable_enabled(matches) { + ErrorOutputType::Short(color) + } else { + early_error(ErrorOutputType::default(), + &format!("the `-Z unstable-options` flag must also be passed to \ + enable the short error message option")); + } + } None => ErrorOutputType::HumanReadable(color), Some(arg) => { early_error(ErrorOutputType::HumanReadable(color), - &format!("argument for --error-format must be human or json (instead \ - was `{}`)", + &format!("argument for --error-format must be `human`, `json` or \ + `short` (instead was `{}`)", arg)) } } @@ -1473,26 +1556,25 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) }) }); - let debugging_opts = build_debugging_options(matches, error_format); + let mut debugging_opts = build_debugging_options(matches, error_format); + + if !debugging_opts.unstable_options && error_format == ErrorOutputType::Json(true) { + early_error(ErrorOutputType::Json(false), + "--error-format=pretty-json is unstable"); + } let mut output_types = BTreeMap::new(); if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { let mut parts = output_type.splitn(2, '='); - let output_type = match parts.next().unwrap() { - "asm" => OutputType::Assembly, - "llvm-ir" => OutputType::LlvmAssembly, - "mir" => OutputType::Mir, - "llvm-bc" => OutputType::Bitcode, - "obj" => OutputType::Object, - "metadata" => OutputType::Metadata, - "link" => OutputType::Exe, - "dep-info" => OutputType::DepInfo, - part => { - early_error(error_format, &format!("unknown emission type: `{}`", - part)) - } + let shorthand = parts.next().unwrap(); + let output_type = match OutputType::from_shorthand(shorthand) { + Some(output_type) => output_type, + None => early_error(error_format, &format!( + "unknown emission type: `{}` - expected one of: {}", + shorthand, OutputType::shorthands_display(), + )), }; let path = parts.next().map(PathBuf::from); output_types.insert(output_type, path); @@ -1521,43 +1603,43 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) } let mut cg = build_codegen_options(matches, error_format); + let mut codegen_units = cg.codegen_units; + let mut thinlto = None; // Issue #30063: if user requests llvm-related output to one // particular path, disable codegen-units. - if matches.opt_present("o") && cg.codegen_units != 1 { - let incompatible: Vec<_> = output_types.iter() - .map(|ot_path| ot_path.0) - .filter(|ot| { - !ot.is_compatible_with_codegen_units_and_single_output_file() - }).collect(); - if !incompatible.is_empty() { - for ot in &incompatible { - early_warn(error_format, &format!("--emit={} with -o incompatible with \ - -C codegen-units=N for N > 1", - ot.shorthand())); + let incompatible: Vec<_> = output_types.iter() + .map(|ot_path| ot_path.0) + .filter(|ot| { + !ot.is_compatible_with_codegen_units_and_single_output_file() + }) + .map(|ot| ot.shorthand()) + .collect(); + if !incompatible.is_empty() { + match codegen_units { + Some(n) if n > 1 => { + if matches.opt_present("o") { + for ot in &incompatible { + early_warn(error_format, &format!("--emit={} with -o incompatible with \ + -C codegen-units=N for N > 1", + ot)); + } + early_warn(error_format, "resetting to default -C codegen-units=1"); + codegen_units = Some(1); + thinlto = Some(false); + } + } + _ => { + codegen_units = Some(1); + thinlto = Some(false); } - early_warn(error_format, "resetting to default -C codegen-units=1"); - cg.codegen_units = 1; } } - if cg.codegen_units < 1 { + if codegen_units == Some(0) { early_error(error_format, "Value for codegen units must be a positive nonzero integer"); } - // It's possible that we have `codegen_units > 1` but only one item in - // `trans.modules`. We could theoretically proceed and do LTO in that - // case, but it would be confusing to have the validity of - // `-Z lto -C codegen-units=2` depend on details of the crate being - // compiled, so we complain regardless. - if cg.lto && cg.codegen_units > 1 { - // This case is impossible to handle because LTO expects to be able - // to combine the entire crate and all its dependencies into a - // single compilation unit, but each codegen unit is in a separate - // LLVM context, so they can't easily be combined. - early_error(error_format, "can't perform LTO when using multiple codegen units"); - } - if cg.lto && debugging_opts.incremental.is_some() { early_error(error_format, "can't perform LTO when compiling incrementally"); } @@ -1579,6 +1661,10 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) prints.push(PrintRequest::CodeModels); cg.code_model = None; } + if debugging_opts.tls_model.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::TlsModels); + debugging_opts.tls_model = None; + } let cg = cg; @@ -1678,6 +1764,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) "target-features" => PrintRequest::TargetFeatures, "relocation-models" => PrintRequest::RelocationModels, "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, "native-static-libs" => PrintRequest::NativeStaticLibs, "target-spec-json" => { if nightly_options::is_unstable_enabled(matches) { @@ -1694,6 +1781,15 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) } })); + let borrowck_mode = match debugging_opts.borrowck.as_ref().map(|s| &s[..]) { + None | Some("ast") => BorrowckMode::Ast, + Some("mir") => BorrowckMode::Mir, + Some("compare") => BorrowckMode::Compare, + Some(m) => { + early_error(error_format, &format!("unknown borrowck mode `{}`", m)) + }, + }; + if !cg.remark.is_empty() && debuginfo == NoDebugInfo { early_warn(error_format, "-C remark will not show source locations without \ --debuginfo"); @@ -1735,6 +1831,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) incremental, debugging_opts, prints, + borrowck_mode, cg, error_format, externs: Externs(externs), @@ -1744,6 +1841,8 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) unstable_features: UnstableFeatures::from_environment(), debug_assertions, actually_rustdoc: false, + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto: thinlto, }, cfg) } @@ -2055,7 +2154,7 @@ mod tests { let registry = errors::registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); - assert!(!sess.diagnostic().can_emit_warnings); + assert!(!sess.diagnostic().flags.can_emit_warnings); } { @@ -2066,7 +2165,7 @@ mod tests { let registry = errors::registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); - assert!(sess.diagnostic().can_emit_warnings); + assert!(sess.diagnostic().flags.can_emit_warnings); } { @@ -2076,7 +2175,7 @@ mod tests { let registry = errors::registry::Registry::new(&[]); let (sessopts, _) = build_session_options_and_crate_config(&matches); let sess = build_session(sessopts, None, registry); - assert!(sess.diagnostic().can_emit_warnings); + assert!(sess.diagnostic().flags.can_emit_warnings); } } @@ -2259,46 +2358,46 @@ mod tests { let mut v5 = super::basic_options(); // Reference - v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); // Native changed - v2.search_paths.add_path("native=XXX", super::ErrorOutputType::Json); - v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v2.search_paths.add_path("native=XXX", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); // Crate changed - v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v2.search_paths.add_path("crate=XXX", super::ErrorOutputType::Json); - v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("crate=XXX", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); // Dependency changed - v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v3.search_paths.add_path("dependency=XXX", super::ErrorOutputType::Json); - v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("dependency=XXX", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); // Framework changed - v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v4.search_paths.add_path("framework=XXX", super::ErrorOutputType::Json); - v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("framework=XXX", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); // All changed - v5.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v5.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v5.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v5.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v5.search_paths.add_path("all=XXX", super::ErrorOutputType::Json); + v5.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v5.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v5.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v5.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v5.search_paths.add_path("all=XXX", super::ErrorOutputType::Json(false)); assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); @@ -2321,29 +2420,29 @@ mod tests { let mut v4 = super::basic_options(); // Reference - v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json); - - v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); - - v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); - v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v3.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json); - - v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json); - v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json); - v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json); - v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); - v4.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); + + v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); + + v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); + + v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json(false)); + v4.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json(false)); assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); @@ -2447,7 +2546,7 @@ mod tests { opts.cg.extra_filename = String::from("extra-filename"); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.cg.codegen_units = 42; + opts.cg.codegen_units = Some(42); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.cg.remark = super::SomePasses(vec![String::from("pass1"), @@ -2519,6 +2618,10 @@ mod tests { opts.cg.code_model = Some(String::from("code model")); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + opts = reference.clone(); + opts.debugging_opts.tls_model = Some(String::from("tls model")); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + opts = reference.clone(); opts.cg.metadata = vec![String::from("A"), String::from("B")]; assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); @@ -2580,8 +2683,6 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.save_analysis = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_move_fragments = true; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.flowgraph_print_loans = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.flowgraph_print_moves = true; @@ -2616,6 +2717,8 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.dump_mir_dir = Some(String::from("abc")); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.dump_mir_graphviz = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); // Make sure changing a [TRACKED] option changes the hash opts = reference.clone(); diff --git a/src/librustc/session/filesearch.rs b/src/librustc/session/filesearch.rs index 1004b2826022a..b636fc6c9950a 100644 --- a/src/librustc/session/filesearch.rs +++ b/src/librustc/session/filesearch.rs @@ -28,8 +28,6 @@ pub enum FileMatch { } // A module for searching for libraries -// FIXME (#2658): I'm not happy how this module turned out. Should -// probably just be folded into cstore. pub struct FileSearch<'a> { pub sysroot: &'a Path, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index e87443619ece7..df5805bacd41a 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -12,6 +12,7 @@ pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo}; pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo}; use hir::def_id::{CrateNum, DefIndex}; +use ich::Fingerprint; use lint; use middle::allocator::AllocatorKind; @@ -23,13 +24,12 @@ use util::nodemap::{FxHashMap, FxHashSet}; use util::common::{duration_to_secs_str, ErrorReported}; use syntax::ast::NodeId; -use errors::{self, DiagnosticBuilder}; +use errors::{self, DiagnosticBuilder, DiagnosticId}; use errors::emitter::{Emitter, EmitterWriter}; use syntax::json::JsonEmitter; use syntax::feature_gate; use syntax::parse; use syntax::parse::ParseSess; -use syntax::symbol::Symbol; use syntax::{ast, codemap}; use syntax::feature_gate::AttributeType; use syntax_pos::{Span, MultiSpan}; @@ -54,42 +54,41 @@ pub mod config; pub mod filesearch; pub mod search_paths; -// Represents the data associated with a compilation -// session for a single crate. +/// Represents the data associated with a compilation +/// session for a single crate. pub struct Session { pub target: config::Config, pub host: Target, pub opts: config::Options, pub parse_sess: ParseSess, - // For a library crate, this is always none + /// For a library crate, this is always none pub entry_fn: RefCell>, pub entry_type: Cell>, pub plugin_registrar_fn: Cell>, pub derive_registrar_fn: Cell>, pub default_sysroot: Option, - // The name of the root source file of the crate, in the local file system. - // The path is always expected to be absolute. `None` means that there is no - // source file. + /// The name of the root source file of the crate, in the local file system. + /// `None` means that there is no source file. pub local_crate_source_file: Option, - // The directory the compiler has been executed in plus a flag indicating - // if the value stored here has been affected by path remapping. + /// The directory the compiler has been executed in plus a flag indicating + /// if the value stored here has been affected by path remapping. pub working_dir: (String, bool), pub lint_store: RefCell, pub buffered_lints: RefCell>, - /// Set of (LintId, Option, message) tuples tracking lint + /// Set of (DiagnosticId, Option, message) tuples tracking /// (sub)diagnostics that have been set once, but should not be set again, - /// in order to avoid redundantly verbose output (Issue #24690). - pub one_time_diagnostics: RefCell, String)>>, + /// in order to avoid redundantly verbose output (Issue #24690, #44953). + pub one_time_diagnostics: RefCell, String)>>, pub plugin_llvm_passes: RefCell>, pub plugin_attributes: RefCell>, pub crate_types: RefCell>, pub dependency_formats: RefCell, - // The crate_disambiguator is constructed out of all the `-C metadata` - // arguments passed to the compiler. Its value together with the crate-name - // forms a unique global identifier for the crate. It is used to allow - // multiple crates with the same name to coexist. See the - // trans::back::symbol_names module for more information. - pub crate_disambiguator: RefCell>, + /// The crate_disambiguator is constructed out of all the `-C metadata` + /// arguments passed to the compiler. Its value together with the crate-name + /// forms a unique global identifier for the crate. It is used to allow + /// multiple crates with the same name to coexist. See the + /// trans::back::symbol_names module for more information. + pub crate_disambiguator: RefCell>, pub features: RefCell, /// The maximum recursion limit for potentially infinitely recursive @@ -144,17 +143,17 @@ pub struct Session { } pub struct PerfStats { - // The accumulated time needed for computing the SVH of the crate + /// The accumulated time needed for computing the SVH of the crate pub svh_time: Cell, - // The accumulated time spent on computing incr. comp. hashes + /// The accumulated time spent on computing incr. comp. hashes pub incr_comp_hashes_time: Cell, - // The number of incr. comp. hash computations performed + /// The number of incr. comp. hash computations performed pub incr_comp_hashes_count: Cell, - // The number of bytes hashed when computing ICH values + /// The number of bytes hashed when computing ICH values pub incr_comp_bytes_hashed: Cell, - // The accumulated time spent on computing symbol hashes + /// The accumulated time spent on computing symbol hashes pub symbol_hash_time: Cell, - // The accumulated time spent decoding def path tables from metadata + /// The accumulated time spent decoding def path tables from metadata pub decode_def_path_tables_time: Cell, } @@ -165,10 +164,19 @@ enum DiagnosticBuilderMethod { // add more variants as needed to support one-time diagnostics } +/// Diagnostic message ID—used by `Session.one_time_diagnostics` to avoid +/// emitting the same message more than once +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticMessageId { + ErrorId(u16), // EXXXX error code as integer + LintId(lint::LintId), + StabilityId(u32) // issue number +} + impl Session { - pub fn local_crate_disambiguator(&self) -> Symbol { + pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { match *self.crate_disambiguator.borrow() { - Some(sym) => sym, + Some(value) => value, None => bug!("accessing disambiguator before initialization"), } } @@ -181,7 +189,7 @@ impl Session { pub fn struct_span_warn_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } @@ -197,7 +205,7 @@ impl Session { pub fn struct_span_err_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_err_with_code(sp, msg, code) } @@ -205,7 +213,11 @@ impl Session { pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.diagnostic().struct_err(msg) } - pub fn struct_err_with_code<'a>(&'a self, msg: &str, code: &str) -> DiagnosticBuilder<'a> { + pub fn struct_err_with_code<'a>( + &'a self, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'a> { self.diagnostic().struct_err_with_code(msg, code) } pub fn struct_span_fatal<'a, S: Into>(&'a self, @@ -217,7 +229,7 @@ impl Session { pub fn struct_span_fatal_with_code<'a, S: Into>(&'a self, sp: S, msg: &str, - code: &str) + code: DiagnosticId) -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } @@ -228,7 +240,12 @@ impl Session { pub fn span_fatal>(&self, sp: S, msg: &str) -> ! { panic!(self.diagnostic().span_fatal(sp, msg)) } - pub fn span_fatal_with_code>(&self, sp: S, msg: &str, code: &str) -> ! { + pub fn span_fatal_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> ! { panic!(self.diagnostic().span_fatal_with_code(sp, msg, code)) } pub fn fatal(&self, msg: &str) -> ! { @@ -244,7 +261,7 @@ impl Session { pub fn span_err>(&self, sp: S, msg: &str) { self.diagnostic().span_err(sp, msg) } - pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { self.diagnostic().span_err_with_code(sp, &msg, code) } pub fn err(&self, msg: &str) { @@ -277,7 +294,7 @@ impl Session { pub fn span_warn>(&self, sp: S, msg: &str) { self.diagnostic().span_warn(sp, msg) } - pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: &str) { + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { self.diagnostic().span_warn_with_code(sp, msg, code) } pub fn warn(&self, msg: &str) { @@ -338,12 +355,15 @@ impl Session { /// Analogous to calling methods on the given `DiagnosticBuilder`, but /// deduplicates on lint ID, span (if any), and message for this `Session` - /// if we're not outputting in JSON mode. fn diag_once<'a, 'b>(&'a self, diag_builder: &'b mut DiagnosticBuilder<'a>, method: DiagnosticBuilderMethod, lint: &'static lint::Lint, message: &str, span: Option) { - let mut do_method = || { + + let lint_id = DiagnosticMessageId::LintId(lint::LintId::of(lint)); + let id_span_message = (lint_id, span, message.to_owned()); + let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message); + if fresh { match method { DiagnosticBuilderMethod::Note => { diag_builder.note(message); @@ -352,22 +372,6 @@ impl Session { diag_builder.span_note(span.expect("span_note expects a span"), message); } } - }; - - match self.opts.error_format { - // when outputting JSON for tool consumption, the tool might want - // the duplicates - config::ErrorOutputType::Json => { - do_method() - }, - _ => { - let lint_id = lint::LintId::of(lint); - let id_span_message = (lint_id, span, message.to_owned()); - let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message); - if fresh { - do_method() - } - } } } @@ -412,10 +416,10 @@ impl Session { pub fn emit_end_regions(&self) -> bool { self.opts.debugging_opts.emit_end_regions || (self.opts.debugging_opts.mir_emit_validate > 0) || - self.opts.debugging_opts.borrowck_mir + self.opts.borrowck_mode.use_mir() } pub fn lto(&self) -> bool { - self.opts.cg.lto + self.opts.cg.lto || self.target.target.options.requires_lto } /// Returns the panic strategy for this compile session. If the user explicitly selected one /// using '-C panic', use that, otherwise use the panic strategy defined by the target. @@ -472,14 +476,18 @@ impl Session { /// Returns the symbol name for the registrar function, /// given the crate Svh and the function DefIndex. - pub fn generate_plugin_registrar_symbol(&self, disambiguator: Symbol, index: DefIndex) + pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator, + index: DefIndex) -> String { - format!("__rustc_plugin_registrar__{}_{}", disambiguator, index.as_usize()) + format!("__rustc_plugin_registrar__{}_{}", disambiguator.to_fingerprint().to_hex(), + index.as_usize()) } - pub fn generate_derive_registrar_symbol(&self, disambiguator: Symbol, index: DefIndex) + pub fn generate_derive_registrar_symbol(&self, disambiguator: CrateDisambiguator, + index: DefIndex) -> String { - format!("__rustc_derive_registrar__{}_{}", disambiguator, index.as_usize()) + format!("__rustc_derive_registrar__{}_{}", disambiguator.to_fingerprint().to_hex(), + index.as_usize()) } pub fn sysroot<'a>(&'a self) -> &'a Path { @@ -637,6 +645,104 @@ impl Session { } ret } + + /// Returns the number of codegen units that should be used for this + /// compilation + pub fn codegen_units(&self) -> usize { + if let Some(n) = self.opts.cli_forced_codegen_units { + return n + } + if let Some(n) = self.target.target.options.default_codegen_units { + return n as usize + } + + // Why is 16 codegen units the default all the time? + // + // The main reason for enabling multiple codegen units by default is to + // leverage the ability for the trans backend to do translation and + // codegen in parallel. This allows us, especially for large crates, to + // make good use of all available resources on the machine once we've + // hit that stage of compilation. Large crates especially then often + // take a long time in trans/codegen and this helps us amortize that + // cost. + // + // Note that a high number here doesn't mean that we'll be spawning a + // large number of threads in parallel. The backend of rustc contains + // global rate limiting through the `jobserver` crate so we'll never + // overload the system with too much work, but rather we'll only be + // optimizing when we're otherwise cooperating with other instances of + // rustc. + // + // Rather a high number here means that we should be able to keep a lot + // of idle cpus busy. By ensuring that no codegen unit takes *too* long + // to build we'll be guaranteed that all cpus will finish pretty closely + // to one another and we should make relatively optimal use of system + // resources + // + // Note that the main cost of codegen units is that it prevents LLVM + // from inlining across codegen units. Users in general don't have a lot + // of control over how codegen units are split up so it's our job in the + // compiler to ensure that undue performance isn't lost when using + // codegen units (aka we can't require everyone to slap `#[inline]` on + // everything). + // + // If we're compiling at `-O0` then the number doesn't really matter too + // much because performance doesn't matter and inlining is ok to lose. + // In debug mode we just want to try to guarantee that no cpu is stuck + // doing work that could otherwise be farmed to others. + // + // In release mode, however (O1 and above) performance does indeed + // matter! To recover the loss in performance due to inlining we'll be + // enabling ThinLTO by default (the function for which is just below). + // This will ensure that we recover any inlining wins we otherwise lost + // through codegen unit partitioning. + // + // --- + // + // Ok that's a lot of words but the basic tl;dr; is that we want a high + // number here -- but not too high. Additionally we're "safe" to have it + // always at the same number at all optimization levels. + // + // As a result 16 was chosen here! Mostly because it was a power of 2 + // and most benchmarks agreed it was roughly a local optimum. Not very + // scientific. + match self.opts.optimize { + config::OptLevel::No => 16, + _ => 1, // FIXME(#46346) this should be 16 + } + } + + /// Returns whether ThinLTO is enabled for this compilation + pub fn thinlto(&self) -> bool { + // If processing command line options determined that we're incompatible + // with ThinLTO (e.g. `-C lto --emit llvm-ir`) then return that option. + if let Some(enabled) = self.opts.cli_forced_thinlto { + return enabled + } + + // If explicitly specified, use that with the next highest priority + if let Some(enabled) = self.opts.debugging_opts.thinlto { + return enabled + } + + // If there's only one codegen unit and LTO isn't enabled then there's + // no need for ThinLTO so just return false. + if self.codegen_units() == 1 && !self.lto() { + return false + } + + // Right now ThinLTO isn't compatible with incremental compilation. + if self.opts.incremental.is_some() { + return false + } + + // Now we're in "defaults" territory. By default we enable ThinLTO for + // optimized compiles (anything greater than O0). + match self.opts.optimize { + config::OptLevel::No => false, + _ => true, + } + } } pub fn build_session(sopts: config::Options, @@ -669,31 +775,42 @@ pub fn build_session_with_codemap(sopts: config::Options, .unwrap_or(false); let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); - let can_print_warnings = !(warnings_allow || cap_lints_allow); + let can_emit_warnings = !(warnings_allow || cap_lints_allow); let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; + let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; + let emitter: Box = match (sopts.error_format, emitter_dest) { (config::ErrorOutputType::HumanReadable(color_config), None) => { - Box::new(EmitterWriter::stderr(color_config, - Some(codemap.clone()))) + Box::new(EmitterWriter::stderr(color_config, Some(codemap.clone()), false)) } (config::ErrorOutputType::HumanReadable(_), Some(dst)) => { - Box::new(EmitterWriter::new(dst, - Some(codemap.clone()))) + Box::new(EmitterWriter::new(dst, Some(codemap.clone()), false)) } - (config::ErrorOutputType::Json, None) => { - Box::new(JsonEmitter::stderr(Some(registry), codemap.clone())) + (config::ErrorOutputType::Json(pretty), None) => { + Box::new(JsonEmitter::stderr(Some(registry), codemap.clone(), pretty)) } - (config::ErrorOutputType::Json, Some(dst)) => { - Box::new(JsonEmitter::new(dst, Some(registry), codemap.clone())) + (config::ErrorOutputType::Json(pretty), Some(dst)) => { + Box::new(JsonEmitter::new(dst, Some(registry), codemap.clone(), pretty)) + } + (config::ErrorOutputType::Short(color_config), None) => { + Box::new(EmitterWriter::stderr(color_config, Some(codemap.clone()), true)) + } + (config::ErrorOutputType::Short(_), Some(dst)) => { + Box::new(EmitterWriter::new(dst, Some(codemap.clone()), true)) } }; let diagnostic_handler = - errors::Handler::with_emitter(can_print_warnings, - treat_err_as_bug, - emitter); + errors::Handler::with_emitter_and_flags( + emitter, + errors::HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + external_macro_backtrace, + .. Default::default() + }); build_session_(sopts, local_crate_source_file, @@ -722,7 +839,6 @@ pub fn build_session_(sopts: config::Options, let file_path_mapping = sopts.file_path_mapping(); - // Make the path absolute, if necessary let local_crate_source_file = local_crate_source_file.map(|path| { file_path_mapping.map_prefix(path.to_string_lossy().into_owned()).0 }); @@ -733,7 +849,12 @@ pub fn build_session_(sopts: config::Options, let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); let print_fuel = Cell::new(0); - let working_dir = env::current_dir().unwrap().to_string_lossy().into_owned(); + let working_dir = match env::current_dir() { + Ok(dir) => dir.to_string_lossy().into_owned(), + Err(e) => { + panic!(p_s.span_diagnostic.fatal(&format!("Current directory is invalid: {}", e))) + } + }; let working_dir = file_path_mapping.map_prefix(working_dir); let sess = Session { @@ -803,27 +924,47 @@ pub fn build_session_(sopts: config::Options, sess } +/// Hash value constructed out of all the `-C metadata` arguments passed to the +/// compiler. Together with the crate-name forms a unique global identifier for +/// the crate. +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] +pub struct CrateDisambiguator(Fingerprint); + +impl CrateDisambiguator { + pub fn to_fingerprint(self) -> Fingerprint { + self.0 + } +} + +impl From for CrateDisambiguator { + fn from(fingerprint: Fingerprint) -> CrateDisambiguator { + CrateDisambiguator(fingerprint) + } +} + +impl_stable_hash_for!(tuple_struct CrateDisambiguator { fingerprint }); + /// Holds data on the current incremental compilation session, if there is one. #[derive(Debug)] pub enum IncrCompSession { - // This is the state the session will be in until the incr. comp. dir is - // needed. + /// This is the state the session will be in until the incr. comp. dir is + /// needed. NotInitialized, - // This is the state during which the session directory is private and can - // be modified. + /// This is the state during which the session directory is private and can + /// be modified. Active { session_directory: PathBuf, lock_file: flock::Lock, load_dep_graph: bool, }, - // This is the state after the session directory has been finalized. In this - // state, the contents of the directory must not be modified any more. + /// This is the state after the session directory has been finalized. In this + /// state, the contents of the directory must not be modified any more. Finalized { session_directory: PathBuf, }, - // This is an error state that is reached when some compilation error has - // occurred. It indicates that the contents of the session directory must - // not be used, since they might be invalid. + /// This is an error state that is reached when some compilation error has + /// occurred. It indicates that the contents of the session directory must + /// not be used, since they might be invalid. InvalidBecauseOfErrors { session_directory: PathBuf, } @@ -832,10 +973,12 @@ pub enum IncrCompSession { pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { let emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(EmitterWriter::stderr(color_config, - None)) + Box::new(EmitterWriter::stderr(color_config, None, false)) + } + config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)), + config::ErrorOutputType::Short(color_config) => { + Box::new(EmitterWriter::stderr(color_config, None, true)) } - config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; let handler = errors::Handler::with_emitter(true, false, emitter); handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal); @@ -845,10 +988,12 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { pub fn early_warn(output: config::ErrorOutputType, msg: &str) { let emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(EmitterWriter::stderr(color_config, - None)) + Box::new(EmitterWriter::stderr(color_config, None, false)) + } + config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)), + config::ErrorOutputType::Short(color_config) => { + Box::new(EmitterWriter::stderr(color_config, None, true)) } - config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; let handler = errors::Handler::with_emitter(true, false, emitter); handler.emit(&MultiSpan::new(), msg, errors::Level::Warning); diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index f3682f8d35d84..10a32c26e741d 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -252,7 +252,6 @@ fn uncovered_tys<'tcx>(tcx: TyCtxt, ty: Ty<'tcx>, infer_is_local: InferIsLocal) fn is_type_parameter(ty: Ty) -> bool { match ty.sty { - // FIXME(#20590) straighten story about projection types ty::TyProjection(..) | ty::TyParam(..) => true, _ => false, } @@ -305,6 +304,10 @@ fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool { def.did.is_local() } + ty::TyForeign(did) => { + did.is_local() + } + ty::TyDynamic(ref tt, ..) => { tt.principal().map_or(false, |p| p.def_id().is_local()) } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index c7c8141f4f768..5703c5c870e88 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -33,9 +33,9 @@ use hir::def_id::DefId; use infer::{self, InferCtxt}; use infer::type_variable::TypeVariableOrigin; use middle::const_val; -use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL; use std::fmt; use syntax::ast; +use session::DiagnosticMessageId; use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; use ty::error::ExpectedFound; use ty::fast_reject; @@ -219,13 +219,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - let mut diag = struct_span_err!( - self.tcx.sess, obligation.cause.span, E0271, - "type mismatch resolving `{}`", predicate - ); - self.note_type_err(&mut diag, &obligation.cause, None, values, err); - self.note_obligation_cause(&mut diag, obligation); - diag.emit(); + let msg = format!("type mismatch resolving `{}`", predicate); + let error_id = (DiagnosticMessageId::ErrorId(271), + Some(obligation.cause.span), msg.clone()); + let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + let mut diag = struct_span_err!( + self.tcx.sess, obligation.cause.span, E0271, + "type mismatch resolving `{}`", predicate + ); + self.note_type_err(&mut diag, &obligation.cause, None, values, err); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + } }); } @@ -255,6 +261,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { AdtKind::Enum => Some(17), }, ty::TyGenerator(..) => Some(18), + ty::TyForeign(..) => Some(19), ty::TyInfer(..) | ty::TyError => None } } @@ -473,30 +480,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { item_name: ast::Name, _impl_item_def_id: DefId, trait_item_def_id: DefId, - requirement: &fmt::Display, - lint_id: Option) // (*) + requirement: &fmt::Display) -> DiagnosticBuilder<'tcx> { - // (*) This parameter is temporary and used only for phasing - // in the bug fix to #18937. If it is `Some`, it has a kind of - // weird effect -- the diagnostic is reported as a lint, and - // the builder which is returned is marked as canceled. - let msg = "impl has stricter requirements than trait"; - let mut err = match lint_id { - Some(node_id) => { - self.tcx.struct_span_lint_node(EXTRA_REQUIREMENT_IN_IMPL, - node_id, - error_span, - msg) - } - None => { - struct_span_err!(self.tcx.sess, - error_span, - E0276, - "{}", msg) - } - }; + let mut err = struct_span_err!(self.tcx.sess, + error_span, + E0276, + "{}", msg); if let Some(trait_item_span) = self.tcx.hir.span_if_local(trait_item_def_id) { let span = self.tcx.sess.codemap().def_span(trait_item_span); @@ -535,15 +526,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let mut err = match *error { SelectionError::Unimplemented => { if let ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, lint_id + item_name, impl_item_def_id, trait_item_def_id, } = obligation.cause.code { self.report_extra_impl_obligation( span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}`", obligation.predicate), - lint_id) + &format!("`{}`", obligation.predicate)) .emit(); return; } @@ -591,6 +581,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { trait_ref.self_ty())); } + self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); + // Try to report a help message if !trait_ref.has_infer_types() && self.predicate_can_apply(obligation.param_env, trait_ref) { @@ -653,8 +645,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { violations) } - ty::Predicate::ClosureKind(closure_def_id, kind) => { - let found_kind = self.closure_kind(closure_def_id).unwrap(); + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + let found_kind = self.closure_kind(closure_def_id, closure_substs).unwrap(); let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap(); let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap(); let mut err = struct_span_err!( @@ -673,14 +665,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { if let Some(tables) = self.in_progress_tables { let tables = tables.borrow(); let closure_hir_id = self.tcx.hir.node_to_hir_id(node_id); - match tables.closure_kinds().get(closure_hir_id) { - Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) => { - err.span_note(span, &format!( + match (found_kind, tables.closure_kind_origins().get(closure_hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, name))) => { + err.span_note(*span, &format!( "closure is `FnOnce` because it moves the \ variable `{}` out of its environment", name)); }, - Some(&(ty::ClosureKind::FnMut, Some((span, name)))) => { - err.span_note(span, &format!( + (ty::ClosureKind::FnMut, Some((span, name))) => { + err.span_note(*span, &format!( "closure is `FnMut` because it mutates the \ variable `{}` here", name)); }, @@ -711,41 +703,105 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, _) => { + OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { + let found_trait_ref = self.resolve_type_vars_if_possible(&*found_trait_ref); let expected_trait_ref = self.resolve_type_vars_if_possible(&*expected_trait_ref); - let actual_trait_ref = self.resolve_type_vars_if_possible(&*actual_trait_ref); - if actual_trait_ref.self_ty().references_error() { + if expected_trait_ref.self_ty().references_error() { return; } - let expected_trait_ty = expected_trait_ref.self_ty(); - let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| { + let found_trait_ty = found_trait_ref.self_ty(); + + let found_did = found_trait_ty.ty_to_def_id(); + let found_span = found_did.and_then(|did| { self.tcx.hir.span_if_local(did) }); - let self_ty_count = - match expected_trait_ref.skip_binder().substs.type_at(1).sty { + let found_ty_count = + match found_trait_ref.skip_binder().substs.type_at(1).sty { ty::TyTuple(ref tys, _) => tys.len(), _ => 1, }; - let arg_ty_count = - match actual_trait_ref.skip_binder().substs.type_at(1).sty { - ty::TyTuple(ref tys, _) => tys.len(), - _ => 1, + let (expected_tys, expected_ty_count) = + match expected_trait_ref.skip_binder().substs.type_at(1).sty { + ty::TyTuple(ref tys, _) => + (tys.iter().map(|t| &t.sty).collect(), tys.len()), + ref sty => (vec![sty], 1), }; - if self_ty_count == arg_ty_count { + if found_ty_count == expected_ty_count { self.report_closure_arg_mismatch(span, found_span, - expected_trait_ref, - actual_trait_ref) + found_trait_ref, + expected_trait_ref) } else { - // Expected `|| { }`, found `|x, y| { }` - // Expected `fn(x) -> ()`, found `|| { }` + let expected_tuple = if expected_ty_count == 1 { + expected_tys.first().and_then(|t| { + if let &&ty::TyTuple(ref tuptys, _) = t { + Some(tuptys.len()) + } else { + None + } + }) + } else { + None + }; + + // FIXME(#44150): Expand this to "N args expected but a N-tuple found." + // Type of the 1st expected argument is somehow provided as type of a + // found one in that case. + // + // ``` + // [1i32, 2, 3].sort_by(|(a, b)| ..) + // // ^^^^^^^^ + // // expected_trait_ref: std::ops::FnMut<(&i32, &i32)> + // // found_trait_ref: std::ops::FnMut<(&i32,)> + // ``` + + let (closure_span, closure_args) = found_did + .and_then(|did| self.tcx.hir.get_if_local(did)) + .and_then(|node| { + if let hir::map::NodeExpr( + &hir::Expr { + node: hir::ExprClosure(_, ref decl, id, span, _), + .. + }) = node + { + let ty_snips = decl.inputs.iter() + .map(|ty| { + self.tcx.sess.codemap().span_to_snippet(ty.span).ok() + .and_then(|snip| { + // filter out dummy spans + if snip == "," || snip == "|" { + None + } else { + Some(snip) + } + }) + }) + .collect::>>(); + + let body = self.tcx.hir.body(id); + let pat_snips = body.arguments.iter() + .map(|arg| + self.tcx.sess.codemap().span_to_snippet(arg.pat.span).ok()) + .collect::>>(); + + Some((span, pat_snips, ty_snips)) + } else { + None + } + }) + .map(|(span, pat, ty)| (Some(span), Some((pat, ty)))) + .unwrap_or((None, None)); + let closure_args = closure_args.and_then(|(pat, ty)| Some((pat?, ty))); + self.report_arg_count_mismatch( span, - found_span, - arg_ty_count, - self_ty_count, - expected_trait_ty.is_closure() + closure_span.or(found_span), + expected_ty_count, + expected_tuple, + found_ty_count, + closure_args, + found_trait_ty.is_closure() ) } } @@ -767,32 +823,118 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err.emit(); } - fn report_arg_count_mismatch(&self, - span: Span, - found_span: Option, - expected: usize, - found: usize, - is_closure: bool) - -> DiagnosticBuilder<'tcx> - { + /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a + /// suggestion to borrow the initializer in order to use have a slice instead. + fn suggest_borrow_on_unsized_slice(&self, + code: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>) { + if let &ObligationCauseCode::VariableType(node_id) = code { + let parent_node = self.tcx.hir.get_parent_node(node_id); + if let Some(hir::map::NodeLocal(ref local)) = self.tcx.hir.find(parent_node) { + if let Some(ref expr) = local.init { + if let hir::ExprIndex(_, _) = expr.node { + if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + err.span_suggestion(expr.span, + "consider borrowing here", + format!("&{}", snippet)); + } + } + } + } + } + } + + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected: usize, + expected_tuple: Option, + found: usize, + closure_args: Option<(Vec, Vec>)>, + is_closure: bool + ) -> DiagnosticBuilder<'tcx> { + use std::borrow::Cow; + + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |n, distinct| format!( + "{} {}argument{}", + n, + if distinct && n >= 2 { "distinct " } else { "" }, + if n == 1 { "" } else { "s" }, + ); + + let expected_str = if let Some(n) = expected_tuple { + assert!(expected == 1); + if closure_args.as_ref().map(|&(ref pats, _)| pats.len()) == Some(n) { + Cow::from("a single tuple as argument") + } else { + // be verbose when numbers differ + Cow::from(format!("a single {}-tuple as argument", n)) + } + } else { + Cow::from(args_str(expected, false)) + }; + + let found_str = if expected_tuple.is_some() { + args_str(found, true) + } else { + args_str(found, false) + }; + + let mut err = struct_span_err!(self.tcx.sess, span, E0593, - "{} takes {} argument{} but {} argument{} {} required", - if is_closure { "closure" } else { "function" }, - found, - if found == 1 { "" } else { "s" }, - expected, - if expected == 1 { "" } else { "s" }, - if expected == 1 { "is" } else { "are" }); - - err.span_label(span, format!("expected {} that takes {} argument{}", - if is_closure { "closure" } else { "function" }, - expected, - if expected == 1 { "" } else { "s" })); + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label( + span, + format!( + "expected {} that takes {}", + kind, + expected_str, + ) + ); + if let Some(span) = found_span { - err.span_label(span, format!("takes {} argument{}", - found, - if found == 1 { "" } else { "s" })); + if let (Some(expected_tuple), Some((pats, tys))) = (expected_tuple, closure_args) { + if expected_tuple != found || pats.len() != found { + err.span_label(span, format!("takes {}", found_str)); + } else { + let sugg = format!( + "|({}){}|", + pats.join(", "), + + // add type annotations if available + if tys.iter().any(|ty| ty.is_some()) { + Cow::from(format!( + ": ({})", + tys.into_iter().map(|ty| if let Some(ty) = ty { + ty + } else { + "_".to_string() + }).collect::>().join(", ") + )) + } else { + Cow::from("") + }, + ); + + err.span_suggestion( + span, + "consider changing the closure to accept a tuple", + sugg + ); + } + } else { + err.span_label(span, format!("takes {}", found_str)); + } } + err } diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index cc2506d1afc50..93e33836818ce 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk}; +use infer::{RegionObligation, InferCtxt, InferOk}; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{ObligationForest, Error}; use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; use std::marker::PhantomData; -use syntax::ast; -use util::nodemap::NodeMap; use hir::def_id::DefId; use super::CodeAmbiguity; @@ -48,39 +46,19 @@ pub struct FulfillmentContext<'tcx> { // A list of all obligations that have been registered with this // fulfillment context. predicates: ObligationForest>, - - // A set of constraints that regionck must validate. Each - // constraint has the form `T:'a`, meaning "some type `T` must - // outlive the lifetime 'a". These constraints derive from - // instantiated type parameters. So if you had a struct defined - // like - // - // struct Foo { ... } + // Should this fulfillment context register type-lives-for-region + // obligations on its parent infcx? In some cases, region + // obligations are either already known to hold (normalization) or + // hopefully verifed elsewhere (type-impls-bound), and therefore + // should not be checked. // - // then in some expression `let x = Foo { ... }` it will - // instantiate the type parameter `T` with a fresh type `$0`. At - // the same time, it will record a region obligation of - // `$0:'static`. This will get checked later by regionck. (We - // can't generally check these things right away because we have - // to wait until types are resolved.) - // - // These are stored in a map keyed to the id of the innermost - // enclosing fn body / static initializer expression. This is - // because the location where the obligation was incurred can be - // relevant with respect to which sublifetime assumptions are in - // place. The reason that we store under the fn-id, and not - // something more fine-grained, is so that it is easier for - // regionck to be sure that it has found *all* the region - // obligations (otherwise, it's easy to fail to walk to a - // particular node-id). - region_obligations: NodeMap>>, -} - -#[derive(Clone)] -pub struct RegionObligation<'tcx> { - pub sub_region: ty::Region<'tcx>, - pub sup_type: Ty<'tcx>, - pub cause: ObligationCause<'tcx>, + // Note that if we are normalizing a type that we already + // know is well-formed, there should be no harm setting this + // to true - all the region variables should be determinable + // using the RFC 447 rules, which don't depend on + // type-lives-for-region constraints, and because the type + // is well-formed, the constraints should hold. + register_region_obligations: bool, } #[derive(Clone, Debug)] @@ -94,7 +72,14 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), - region_obligations: NodeMap(), + register_region_obligations: true + } + } + + pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: false } } @@ -157,14 +142,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_region_obligation(&mut self, - t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>) - { - register_region_obligation(t_a, r_b, cause, &mut self.region_obligations); - } - pub fn register_predicate_obligation(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, obligation: PredicateObligation<'tcx>) @@ -183,26 +160,16 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_predicate_obligations(&mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - obligations: Vec>) + pub fn register_predicate_obligations(&mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + obligations: I) + where I: IntoIterator> { for obligation in obligations { self.register_predicate_obligation(infcx, obligation); } } - - pub fn region_obligations(&self, - body_id: ast::NodeId) - -> &[RegionObligation<'tcx>] - { - match self.region_obligations.get(&body_id) { - None => Default::default(), - Some(vec) => vec, - } - } - pub fn select_all_or_error(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Result<(),Vec>> @@ -247,7 +214,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { // Process pending obligations. let outcome = self.predicates.process_obligations(&mut FulfillProcessor { selcx, - region_obligations: &mut self.region_obligations, + register_region_obligations: self.register_region_obligations }); debug!("select: outcome={:?}", outcome); @@ -277,7 +244,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> { selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, - region_obligations: &'a mut NodeMap>>, + register_region_obligations: bool } impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> { @@ -288,9 +255,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, obligation: &mut Self::Obligation) -> Result>, Self::Error> { - process_predicate(self.selcx, - obligation, - self.region_obligations) + process_predicate(self.selcx, obligation, self.register_region_obligations) .map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] @@ -330,7 +295,7 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't fn process_predicate<'a, 'gcx, 'tcx>( selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, pending_obligation: &mut PendingPredicateObligation<'tcx>, - region_obligations: &mut NodeMap>>) + register_region_obligations: bool) -> Result>>, FulfillmentErrorCode<'tcx>> { @@ -435,14 +400,14 @@ fn process_predicate<'a, 'gcx, 'tcx>( ty::Predicate::TypeOutlives(ref binder) => { // Check if there are higher-ranked regions. - match selcx.tcx().no_late_bound_regions(binder) { + match binder.no_late_bound_regions() { // If there are, inspect the underlying type further. None => { // Convert from `Binder>` to `Binder`. let binder = binder.map_bound_ref(|pred| pred.0); // Check if the type has any bound regions. - match selcx.tcx().no_late_bound_regions(&binder) { + match binder.no_late_bound_regions() { // If so, this obligation is an error (for now). Eventually we should be // able to support additional cases here, like `for<'a> &'a str: 'a`. None => { @@ -452,18 +417,30 @@ fn process_predicate<'a, 'gcx, 'tcx>( // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`. Some(t_a) => { let r_static = selcx.tcx().types.re_static; - register_region_obligation(t_a, r_static, - obligation.cause.clone(), - region_obligations); + if register_region_obligations { + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_static, + cause: obligation.cause.clone(), + }); + } Ok(Some(vec![])) } } } // If there aren't, register the obligation. Some(ty::OutlivesPredicate(t_a, r_b)) => { - register_region_obligation(t_a, r_b, - obligation.cause.clone(), - region_obligations); + if register_region_obligations { + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_b, + cause: obligation.cause.clone() + }); + } Ok(Some(vec![])) } } @@ -491,8 +468,8 @@ fn process_predicate<'a, 'gcx, 'tcx>( } } - ty::Predicate::ClosureKind(closure_def_id, kind) => { - match selcx.infcx().closure_kind(closure_def_id) { + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + match selcx.infcx().closure_kind(closure_def_id, closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { Ok(Some(vec![])) @@ -566,25 +543,6 @@ fn process_predicate<'a, 'gcx, 'tcx>( } } - -fn register_region_obligation<'tcx>(t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>, - region_obligations: &mut NodeMap>>) -{ - let region_obligation = RegionObligation { sup_type: t_a, - sub_region: r_b, - cause: cause }; - - debug!("register_region_obligation({:?}, cause={:?})", - region_obligation, region_obligation.cause); - - region_obligations.entry(region_obligation.cause.body_id) - .or_insert(vec![]) - .push(region_obligation); - -} - fn to_fulfillment_error<'tcx>( error: Error, FulfillmentErrorCode<'tcx>>) -> FulfillmentError<'tcx> diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a1817f181066c..d6f8a5f9cc6a1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -17,9 +17,9 @@ pub use self::ObligationCauseCode::*; use hir; use hir::def_id::DefId; +use infer::outlives::env::OutlivesEnvironment; use middle::const_val::ConstEvalErr; use middle::region; -use middle::free_region::FreeRegionMap; use ty::subst::Substs; use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate}; use ty::error::{ExpectedFound, TypeError}; @@ -30,7 +30,7 @@ use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult}; -pub use self::fulfill::{FulfillmentContext, RegionObligation}; +pub use self::fulfill::FulfillmentContext; pub use self::project::MismatchedProjectionTypes; pub use self::project::{normalize, normalize_projection_type, Normalized}; pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal}; @@ -152,7 +152,6 @@ pub enum ObligationCauseCode<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - lint_id: Option, }, /// Checking that this expression can be assigned where it needs to be @@ -283,16 +282,16 @@ pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; /// ### The type parameter `N` /// /// See explanation on `VtableImplData`. -#[derive(Clone)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub enum Vtable<'tcx, N> { /// Vtable identifying a particular impl. VtableImpl(VtableImplData<'tcx, N>), - /// Vtable for default trait implementations + /// Vtable for auto trait implementations /// This carries the information and nested obligations with regards - /// to a default implementation for a trait `Trait`. The nested obligations + /// to an auto implementation for a trait `Trait`. The nested obligations /// ensure the trait implementation holds for all the constituent types. - VtableDefaultImpl(VtableDefaultImplData), + VtableAutoImpl(VtableAutoImplData), /// Successful resolution to an obligation provided by the caller /// for some type parameter. The `Vec` represents the @@ -328,14 +327,14 @@ pub enum Vtable<'tcx, N> { /// is `Obligation`, as one might expect. During trans, however, this /// is `()`, because trans only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableImplData<'tcx, N> { pub impl_def_id: DefId, pub substs: &'tcx Substs<'tcx>, pub nested: Vec } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableGeneratorData<'tcx, N> { pub closure_def_id: DefId, pub substs: ty::ClosureSubsts<'tcx>, @@ -344,7 +343,7 @@ pub struct VtableGeneratorData<'tcx, N> { pub nested: Vec } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableClosureData<'tcx, N> { pub closure_def_id: DefId, pub substs: ty::ClosureSubsts<'tcx>, @@ -353,20 +352,20 @@ pub struct VtableClosureData<'tcx, N> { pub nested: Vec } -#[derive(Clone)] -pub struct VtableDefaultImplData { +#[derive(Clone, RustcEncodable, RustcDecodable)] +pub struct VtableAutoImplData { pub trait_def_id: DefId, pub nested: Vec } -#[derive(Clone)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct VtableBuiltinData { pub nested: Vec } /// A vtable for some object-safe trait `Foo` automatically derived /// for the object type `Foo`. -#[derive(PartialEq,Eq,Clone)] +#[derive(PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)] pub struct VtableObjectData<'tcx, N> { /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, @@ -379,7 +378,7 @@ pub struct VtableObjectData<'tcx, N> { pub nested: Vec, } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableFnPointerData<'tcx, N> { pub fn_ty: Ty<'tcx>, pub nested: Vec @@ -432,7 +431,10 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx // this function's result remains infallible, we must confirm // that guess. While imperfect, I believe this is sound. - let mut fulfill_cx = FulfillmentContext::new(); + // The handling of regions in this area of the code is terrible, + // see issue #29149. We should be able to improve on this with + // NLL. + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); // We can use a dummy node-id here because we won't pay any mind // to region obligations that arise (there shouldn't really be any @@ -512,8 +514,24 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, unnormalized_env.reveal); tcx.infer_ctxt().enter(|infcx| { - let predicates = match fully_normalize( + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + let fulfill_cx = FulfillmentContext::new_ignoring_regions(); + + let predicates = match fully_normalize_with_fulfillcx( &infcx, + fulfill_cx, cause, elaborated_env, // You would really want to pass infcx.param_env.caller_bounds here, @@ -536,8 +554,13 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, predicates); let region_scope_tree = region::ScopeTree::default(); - let free_regions = FreeRegionMap::new(); - infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); + + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &outlives_env); + let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, Err(fixup_err) => { @@ -573,9 +596,6 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, -> Result>> where T : TypeFoldable<'tcx> { - debug!("fully_normalize(value={:?})", value); - - let selcx = &mut SelectionContext::new(infcx); // FIXME (@jroesch) ISSUE 26721 // I'm not sure if this is a bug or not, needs further investigation. // It appears that by reusing the fulfillment_cx here we incur more @@ -589,8 +609,21 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // // I think we should probably land this refactor and then come // back to this is a follow-up patch. - let mut fulfill_cx = FulfillmentContext::new(); + let fulfillcx = FulfillmentContext::new(); + fully_normalize_with_fulfillcx(infcx, fulfillcx, cause, param_env, value) +} +pub fn fully_normalize_with_fulfillcx<'a, 'gcx, 'tcx, T>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + mut fulfill_cx: FulfillmentContext<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> Result>> + where T : TypeFoldable<'tcx> +{ + debug!("fully_normalize_with_fulfillcx(value={:?})", value); + let selcx = &mut SelectionContext::new(infcx); let Normalized { value: normalized_value, obligations } = project::normalize(selcx, param_env, cause, value); debug!("fully_normalize: normalized_value={:?} obligations={:?}", @@ -650,53 +683,55 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// Given a trait `trait_ref`, iterates the vtable entries /// that come from `trait_ref`, including its supertraits. #[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait. -pub fn get_vtable_methods<'a, 'tcx>( +fn vtable_methods<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) - -> impl Iterator)>> + 'a + -> Rc)>>> { - debug!("get_vtable_methods({:?})", trait_ref); - - supertraits(tcx, trait_ref).flat_map(move |trait_ref| { - let trait_methods = tcx.associated_items(trait_ref.def_id()) - .filter(|item| item.kind == ty::AssociatedKind::Method); - - // Now list each method's DefId and Substs (for within its trait). - // If the method can never be called from this object, produce None. - trait_methods.map(move |trait_method| { - debug!("get_vtable_methods: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !tcx.is_vtable_safe_method(trait_ref.def_id(), &trait_method) { - debug!("get_vtable_methods: not vtable safe"); - return None; - } - - // the method may have some early-bound lifetimes, add - // regions for those - let substs = Substs::for_item(tcx, def_id, - |_, _| tcx.types.re_erased, - |def, _| trait_ref.substs().type_for_def(def)); - - // the trait type may have higher-ranked lifetimes in it; - // so erase them if they appear, so that we get the type - // at some particular call site - let substs = tcx.erase_late_bound_regions_and_normalize(&ty::Binder(substs)); - - // It's possible that the method relies on where clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and trans it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if !normalize_and_test_predicates(tcx, predicates.predicates) { - debug!("get_vtable_methods: predicates do not hold"); - return None; - } - - Some((def_id, substs)) - }) - }) + debug!("vtable_methods({:?})", trait_ref); + + Rc::new( + supertraits(tcx, trait_ref).flat_map(move |trait_ref| { + let trait_methods = tcx.associated_items(trait_ref.def_id()) + .filter(|item| item.kind == ty::AssociatedKind::Method); + + // Now list each method's DefId and Substs (for within its trait). + // If the method can never be called from this object, produce None. + trait_methods.map(move |trait_method| { + debug!("vtable_methods: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !tcx.is_vtable_safe_method(trait_ref.def_id(), &trait_method) { + debug!("vtable_methods: not vtable safe"); + return None; + } + + // the method may have some early-bound lifetimes, add + // regions for those + let substs = Substs::for_item(tcx, def_id, + |_, _| tcx.types.re_erased, + |def, _| trait_ref.substs().type_for_def(def)); + + // the trait type may have higher-ranked lifetimes in it; + // so erase them if they appear, so that we get the type + // at some particular call site + let substs = tcx.erase_late_bound_regions_and_normalize(&ty::Binder(substs)); + + // It's possible that the method relies on where clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and trans it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if !normalize_and_test_predicates(tcx, predicates.predicates) { + debug!("vtable_methods: predicates do not hold"); + return None; + } + + Some((def_id, substs)) + }) + }).collect() + ) } impl<'tcx,O> Obligation<'tcx,O> { @@ -756,7 +791,7 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableImpl(i) => i.nested, VtableParam(n) => n, VtableBuiltin(i) => i.nested, - VtableDefaultImpl(d) => d.nested, + VtableAutoImpl(d) => d.nested, VtableClosure(c) => c.nested, VtableGenerator(c) => c.nested, VtableObject(d) => d.nested, @@ -769,7 +804,7 @@ impl<'tcx, N> Vtable<'tcx, N> { &mut VtableImpl(ref mut i) => &mut i.nested, &mut VtableParam(ref mut n) => n, &mut VtableBuiltin(ref mut i) => &mut i.nested, - &mut VtableDefaultImpl(ref mut d) => &mut d.nested, + &mut VtableAutoImpl(ref mut d) => &mut d.nested, &mut VtableGenerator(ref mut c) => &mut c.nested, &mut VtableClosure(ref mut c) => &mut c.nested, &mut VtableObject(ref mut d) => &mut d.nested, @@ -793,7 +828,7 @@ impl<'tcx, N> Vtable<'tcx, N> { vtable_base: o.vtable_base, nested: o.nested.into_iter().map(f).collect(), }), - VtableDefaultImpl(d) => VtableDefaultImpl(VtableDefaultImplData { + VtableAutoImpl(d) => VtableAutoImpl(VtableAutoImplData { trait_def_id: d.trait_def_id, nested: d.nested.into_iter().map(f).collect(), }), @@ -835,15 +870,8 @@ pub fn provide(providers: &mut ty::maps::Providers) { is_object_safe: object_safety::is_object_safe_provider, specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, - ..*providers - }; -} - -pub fn provide_extern(providers: &mut ty::maps::Providers) { - *providers = ty::maps::Providers { - is_object_safe: object_safety::is_object_safe_provider, - specialization_graph_of: specialize::specialization_graph_provider, - specializes: specialize::specializes, + trans_fulfill_obligation: trans::trans_fulfill_obligation, + vtable_methods, ..*providers }; } diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index 1e9816095ea2e..facd6350e196c 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -23,6 +23,7 @@ use hir::def_id::DefId; use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::subst::Substs; +use ty::util::ExplicitSelf; use std::borrow::Cow; use syntax::ast; @@ -57,6 +58,10 @@ impl ObjectSafetyViolation { in its arguments or return type", name).into(), ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) => format!("method `{}` has generic type parameters", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) => + format!("method `{}` has a non-standard `self` type. Only `&self`, \ + `&mut self`, and `Box` are currently supported \ + for trait objects", name).into(), ObjectSafetyViolation::AssociatedConst(name) => format!("the trait cannot contain associated consts like `{}`", name).into(), } @@ -74,6 +79,9 @@ pub enum MethodViolationCode { /// e.g., `fn foo()` Generic, + + /// arbitrary `self` type, e.g. `self: Rc` + NonStandardSelfType, } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { @@ -260,9 +268,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { return Some(MethodViolationCode::StaticMethod); } + let sig = self.fn_sig(method.def_id); + + let self_ty = self.mk_self_type(); + let self_arg_ty = sig.skip_binder().inputs()[0]; + if let ExplicitSelf::Other = ExplicitSelf::determine(self_arg_ty, |ty| ty == self_ty) { + return Some(MethodViolationCode::NonStandardSelfType); + } + // The `Self` type is erased, so it should not appear in list of // arguments or return type apart from the receiver. - let ref sig = self.fn_sig(method.def_id); for input_ty in &sig.skip_binder().inputs()[1..] { if self.contains_illegal_self_type_reference(trait_def_id, input_ty) { return Some(MethodViolationCode::ReferencesSelf); diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs index 94f6efcad4adc..16e200d56f9fb 100644 --- a/src/librustc/traits/on_unimplemented.rs +++ b/src/librustc/traits/on_unimplemented.rs @@ -254,7 +254,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString { } }, // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) => { + Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { span_err!(tcx.sess, span, E0231, "only named substitution \ parameters are allowed"); diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 54e31aed272a3..429771cca9844 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -29,7 +29,6 @@ use infer::{InferCtxt, InferOk}; use infer::type_variable::TypeVariableOrigin; use middle::const_val::ConstVal; use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; -use syntax::ast; use syntax::symbol::Symbol; use ty::subst::{Subst, Substs}; use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt}; @@ -639,7 +638,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, // but we have `T: Foo` and `?1: Bar`). ty::Predicate::Projection(ref data) => - !infcx.any_unresolved_type_vars(&data.ty()), + infcx.any_unresolved_type_vars(&data.ty()), // We are only interested in `T: Foo` predicates, whre // `U` references one of `unresolved_type_vars`. =) @@ -1044,10 +1043,9 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( // In either case, we handle this by not adding a // candidate for an impl if it contains a `default` // type. - let item_name = selcx.tcx().associated_item(obligation.predicate.item_def_id).name; let node_item = assoc_ty_def(selcx, impl_data.impl_def_id, - item_name); + obligation.predicate.item_def_id); let is_default = if node_item.node.is_from_trait() { // If true, the impl inherited a `type Foo = Bar` @@ -1118,7 +1116,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( // projection. And the projection where clause is handled // in `assemble_candidates_from_param_env`. } - super::VtableDefaultImpl(..) | + super::VtableAutoImpl(..) | super::VtableBuiltin(..) => { // These traits have no associated types. span_bug!( @@ -1184,7 +1182,7 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>( confirm_fn_pointer_candidate(selcx, obligation, data), super::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), - super::VtableDefaultImpl(..) | + super::VtableAutoImpl(..) | super::VtableParam(..) | super::VtableBuiltin(..) => // we don't create Select candidates with this kind of resolution @@ -1266,8 +1264,7 @@ fn confirm_generator_candidate<'cx, 'gcx, 'tcx>( vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>) -> Progress<'tcx> { - let gen_sig = selcx.infcx().generator_sig(vtable.closure_def_id).unwrap() - .subst(selcx.tcx(), vtable.substs.substs); + let gen_sig = vtable.substs.generator_poly_sig(vtable.closure_def_id, selcx.tcx()); let Normalized { value: gen_sig, obligations @@ -1441,8 +1438,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>( let tcx = selcx.tcx(); let param_env = obligation.param_env; - let assoc_ty = assoc_ty_def(selcx, impl_def_id, - tcx.associated_item(obligation.predicate.item_def_id).name); + let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_def_id); let ty = if !assoc_ty.item.defaultness.has_value() { // This means that the impl is missing a definition for the @@ -1471,10 +1467,11 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>( fn assoc_ty_def<'cx, 'gcx, 'tcx>( selcx: &SelectionContext<'cx, 'gcx, 'tcx>, impl_def_id: DefId, - assoc_ty_name: ast::Name) + assoc_ty_def_id: DefId) -> specialization_graph::NodeItem { let tcx = selcx.tcx(); + let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).name; let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; let trait_def = tcx.trait_def(trait_def_id); @@ -1486,7 +1483,8 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>( // cycle error if the specialization graph is currently being built. let impl_node = specialization_graph::Node::Impl(impl_def_id); for item in impl_node.items(tcx) { - if item.kind == ty::AssociatedKind::Type && item.name == assoc_ty_name { + if item.kind == ty::AssociatedKind::Type && + tcx.hygienic_eq(item.name, assoc_ty_name, trait_def_id) { return specialization_graph::NodeItem { node: specialization_graph::Node::Impl(impl_def_id), item, @@ -1496,7 +1494,7 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>( if let Some(assoc_item) = trait_def .ancestors(tcx, impl_def_id) - .defs(tcx, assoc_ty_name, ty::AssociatedKind::Type) + .defs(tcx, assoc_ty_name, ty::AssociatedKind::Type, trait_def_id) .next() { assoc_item } else { @@ -1561,7 +1559,7 @@ impl<'cx, 'gcx, 'tcx> ProjectionCacheKey<'tcx> { let infcx = selcx.infcx(); // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use - infcx.tcx.no_late_bound_regions(&predicate) + predicate.no_late_bound_regions() .map(|predicate| ProjectionCacheKey { // We don't attempt to match up with a specific type-variable state // from a specific call to `opt_normalize_projection_type` - if diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 00f0672822fc1..91e6c4270b32a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -25,9 +25,9 @@ use super::TraitNotObjectSafe; use super::Selection; use super::SelectionResult; use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableGenerator, - VtableFnPointer, VtableObject, VtableDefaultImpl}; + VtableFnPointer, VtableObject, VtableAutoImpl}; use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableGeneratorData, - VtableClosureData, VtableDefaultImplData, VtableFnPointerData}; + VtableClosureData, VtableAutoImplData, VtableFnPointerData}; use super::util; use dep_graph::{DepNodeIndex, DepKind}; @@ -225,7 +225,7 @@ enum SelectionCandidate<'tcx> { BuiltinCandidate { has_nested: bool }, ParamCandidate(ty::PolyTraitRef<'tcx>), ImplCandidate(DefId), - DefaultImplCandidate(DefId), + AutoImplCandidate(DefId), /// This is a trait matching with a projected type as `Self`, and /// we found an applicable bound in the trait definition. @@ -260,7 +260,7 @@ impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> { } } ImplCandidate(def_id) => ImplCandidate(def_id), - DefaultImplCandidate(def_id) => DefaultImplCandidate(def_id), + AutoImplCandidate(def_id) => AutoImplCandidate(def_id), ProjectionCandidate => ProjectionCandidate, FnPointerCandidate => FnPointerCandidate, ObjectCandidate => ObjectCandidate, @@ -718,8 +718,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } } - ty::Predicate::ClosureKind(closure_def_id, kind) => { - match self.infcx.closure_kind(closure_def_id) { + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + match self.infcx.closure_kind(closure_def_id, closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { EvaluatedToOk @@ -910,7 +910,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { let result = match predicate { ty::Predicate::Trait(ref data) => { - self.tcx().trait_has_default_impl(data.def_id()) + self.tcx().trait_is_auto(data.def_id()) } _ => { false @@ -1309,13 +1309,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { }; if obligation.predicate.skip_binder().self_ty().is_ty_var() { - // FIXME(#20297): Self is a type variable (e.g. `_: AsRef`). + // Self is a type variable (e.g. `_: AsRef`). // // This is somewhat problematic, as the current scheme can't really // handle it turning to be a projection. This does end up as truly // ambiguous in most cases anyway. // - // Until this is fixed, take the fast path out - this also improves + // Take the fast path out - this also improves // performance by preventing assemble_candidates_from_impls from // matching every impl for this trait. return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); @@ -1368,10 +1368,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { self.assemble_candidates_from_projected_tys(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - // Default implementations have lower priority, so we only + // Auto implementations have lower priority, so we only // consider triggering a default if there is no other impl that can apply. if candidates.vec.is_empty() { - self.assemble_candidates_from_default_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; } debug!("candidate list size: {}", candidates.vec.len()); Ok(candidates) @@ -1383,8 +1383,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { { debug!("assemble_candidates_for_projected_tys({:?})", obligation); - // FIXME(#20297) -- just examining the self-type is very simplistic - // before we go into the whole skolemization thing, just // quickly check if the self-type is a projection at all. match obligation.predicate.0.trait_ref.self_ty().sty { @@ -1595,10 +1593,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters match obligation.self_ty().skip_binder().sty { - ty::TyClosure(closure_def_id, _) => { + ty::TyClosure(closure_def_id, closure_substs) => { debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); - match self.infcx.closure_kind(closure_def_id) { + match self.infcx.closure_kind(closure_def_id, closure_substs) { Some(closure_kind) => { debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); if closure_kind.extends(kind) { @@ -1688,18 +1686,18 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ok(()) } - fn assemble_candidates_from_default_impls(&mut self, + fn assemble_candidates_from_auto_impls(&mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>) -> Result<(), SelectionError<'tcx>> { // OK to skip binder here because the tests we do below do not involve bound regions let self_ty = *obligation.self_ty().skip_binder(); - debug!("assemble_candidates_from_default_impls(self_ty={:?})", self_ty); + debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); let def_id = obligation.predicate.def_id(); - if self.tcx().trait_has_default_impl(def_id) { + if self.tcx().trait_is_auto(def_id) { match self_ty.sty { ty::TyDynamic(..) => { // For object types, we don't know what the closed @@ -1707,6 +1705,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // say nothing; a candidate may be added by // `assemble_candidates_from_object_ty`. } + ty::TyForeign(..) => { + // Since the contents of foreign types is unknown, + // we don't add any `..` impl. Default traits could + // still be provided by a manual implementation for + // this trait and type. + } ty::TyParam(..) | ty::TyProjection(..) => { // In these cases, we don't know what the actual @@ -1724,11 +1728,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // this path. } ty::TyInfer(ty::TyVar(_)) => { - // the defaulted impl might apply, we don't know + // the auto impl might apply, we don't know candidates.ambiguous = true; } _ => { - candidates.vec.push(DefaultImplCandidate(def_id.clone())) + candidates.vec.push(AutoImplCandidate(def_id.clone())) } } } @@ -1830,7 +1834,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // T: Trait // so it seems ok if we (conservatively) fail to accept that `Unsize` // obligation above. Should be possible to extend this in the future. - let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) { + let source = match obligation.self_ty().no_late_bound_regions() { Some(t) => t, None => { // Don't add any candidates if there are bound regions. @@ -1929,7 +1933,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { match other.candidate { ObjectCandidate | ParamCandidate(_) | ProjectionCandidate => match victim.candidate { - DefaultImplCandidate(..) => { + AutoImplCandidate(..) => { bug!( "default implementations shouldn't be recorded \ when there are other valid candidates"); @@ -2024,7 +2028,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Where(ty::Binder(Vec::new())) } - ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) => Never, + ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) | ty::TyForeign(..) => Never, ty::TyTuple(tys, _) => { Where(ty::Binder(tys.last().into_iter().cloned().collect())) @@ -2068,7 +2072,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Where(ty::Binder(Vec::new())) } - ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | ty::TyGenerator(..) | + ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | + ty::TyGenerator(..) | ty::TyForeign(..) | ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { Never } @@ -2150,6 +2155,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyDynamic(..) | ty::TyParam(..) | + ty::TyForeign(..) | ty::TyProjection(..) | ty::TyInfer(ty::TyVar(_)) | ty::TyInfer(ty::FreshTy(_)) | @@ -2174,14 +2180,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::TyClosure(def_id, ref substs) => { - // FIXME(#27086). We are invariant w/r/t our - // func_substs, but we don't see them as - // constituent types; this seems RIGHT but also like - // something that a normal type couldn't simulate. Is - // this just a gap with the way that PhantomData and - // OIBIT interact? That is, there is no way to say - // "make me invariant with respect to this TYPE, but - // do not act as though I can reach it" substs.upvar_tys(def_id, self.tcx()).collect() } @@ -2284,9 +2282,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ok(VtableParam(obligations)) } - DefaultImplCandidate(trait_def_id) => { - let data = self.confirm_default_impl_candidate(obligation, trait_def_id); - Ok(VtableDefaultImpl(data)) + AutoImplCandidate(trait_def_id) => { + let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); + Ok(VtableAutoImpl(data)) } ImplCandidate(impl_def_id) => { @@ -2419,29 +2417,29 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { /// /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. - fn confirm_default_impl_candidate(&mut self, + fn confirm_auto_impl_candidate(&mut self, obligation: &TraitObligation<'tcx>, trait_def_id: DefId) - -> VtableDefaultImplData> + -> VtableAutoImplData> { - debug!("confirm_default_impl_candidate({:?}, {:?})", + debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); // binder is moved below let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); let types = self.constituent_types_for_ty(self_ty); - self.vtable_default_impl(obligation, trait_def_id, ty::Binder(types)) + self.vtable_auto_impl(obligation, trait_def_id, ty::Binder(types)) } - /// See `confirm_default_impl_candidate` - fn vtable_default_impl(&mut self, + /// See `confirm_auto_impl_candidate` + fn vtable_auto_impl(&mut self, obligation: &TraitObligation<'tcx>, trait_def_id: DefId, nested: ty::Binder>>) - -> VtableDefaultImplData> + -> VtableAutoImplData> { - debug!("vtable_default_impl: nested={:?}", nested); + debug!("vtable_auto_impl: nested={:?}", nested); let cause = obligation.derived_cause(BuiltinDerivedObligation); let mut obligations = self.collect_predicates_for_types( @@ -2467,9 +2465,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { obligations.extend(trait_obligations); - debug!("vtable_default_impl: obligations={:?}", obligations); + debug!("vtable_auto_impl: obligations={:?}", obligations); - VtableDefaultImplData { + VtableAutoImplData { trait_def_id, nested: obligations } @@ -2728,7 +2726,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { obligations.push(Obligation::new( obligation.cause.clone(), obligation.param_env, - ty::Predicate::ClosureKind(closure_def_id, kind))); + ty::Predicate::ClosureKind(closure_def_id, substs, kind))); Ok(VtableClosureData { closure_def_id, @@ -2786,7 +2784,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // assemble_candidates_for_unsizing should ensure there are no late bound // regions here. See the comment there for more details. let source = self.infcx.shallow_resolve( - tcx.no_late_bound_regions(&obligation.self_ty()).unwrap()); + obligation.self_ty().no_late_bound_regions().unwrap()); let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); let target = self.infcx.shallow_resolve(target); @@ -3186,8 +3184,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { substs: ty::ClosureSubsts<'tcx>) -> ty::PolyTraitRef<'tcx> { - let gen_sig = self.infcx.generator_sig(closure_def_id).unwrap() - .subst(self.tcx(), substs.substs); + let gen_sig = substs.generator_poly_sig(closure_def_id, self.tcx()); let ty::Binder((trait_ref, ..)) = self.tcx().generator_trait_ref_and_outputs(obligation.predicate.def_id(), obligation.predicate.0.self_ty(), // (1) diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index f9332fc6697e0..1b5b0d35ba390 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -20,7 +20,7 @@ use super::{SelectionContext, FulfillmentContext}; use super::util::impl_trait_ref_and_oblig; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use hir::def_id::DefId; use infer::{InferCtxt, InferOk}; use ty::subst::{Subst, Substs}; @@ -125,7 +125,7 @@ pub fn find_associated_item<'a, 'tcx>( let trait_def = tcx.trait_def(trait_def_id); let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id); - match ancestors.defs(tcx, item.name, item.kind).next() { + match ancestors.defs(tcx, item.name, item.kind, trait_def_id).next() { Some(node_item) => { let substs = tcx.infer_ctxt().enter(|infcx| { let param_env = ty::ParamEnv::empty(Reveal::All); @@ -241,7 +241,18 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // (which are packed up in penv) infcx.save_and_restore_in_snapshot_flag(|infcx| { - let mut fulfill_cx = FulfillmentContext::new(); + // If we came from `translate_substs`, we already know that the + // predicates for our impl hold (after all, we know that a more + // specialized impl holds, so our impl must hold too), and + // we only want to process the projections to determine the + // the types in our substs using RFC 447, so we can safely + // ignore region obligations, which allows us to avoid threading + // a node-id to assign them with. + // + // If we came from specialization graph construction, then + // we already make a mockery out of the region system, so + // why not ignore them a bit earlier? + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); for oblig in obligations.into_iter() { fulfill_cx.register_predicate_obligation(&infcx, oblig); } @@ -335,7 +346,12 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx |ty| format!(" for `{}`", ty)))); } Err(cname) => { - err.note(&format!("conflicting implementation in crate `{}`", cname)); + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => format!( + "conflicting implementation in crate `{}`:\n- {}", cname, s), + None => format!("conflicting implementation in crate `{}`", cname), + }; + err.note(&msg); } } @@ -353,3 +369,56 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx Rc::new(sg) } + +/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a +/// string. +fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option { + use std::fmt::Write; + + let trait_ref = if let Some(tr) = tcx.impl_trait_ref(impl_def_id) { + tr + } else { + return None; + }; + + let mut w = "impl".to_owned(); + + let substs = Substs::identity_for_item(tcx, impl_def_id); + + // FIXME: Currently only handles ?Sized. + // Needs to support ?Move and ?DynSized when they are implemented. + let mut types_without_default_bounds = FxHashSet::default(); + let sized_trait = tcx.lang_items().sized_trait(); + + if !substs.is_noop() { + types_without_default_bounds.extend(substs.types()); + w.push('<'); + w.push_str(&substs.iter().map(|k| k.to_string()).collect::>().join(", ")); + w.push('>'); + } + + write!(w, " {} for {}", trait_ref, tcx.type_of(impl_def_id)).unwrap(); + + // The predicates will contain default bounds like `T: Sized`. We need to + // remove these bounds, and add `T: ?Sized` to any untouched type parameters. + let predicates = tcx.predicates_of(impl_def_id).predicates; + let mut pretty_predicates = Vec::with_capacity(predicates.len()); + for p in predicates { + if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { + if Some(poly_trait_ref.def_id()) == sized_trait { + types_without_default_bounds.remove(poly_trait_ref.self_ty()); + continue; + } + } + pretty_predicates.push(p.to_string()); + } + for ty in types_without_default_bounds { + pretty_predicates.push(format!("{}: ?Sized", ty)); + } + if !pretty_predicates.is_empty() { + write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); + } + + w.push(';'); + Some(w) +} diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index 0651d1904bf06..da9dbc0e2c999 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -346,11 +346,14 @@ impl<'a, 'gcx, 'tcx> Ancestors { /// Search the items from the given ancestors, returning each definition /// with the given name and the given kind. #[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait. - pub fn defs(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, name: Name, kind: ty::AssociatedKind) + pub fn defs(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, trait_item_name: Name, + trait_item_kind: ty::AssociatedKind, trait_def_id: DefId) -> impl Iterator> + 'a { self.flat_map(move |node| { - node.items(tcx).filter(move |item| item.kind == kind && item.name == name) - .map(move |item| NodeItem { node: node, item: item }) + node.items(tcx).filter(move |impl_item| { + impl_item.kind == trait_item_kind && + tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id) + }).map(move |item| NodeItem { node: node, item: item }) }) } } diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 19ed03aa14917..e1e2798ecb51c 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -10,7 +10,7 @@ use traits; use traits::project::Normalized; -use ty::{Lift, TyCtxt}; +use ty::{self, Lift, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use std::fmt; @@ -26,18 +26,18 @@ impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> { } } -impl<'tcx> fmt::Debug for traits::RegionObligation<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, - self.sup_type) - } -} impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Obligation(predicate={:?},depth={})", - self.predicate, - self.recursion_depth) + if ty::tls::with(|tcx| tcx.sess.verbose()) { + write!(f, "Obligation(predicate={:?},cause={:?},depth={})", + self.predicate, + self.cause, + self.recursion_depth) + } else { + write!(f, "Obligation(predicate={:?},depth={})", + self.predicate, + self.recursion_depth) + } } } @@ -47,7 +47,7 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> { super::VtableImpl(ref v) => write!(f, "{:?}", v), - super::VtableDefaultImpl(ref t) => + super::VtableAutoImpl(ref t) => write!(f, "{:?}", t), super::VtableClosure(ref d) => @@ -104,9 +104,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableBuiltinData { } } -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableDefaultImplData { +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableAutoImplData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VtableDefaultImplData(trait_def_id={:?}, nested={:?})", + write!(f, "VtableAutoImplData(trait_def_id={:?}, nested={:?})", self.trait_def_id, self.nested) } @@ -221,13 +221,11 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { } super::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { Some(super::CompareImplMethodObligation { item_name, impl_item_def_id, trait_item_def_id, - lint_id, }) } super::ExprAssignable => Some(super::ExprAssignable), @@ -292,7 +290,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { }) }) } - traits::VtableDefaultImpl(t) => Some(traits::VtableDefaultImpl(t)), + traits::VtableAutoImpl(t) => Some(traits::VtableAutoImpl(t)), traits::VtableGenerator(traits::VtableGeneratorData { closure_def_id, substs, @@ -407,9 +405,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureDa } } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultImplData { +impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableAutoImplData { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableDefaultImplData { + traits::VtableAutoImplData { trait_def_id: self.trait_def_id, nested: self.nested.fold_with(folder), } @@ -463,7 +461,7 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)), - traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)), + traits::VtableAutoImpl(ref t) => traits::VtableAutoImpl(t.fold_with(folder)), traits::VtableGenerator(ref d) => { traits::VtableGenerator(d.fold_with(folder)) } @@ -482,7 +480,7 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { traits::VtableImpl(ref v) => v.visit_with(visitor), - traits::VtableDefaultImpl(ref t) => t.visit_with(visitor), + traits::VtableAutoImpl(ref t) => t.visit_with(visitor), traits::VtableGenerator(ref d) => d.visit_with(visitor), traits::VtableClosure(ref d) => d.visit_with(visitor), traits::VtableFnPointer(ref d) => d.visit_with(visitor), diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs index 947e7117c4ea2..73fdbfe8831e3 100644 --- a/src/librustc/traits/trans/mod.rs +++ b/src/librustc/traits/trans/mod.rs @@ -13,89 +13,79 @@ // seems likely that they should eventually be merged into more // general routines. -use dep_graph::{DepGraph, DepKind, DepTrackingMap, DepTrackingMapConfig}; +use dep_graph::{DepKind, DepTrackingMapConfig}; use infer::TransNormalize; -use std::cell::RefCell; use std::marker::PhantomData; -use syntax::ast; -use syntax_pos::Span; +use syntax_pos::DUMMY_SP; use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable}; use ty::{self, Ty, TyCtxt}; use ty::subst::{Subst, Substs}; use ty::fold::{TypeFoldable, TypeFolder}; -use util::common::MemoizationMap; -impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { - /// Attempts to resolve an obligation to a vtable.. The result is - /// a shallow vtable resolution -- meaning that we do not - /// (necessarily) resolve all nested obligations on the impl. Note - /// that type check should guarantee to us that all nested - /// obligations *could be* resolved if we wanted to. - /// Assumes that this is run after the entire crate has been successfully type-checked. - pub fn trans_fulfill_obligation(self, - span: Span, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>) - -> Vtable<'tcx, ()> - { - // Remove any references to regions; this helps improve caching. - let trait_ref = self.erase_regions(&trait_ref); - - self.trans_trait_caches.trait_cache.memoize((param_env, trait_ref), || { - debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", - (param_env, trait_ref), trait_ref.def_id()); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - self.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = ObligationCause::misc(span, - ast::DUMMY_NODE_ID); - let obligation = Obligation::new(obligation_cause, - param_env, - trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - debug!("Encountered ambiguity selecting `{:?}` during trans, \ - presuming due to overflow", - trait_ref); - self.sess.span_fatal(span, - "reached the recursion limit during monomorphization \ - (selection ambiguity)"); - } - Err(e) => { - span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", - e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) - }) - } +/// Attempts to resolve an obligation to a vtable.. The result is +/// a shallow vtable resolution -- meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, + (param_env, trait_ref): + (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) + -> Vtable<'tcx, ()> +{ + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = Obligation::new(obligation_cause, + param_env, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + bug!("Encountered ambiguity selecting `{:?}` during trans, \ + presuming due to overflow", + trait_ref) + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during trans", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) +} +impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// Monomorphizes a type from the AST by first applying the in-scope /// substitutions and then normalizing any associated types. pub fn trans_apply_param_substs(self, @@ -109,6 +99,26 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { let substituted = self.erase_regions(&substituted); AssociatedTypeNormalizer::new(self).fold(&substituted) } + + pub fn trans_apply_param_substs_env( + self, + param_substs: &Substs<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T, + ) -> T + where + T: TransNormalize<'tcx>, + { + debug!( + "apply_param_substs_env(param_substs={:?}, value={:?}, param_env={:?})", + param_substs, + value, + param_env, + ); + let substituted = value.subst(self, param_substs); + let substituted = self.erase_regions(&substituted); + AssociatedTypeNormalizerEnv::new(self, param_env).fold(&substituted) + } } struct AssociatedTypeNormalizer<'a, 'gcx: 'a> { @@ -138,26 +148,42 @@ impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizer<'a, 'gcx> { if !ty.has_projections() { ty } else { - self.tcx.trans_trait_caches.project_cache.memoize(ty, || { - debug!("AssociatedTypeNormalizer: ty={:?}", ty); - self.tcx.normalize_associated_type(&ty) - }) + debug!("AssociatedTypeNormalizer: ty={:?}", ty); + self.tcx.fully_normalize_monormophic_ty(ty) } } } -/// Specializes caches used in trans -- in particular, they assume all -/// types are fully monomorphized and that free regions can be erased. -pub struct TransTraitCaches<'tcx> { - trait_cache: RefCell>>, - project_cache: RefCell>>, +struct AssociatedTypeNormalizerEnv<'a, 'gcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'gcx>, + param_env: ty::ParamEnv<'gcx>, } -impl<'tcx> TransTraitCaches<'tcx> { - pub fn new(graph: DepGraph) -> Self { - TransTraitCaches { - trait_cache: RefCell::new(DepTrackingMap::new(graph.clone())), - project_cache: RefCell::new(DepTrackingMap::new(graph)), +impl<'a, 'gcx> AssociatedTypeNormalizerEnv<'a, 'gcx> { + fn new(tcx: TyCtxt<'a, 'gcx, 'gcx>, param_env: ty::ParamEnv<'gcx>) -> Self { + Self { tcx, param_env } + } + + fn fold>(&mut self, value: &T) -> T { + if !value.has_projections() { + value.clone() + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizerEnv<'a, 'gcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'gcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> { + if !ty.has_projections() { + ty + } else { + debug!("AssociatedTypeNormalizerEnv: ty={:?}", ty); + self.tcx.normalize_associated_type_in_env(&ty, self.param_env) } } } diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index 42e0834e8e43b..898accb902159 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs @@ -43,8 +43,8 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::Predicate::ObjectSafe(data) => ty::Predicate::ObjectSafe(data), - ty::Predicate::ClosureKind(closure_def_id, kind) => - ty::Predicate::ClosureKind(closure_def_id, kind), + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind), ty::Predicate::Subtype(ref data) => ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)), diff --git a/src/librustc/ty/README.md b/src/librustc/ty/README.md index 4f63912a1e0d1..3fd956ecfb87d 100644 --- a/src/librustc/ty/README.md +++ b/src/librustc/ty/README.md @@ -42,7 +42,7 @@ wasteful. Often, we wish to write code that explicitly asserts that it is not taking place during inference. In that case, there is no "local" arena, and all the types that you can access are allocated in the -global arena. To express this, the idea is to us the same lifetime +global arena. To express this, the idea is to use the same lifetime for the `'gcx` and `'tcx` parameters of `TyCtxt`. Just to be a touch confusing, we tend to use the name `'tcx` in such contexts. Here is an example: @@ -100,10 +100,10 @@ fn test_type<'tcx>(ty: Ty<'tcx>) { The `sty` field (the origin of this name is unclear to me; perhaps structural type?) is of type `TypeVariants<'tcx>`, which is an enum -definined all of the different kinds of types in the compiler. +defining all of the different kinds of types in the compiler. > NB: inspecting the `sty` field on types during type inference can be -> risky, as there are may be inference variables and other things to +> risky, as there may be inference variables and other things to > consider, or sometimes types are not yet known that will become > known later.). @@ -132,7 +132,7 @@ a safe approximation, so that is what you get back. > you are going to be testing for type equality, you probably need to > start looking into the inference code to do it right. -You can also find various common types in the tcx itself by accessing +You can also find various common types in the `tcx` itself by accessing `tcx.types.bool`, `tcx.types.char`, etc (see `CommonTypes` for more). ### Beyond types: Other kinds of arena-allocated data structures @@ -143,7 +143,7 @@ module. Here are a few examples: - `Substs`, allocated with `mk_substs` -- this will intern a slice of types, often used to specify the values to be substituted for generics (e.g., `HashMap` - would be represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`. + would be represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`). - `TraitRef`, typically passed by value -- a **trait reference** consists of a reference to a trait along with its various type parameters (including `Self`), like `i32: Display` (here, the def-id diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs new file mode 100644 index 0000000000000..fbb14f39ade34 --- /dev/null +++ b/src/librustc/ty/codec.rs @@ -0,0 +1,396 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This module contains some shared code for encoding and decoding various +// things from the `ty` module, and in particular implements support for +// "shorthands" which allow to have pointers back into the already encoded +// stream instead of re-encoding the same thing twice. +// +// The functionality in here is shared between persisting to crate metadata and +// persisting to incr. comp. caches. + +use hir::def_id::{DefId, CrateNum}; +use middle::const_val::ByteArray; +use rustc_data_structures::fx::FxHashMap; +use rustc_serialize::{Decodable, Decoder, Encoder, Encodable, opaque}; +use std::hash::Hash; +use std::intrinsics; +use ty::{self, Ty, TyCtxt}; +use ty::subst::Substs; + +/// The shorthand encoding uses an enum's variant index `usize` +/// and is offset by this value so it never matches a real variant. +/// This offset is also chosen so that the first byte is never < 0x80. +pub const SHORTHAND_OFFSET: usize = 0x80; + +pub trait EncodableWithShorthand: Clone + Eq + Hash { + type Variant: Encodable; + fn variant(&self) -> &Self::Variant; +} + +impl<'tcx> EncodableWithShorthand for Ty<'tcx> { + type Variant = ty::TypeVariants<'tcx>; + fn variant(&self) -> &Self::Variant { + &self.sty + } +} + +impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> { + type Variant = ty::Predicate<'tcx>; + fn variant(&self) -> &Self::Variant { + self + } +} + +pub trait TyEncoder: Encoder { + fn position(&self) -> usize; +} + +impl<'buf> TyEncoder for opaque::Encoder<'buf> { + #[inline] + fn position(&self) -> usize { + self.position() + } +} + +/// Encode the given value or a previously cached shorthand. +pub fn encode_with_shorthand(encoder: &mut E, + value: &T, + cache: M) + -> Result<(), E::Error> + where E: TyEncoder, + M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + T: EncodableWithShorthand, +{ + let existing_shorthand = cache(encoder).get(value).cloned(); + if let Some(shorthand) = existing_shorthand { + return encoder.emit_usize(shorthand); + } + + let variant = value.variant(); + + let start = encoder.position(); + variant.encode(encoder)?; + let len = encoder.position() - start; + + // The shorthand encoding uses the same usize as the + // discriminant, with an offset so they can't conflict. + let discriminant = unsafe { intrinsics::discriminant_value(variant) }; + assert!(discriminant < SHORTHAND_OFFSET as u64); + let shorthand = start + SHORTHAND_OFFSET; + + // Get the number of bits that leb128 could fit + // in the same space as the fully encoded type. + let leb128_bits = len * 7; + + // Check that the shorthand is a not longer than the + // full encoding itself, i.e. it's an obvious win. + if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { + cache(encoder).insert(value.clone(), shorthand); + } + + Ok(()) +} + +pub fn encode_predicates<'tcx, E, C>(encoder: &mut E, + predicates: &ty::GenericPredicates<'tcx>, + cache: C) + -> Result<(), E::Error> + where E: TyEncoder, + C: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, usize>, +{ + predicates.parent.encode(encoder)?; + predicates.predicates.len().encode(encoder)?; + for predicate in &predicates.predicates { + encode_with_shorthand(encoder, predicate, &cache)? + } + Ok(()) +} + +pub trait TyDecoder<'a, 'tcx: 'a>: Decoder { + + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx>; + + fn peek_byte(&self) -> u8; + + fn position(&self) -> usize; + + fn cached_ty_for_shorthand(&mut self, + shorthand: usize, + or_insert_with: F) + -> Result, Self::Error> + where F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn with_position(&mut self, pos: usize, f: F) -> R + where F: FnOnce(&mut Self) -> R; + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; + + fn positioned_at_shorthand(&self) -> bool { + (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 + } +} + +#[inline] +pub fn decode_cnum<'a, 'tcx, D>(decoder: &mut D) -> Result + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let cnum = CrateNum::from_u32(u32::decode(decoder)?); + Ok(decoder.map_encoded_cnum_to_current(cnum)) +} + +#[inline] +pub fn decode_ty<'a, 'tcx, D>(decoder: &mut D) -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.cached_ty_for_shorthand(shorthand, |decoder| { + decoder.with_position(shorthand, Ty::decode) + }) + } else { + let tcx = decoder.tcx(); + Ok(tcx.mk_ty(ty::TypeVariants::decode(decoder)?)) + } +} + +#[inline] +pub fn decode_predicates<'a, 'tcx, D>(decoder: &mut D) + -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(ty::GenericPredicates { + parent: Decodable::decode(decoder)?, + predicates: (0..decoder.read_usize()?).map(|_| { + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.with_position(shorthand, ty::Predicate::decode) + } else { + ty::Predicate::decode(decoder) + } + }) + .collect::, _>>()?, + }) +} + +#[inline] +pub fn decode_substs<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx Substs<'tcx>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + let tcx = decoder.tcx(); + Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_region<'a, 'tcx, D>(decoder: &mut D) -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) +} + +#[inline] +pub fn decode_ty_slice<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Slice>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_adt_def<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::AdtDef, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let def_id = DefId::decode(decoder)?; + Ok(decoder.tcx().adt_def(def_id)) +} + +#[inline] +pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Slice>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx() + .mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D) + -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(ByteArray { + data: decoder.tcx().alloc_byte_array(&Vec::decode(decoder)?) + }) +} + +#[inline] +pub fn decode_const<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Const<'tcx>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) +} + +#[macro_export] +macro_rules! __impl_decoder_methods { + ($($name:ident -> $ty:ty;)*) => { + $(fn $name(&mut self) -> Result<$ty, Self::Error> { + self.opaque.$name() + })* + } +} + +#[macro_export] +macro_rules! implement_ty_decoder { + ($DecoderName:ident <$($typaram:tt),*>) => { + mod __ty_decoder_impl { + use super::$DecoderName; + use $crate::ty; + use $crate::ty::codec::*; + use $crate::ty::subst::Substs; + use $crate::hir::def_id::{CrateNum}; + use $crate::middle::const_val::ByteArray; + use rustc_serialize::{Decoder, SpecializedDecoder}; + use std::borrow::Cow; + + impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { + type Error = String; + + __impl_decoder_methods! { + read_nil -> (); + + read_u128 -> u128; + read_u64 -> u64; + read_u32 -> u32; + read_u16 -> u16; + read_u8 -> u8; + read_usize -> usize; + + read_i128 -> i128; + read_i64 -> i64; + read_i32 -> i32; + read_i16 -> i16; + read_i8 -> i8; + read_isize -> isize; + + read_bool -> bool; + read_f64 -> f64; + read_f32 -> f32; + read_char -> char; + read_str -> Cow; + } + + fn error(&mut self, err: &str) -> Self::Error { + self.opaque.error(err) + } + } + + // FIXME(#36588) These impls are horribly unsound as they allow + // the caller to pick any lifetime for 'tcx, including 'static, + // by using the unspecialized proxies to them. + + impl<$($typaram),*> SpecializedDecoder + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result { + decode_cnum(self) + } + } + + impl<$($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result, Self::Error> { + decode_ty(self) + } + } + + impl<$($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) + -> Result, Self::Error> { + decode_predicates(self) + } + } + + impl<$($typaram),*> SpecializedDecoder<&'tcx Substs<'tcx>> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result<&'tcx Substs<'tcx>, Self::Error> { + decode_substs(self) + } + } + + impl<$($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result, Self::Error> { + decode_region(self) + } + } + + impl<$($typaram),*> SpecializedDecoder<&'tcx ty::Slice>> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) + -> Result<&'tcx ty::Slice>, Self::Error> { + decode_ty_slice(self) + } + } + + impl<$($typaram),*> SpecializedDecoder<&'tcx ty::AdtDef> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result<&'tcx ty::AdtDef, Self::Error> { + decode_adt_def(self) + } + } + + impl<$($typaram),*> SpecializedDecoder<&'tcx ty::Slice>> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) + -> Result<&'tcx ty::Slice>, Self::Error> { + decode_existential_predicate_slice(self) + } + } + + impl<$($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result, Self::Error> { + decode_byte_array(self) + } + } + + impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::Const<'tcx>> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + decode_const(self) + } + } + } + } +} + diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 315ba622ccf6d..2b264566415e2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -23,17 +23,17 @@ use hir::map as hir_map; use hir::map::DefPathHash; use lint::{self, Lint}; use ich::{StableHashingContext, NodeIdHashingMode}; +use infer::outlives::free_region_map::FreeRegionMap; use middle::const_val::ConstVal; -use middle::cstore::{CrateStore, LinkMeta, EncodedMetadataHashes}; +use middle::cstore::{CrateStore, LinkMeta}; use middle::cstore::EncodedMetadata; -use middle::free_region::FreeRegionMap; use middle::lang_items; use middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use middle::stability; -use mir::Mir; -use mir::transform::Passes; +use mir::{Mir, interpret}; use ty::subst::{Kind, Substs}; use ty::ReprOptions; +use ty::Instance; use traits; use ty::{self, Ty, TypeAndMut}; use ty::{TyS, TypeVariants, Slice}; @@ -42,8 +42,7 @@ use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predic use ty::RegionKind; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; -use ty::layout::{Layout, TargetDataLayout}; -use ty::inhabitedness::DefIdForest; +use ty::layout::{LayoutDetails, TargetDataLayout}; use ty::maps; use ty::steal::Steal; use ty::BindingMode; @@ -80,7 +79,7 @@ use hir; /// Internal storage pub struct GlobalArenas<'tcx> { // internings - layout: TypedArena, + layout: TypedArena, // references generics: TypedArena, @@ -89,6 +88,8 @@ pub struct GlobalArenas<'tcx> { steal_mir: TypedArena>>, mir: TypedArena>, tables: TypedArena>, + /// miri allocations + const_allocs: TypedArena, } impl<'tcx> GlobalArenas<'tcx> { @@ -101,6 +102,7 @@ impl<'tcx> GlobalArenas<'tcx> { steal_mir: TypedArena::new(), mir: TypedArena::new(), tables: TypedArena::new(), + const_allocs: TypedArena::new(), } } } @@ -337,22 +339,30 @@ pub struct TypeckTables<'tcx> { adjustments: ItemLocalMap>>, - // Stores the actual binding mode for all instances of hir::BindingAnnotation. + /// Stores the actual binding mode for all instances of hir::BindingAnnotation. pat_binding_modes: ItemLocalMap, + /// Stores the types which were implicitly dereferenced in pattern binding modes + /// for later usage in HAIR lowering. For example, + /// + /// ``` + /// match &&Some(5i32) { + /// Some(n) => {}, + /// _ => {}, + /// } + /// ``` + /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. + /// + /// See: + /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions + pat_adjustments: ItemLocalMap>>, + /// Borrows pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, - /// Records the type of each closure. - closure_tys: ItemLocalMap>, - - /// Records the kind of each closure and the span and name of the variable - /// that caused the closure to be this kind. - closure_kinds: ItemLocalMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, - - generator_sigs: ItemLocalMap>>, - - generator_interiors: ItemLocalMap>, + /// Records the reasons that we picked the kind of each closure; + /// not all closures are present in the map. + closure_kind_origins: ItemLocalMap<(Span, ast::Name)>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions @@ -372,8 +382,10 @@ pub struct TypeckTables<'tcx> { cast_kinds: ItemLocalMap, /// Set of trait imports actually used in the method resolution. - /// This is used for warning unused imports. - pub used_trait_imports: DefIdSet, + /// This is used for warning unused imports. During type + /// checking, this `Rc` should not be cloned: it must have a ref-count + /// of 1 so that we can insert things into the set mutably. + pub used_trait_imports: Rc, /// If any errors occurred while type-checking this body, /// this field will be set to `true`. @@ -394,15 +406,13 @@ impl<'tcx> TypeckTables<'tcx> { node_substs: ItemLocalMap(), adjustments: ItemLocalMap(), pat_binding_modes: ItemLocalMap(), + pat_adjustments: ItemLocalMap(), upvar_capture_map: FxHashMap(), - generator_sigs: ItemLocalMap(), - generator_interiors: ItemLocalMap(), - closure_tys: ItemLocalMap(), - closure_kinds: ItemLocalMap(), + closure_kind_origins: ItemLocalMap(), liberated_fn_sigs: ItemLocalMap(), fru_field_types: ItemLocalMap(), cast_kinds: ItemLocalMap(), - used_trait_imports: DefIdSet(), + used_trait_imports: Rc::new(DefIdSet()), tainted_by_errors: false, free_region_map: FreeRegionMap::new(), } @@ -574,38 +584,36 @@ impl<'tcx> TypeckTables<'tcx> { } } - pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { - self.upvar_capture_map[&upvar_id] - } - - pub fn closure_tys(&self) -> LocalTableInContext> { + pub fn pat_adjustments(&self) -> LocalTableInContext>> { LocalTableInContext { local_id_root: self.local_id_root, - data: &self.closure_tys + data: &self.pat_adjustments, } } - pub fn closure_tys_mut(&mut self) - -> LocalTableInContextMut> { + pub fn pat_adjustments_mut(&mut self) + -> LocalTableInContextMut>> { LocalTableInContextMut { local_id_root: self.local_id_root, - data: &mut self.closure_tys + data: &mut self.pat_adjustments, } } - pub fn closure_kinds(&self) -> LocalTableInContext<(ty::ClosureKind, - Option<(Span, ast::Name)>)> { + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { + self.upvar_capture_map[&upvar_id] + } + + pub fn closure_kind_origins(&self) -> LocalTableInContext<(Span, ast::Name)> { LocalTableInContext { local_id_root: self.local_id_root, - data: &self.closure_kinds + data: &self.closure_kind_origins } } - pub fn closure_kinds_mut(&mut self) - -> LocalTableInContextMut<(ty::ClosureKind, Option<(Span, ast::Name)>)> { + pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<(Span, ast::Name)> { LocalTableInContextMut { local_id_root: self.local_id_root, - data: &mut self.closure_kinds + data: &mut self.closure_kind_origins } } @@ -650,42 +658,6 @@ impl<'tcx> TypeckTables<'tcx> { data: &mut self.cast_kinds } } - - pub fn generator_sigs(&self) - -> LocalTableInContext>> - { - LocalTableInContext { - local_id_root: self.local_id_root, - data: &self.generator_sigs, - } - } - - pub fn generator_sigs_mut(&mut self) - -> LocalTableInContextMut>> - { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.generator_sigs, - } - } - - pub fn generator_interiors(&self) - -> LocalTableInContext> - { - LocalTableInContext { - local_id_root: self.local_id_root, - data: &self.generator_interiors, - } - } - - pub fn generator_interiors_mut(&mut self) - -> LocalTableInContextMut> - { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.generator_interiors, - } - } } impl<'gcx> HashStable> for TypeckTables<'gcx> { @@ -699,9 +671,9 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { ref node_substs, ref adjustments, ref pat_binding_modes, + ref pat_adjustments, ref upvar_capture_map, - ref closure_tys, - ref closure_kinds, + ref closure_kind_origins, ref liberated_fn_sigs, ref fru_field_types, @@ -710,8 +682,6 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { ref used_trait_imports, tainted_by_errors, ref free_region_map, - ref generator_sigs, - ref generator_interiors, } = *self; hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { @@ -720,6 +690,7 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { node_substs.hash_stable(hcx, hasher); adjustments.hash_stable(hcx, hasher); pat_binding_modes.hash_stable(hcx, hasher); + pat_adjustments.hash_stable(hcx, hasher); hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { let ty::UpvarId { var_id, @@ -735,20 +706,17 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { }; let closure_def_id = DefId { krate: local_id_root.krate, - index: closure_expr_id, + index: closure_expr_id.to_def_id().index, }; (hcx.def_path_hash(var_owner_def_id), var_id.local_id, hcx.def_path_hash(closure_def_id)) }); - closure_tys.hash_stable(hcx, hasher); - closure_kinds.hash_stable(hcx, hasher); + closure_kind_origins.hash_stable(hcx, hasher); liberated_fn_sigs.hash_stable(hcx, hasher); fru_field_types.hash_stable(hcx, hasher); cast_kinds.hash_stable(hcx, hasher); - generator_sigs.hash_stable(hcx, hasher); - generator_interiors.hash_stable(hcx, hasher); used_trait_imports.hash_stable(hcx, hasher); tainted_by_errors.hash_stable(hcx, hasher); free_region_map.hash_stable(hcx, hasher); @@ -819,11 +787,13 @@ pub struct GlobalCtxt<'tcx> { pub sess: &'tcx Session, - - pub trans_trait_caches: traits::trans::TransTraitCaches<'tcx>, - pub dep_graph: DepGraph, + /// This provides access to the incr. comp. on-disk cache for query results. + /// Do not access this directly. It is only meant to be used by + /// `DepGraph::try_mark_green()` and the query infrastructure in `ty::maps`. + pub(crate) on_disk_query_result_cache: maps::OnDiskCache<'tcx>, + /// Common types, pre-interned for your convenience. pub types: CommonTypes<'tcx>, @@ -846,8 +816,6 @@ pub struct GlobalCtxt<'tcx> { pub maps: maps::Maps<'tcx>, - pub mir_passes: Rc, - // Records the free variables refrenced by every closure // expression. Do not track deps for this, just recompute it from // scratch every time. @@ -860,16 +828,6 @@ pub struct GlobalCtxt<'tcx> { // Internal cache for metadata decoding. No need to track deps on this. pub rcache: RefCell>>, - // FIXME dep tracking -- should be harmless enough - pub normalized_cache: RefCell, Ty<'tcx>>>, - - pub inhabitedness_cache: RefCell, DefIdForest>>, - - /// Set of nodes which mark locals as mutable which end up getting used at - /// some point. Local variable definitions not in this set can be warned - /// about. - pub used_mut_nodes: RefCell, - /// Caches the results of trait selection. This cache is used /// for things that do not have to do with the parameters in scope. pub selection_cache: traits::SelectionCache<'tcx>, @@ -879,9 +837,6 @@ pub struct GlobalCtxt<'tcx> { /// Merge this with `selection_cache`? pub evaluation_cache: traits::EvaluationCache<'tcx>, - /// Maps Expr NodeId's to `true` iff `&expr` can have 'static lifetime. - pub rvalue_promotable_to_static: RefCell>, - /// The definite name of the current crate after taking into account /// attributes, commandline parameters, etc. pub crate_name: Symbol, @@ -898,7 +853,9 @@ pub struct GlobalCtxt<'tcx> { stability_interner: RefCell>, - layout_interner: RefCell>, + pub interpret_interner: RefCell>, + + layout_interner: RefCell>, /// A vector of every trait accessible in the whole crate /// (i.e. including those from subcrates). This is used only for @@ -917,6 +874,104 @@ pub struct GlobalCtxt<'tcx> { output_filenames: Arc, } +/// Everything needed to efficiently work with interned allocations +#[derive(Debug, Default)] +pub struct InterpretInterner<'tcx> { + /// Stores the value of constants (and deduplicates the actual memory) + allocs: FxHashSet<&'tcx interpret::Allocation>, + + /// Allows obtaining function instance handles via a unique identifier + functions: FxHashMap>, + + /// Inverse map of `interpret_functions`. + /// Used so we don't allocate a new pointer every time we need one + function_cache: FxHashMap, u64>, + + /// Allows obtaining const allocs via a unique identifier + alloc_by_id: FxHashMap, + + /// The AllocId to assign to the next new regular allocation. + /// Always incremented, never gets smaller. + next_id: u64, + + /// Allows checking whether a constant already has an allocation + /// + /// The pointers are to the beginning of an `alloc_by_id` allocation + alloc_cache: FxHashMap, interpret::PtrAndAlign>, + + /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate + /// allocations for string and bytestring literals. + literal_alloc_cache: FxHashMap, u64>, +} + +impl<'tcx> InterpretInterner<'tcx> { + pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> u64 { + if let Some(&alloc_id) = self.function_cache.get(&instance) { + return alloc_id; + } + let id = self.reserve(); + debug!("creating fn ptr: {}", id); + self.functions.insert(id, instance); + self.function_cache.insert(instance, id); + id + } + + pub fn get_fn( + &self, + id: u64, + ) -> Option> { + self.functions.get(&id).cloned() + } + + pub fn get_alloc( + &self, + id: u64, + ) -> Option<&'tcx interpret::Allocation> { + self.alloc_by_id.get(&id).cloned() + } + + pub fn get_cached( + &self, + global_id: interpret::GlobalId<'tcx>, + ) -> Option { + self.alloc_cache.get(&global_id).cloned() + } + + pub fn cache( + &mut self, + global_id: interpret::GlobalId<'tcx>, + ptr: interpret::PtrAndAlign, + ) { + if let Some(old) = self.alloc_cache.insert(global_id, ptr) { + bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old); + } + } + + pub fn intern_at_reserved( + &mut self, + id: u64, + alloc: &'tcx interpret::Allocation, + ) { + if let Some(old) = self.alloc_by_id.insert(id, alloc) { + bug!("tried to intern allocation at {}, but was already existing as {:#?}", id, old); + } + } + + /// obtains a new allocation ID that can be referenced but does not + /// yet have an allocation backing it. + pub fn reserve( + &mut self, + ) -> u64 { + let next = self.next_id; + self.next_id = self.next_id + .checked_add(1) + .expect("You overflowed a u64 by incrementing by 1... \ + You've just earned yourself a free drink if we ever meet. \ + Seriously, how did you do that?!"); + next + } +} + impl<'tcx> GlobalCtxt<'tcx> { /// Get the global TyCtxt. pub fn global_tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { @@ -984,6 +1039,41 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn intern_const_alloc( + self, + alloc: interpret::Allocation, + ) -> &'gcx interpret::Allocation { + if let Some(alloc) = self.interpret_interner.borrow().allocs.get(&alloc) { + return alloc; + } + + let interned = self.global_arenas.const_allocs.alloc(alloc); + if let Some(prev) = self.interpret_interner.borrow_mut().allocs.replace(interned) { + bug!("Tried to overwrite interned Allocation: {:#?}", prev) + } + interned + } + + /// Allocates a byte or string literal for `mir::interpret` + pub fn allocate_cached(self, bytes: &[u8]) -> u64 { + // check whether we already allocated this literal or a constant with the same memory + if let Some(&alloc_id) = self.interpret_interner.borrow().literal_alloc_cache.get(bytes) { + return alloc_id; + } + // create an allocation that just contains these bytes + let alloc = interpret::Allocation::from_bytes(bytes); + let alloc = self.intern_const_alloc(alloc); + + let mut int = self.interpret_interner.borrow_mut(); + // the next unique id + let id = int.reserve(); + // make the allocation identifiable + int.alloc_by_id.insert(id, alloc); + // cache it for the future + int.literal_alloc_cache.insert(bytes.to_owned(), id); + id + } + pub fn intern_stability(self, stab: attr::Stability) -> &'gcx attr::Stability { if let Some(st) = self.stability_interner.borrow().get(&stab) { return st; @@ -996,7 +1086,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { interned } - pub fn intern_layout(self, layout: Layout) -> &'gcx Layout { + pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails { if let Some(layout) = self.layout_interner.borrow().get(&layout) { return layout; } @@ -1032,12 +1122,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { cstore: &'tcx CrateStore, local_providers: ty::maps::Providers<'tcx>, extern_providers: ty::maps::Providers<'tcx>, - mir_passes: Rc, arenas: &'tcx GlobalArenas<'tcx>, arena: &'tcx DroplessArena, resolutions: ty::Resolutions, named_region_map: resolve_lifetime::NamedRegionMap, hir: hir_map::Map<'tcx>, + on_disk_query_result_cache: maps::OnDiskCache<'tcx>, crate_name: &str, tx: mpsc::Sender>, output_filenames: &OutputFilenames, @@ -1118,10 +1208,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { tls::enter_global(GlobalCtxt { sess: s, cstore, - trans_trait_caches: traits::trans::TransTraitCaches::new(dep_graph.clone()), global_arenas: arenas, global_interners: interners, dep_graph: dep_graph.clone(), + on_disk_query_result_cache, types: common_types, named_region_map: NamedRegionMap { defs, @@ -1148,20 +1238,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { hir, def_path_hash_to_def_id, maps: maps::Maps::new(providers), - mir_passes, rcache: RefCell::new(FxHashMap()), - normalized_cache: RefCell::new(FxHashMap()), - inhabitedness_cache: RefCell::new(FxHashMap()), - used_mut_nodes: RefCell::new(NodeSet()), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), - rvalue_promotable_to_static: RefCell::new(NodeMap()), crate_name: Symbol::intern(crate_name), data_layout, layout_interner: RefCell::new(FxHashSet()), layout_depth: Cell::new(0), derive_macros: RefCell::new(NodeMap()), stability_interner: RefCell::new(FxHashSet()), + interpret_interner: Default::default(), all_traits: RefCell::new(None), tx_to_llvm_workers: tx, output_filenames: Arc::new(output_filenames.clone()), @@ -1178,11 +1264,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn stability(self) -> Rc> { - // FIXME(#42293) we should actually track this, but fails too many tests - // today. - self.dep_graph.with_ignore(|| { - self.stability_index(LOCAL_CRATE) - }) + self.stability_index(LOCAL_CRATE) } pub fn crates(self) -> Rc> { @@ -1219,6 +1301,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn def_path_debug_str(self, def_id: DefId) -> String { + // We are explicitly not going through queries here in order to get + // crate name and disambiguator since this code is called from debug!() + // statements within the query system and we'd run into endless + // recursion otherwise. + let (crate_name, crate_disambiguator) = if def_id.is_local() { + (self.crate_name.clone(), + self.sess.local_crate_disambiguator()) + } else { + (self.cstore.crate_name_untracked(def_id.krate), + self.cstore.crate_disambiguator_untracked(def_id.krate)) + }; + + format!("{}[{}]{}", + crate_name, + // Don't print the whole crate disambiguator. That's just + // annoying in debug output. + &(crate_disambiguator.to_fingerprint().to_hex())[..4], + self.def_path(def_id).to_string_no_crate()) + } + pub fn metadata_encoding_version(self) -> Vec { self.cstore.metadata_encoding_version().to_vec() } @@ -1266,11 +1369,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.in_scope_traits_map(def_index); } } + + pub fn serialize_query_result_cache(self, + encoder: &mut E) + -> Result<(), E::Error> + where E: ty::codec::TyEncoder + { + self.on_disk_query_result_cache.serialize(self.global_tcx(), encoder) + } + } impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { pub fn encode_metadata(self, link_meta: &LinkMeta, reachable: &NodeSet) - -> (EncodedMetadata, EncodedMetadataHashes) + -> EncodedMetadata { self.cstore.encode_metadata(self, link_meta, reachable) } @@ -1576,12 +1688,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { pub fn print_debug_stats(self) { sty_debug_print!( self, - TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator, + TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator, TyForeign, TyDynamic, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon); println!("Substs interner: #{}", self.interners.substs.borrow().len()); println!("Region interner: #{}", self.interners.region.borrow().len()); println!("Stability interner: #{}", self.stability_interner.borrow().len()); + println!("Interpret interner: #{}", self.interpret_interner.borrow().allocs.len()); println!("Layout interner: #{}", self.layout_interner.borrow().len()); } } @@ -1827,6 +1940,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(TyAdt(def, substs)) } + pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> { + self.mk_ty(TyForeign(def_id)) + } + pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> { let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem); let adt_def = self.adt_def(def_id); @@ -1932,11 +2049,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn mk_closure(self, closure_id: DefId, - substs: &'tcx Substs<'tcx>) - -> Ty<'tcx> { - self.mk_closure_from_closure_substs(closure_id, ClosureSubsts { - substs, - }) + substs: ClosureSubsts<'tcx>) + -> Ty<'tcx> { + self.mk_closure_from_closure_substs(closure_id, substs) } pub fn mk_closure_from_closure_substs(self, @@ -2286,4 +2401,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { assert_eq!(cnum, LOCAL_CRATE); tcx.sess.features.borrow().clone_closures }; + providers.fully_normalize_monormophic_ty = |tcx, ty| { + tcx.fully_normalize_associated_types_in(&ty) + }; } diff --git a/src/librustc/ty/erase_regions.rs b/src/librustc/ty/erase_regions.rs new file mode 100644 index 0000000000000..4f8fca67949b5 --- /dev/null +++ b/src/librustc/ty/erase_regions.rs @@ -0,0 +1,79 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ty::{self, Ty, TyCtxt}; +use ty::fold::{TypeFolder, TypeFoldable}; + +pub(super) fn provide(providers: &mut ty::maps::Providers) { + *providers = ty::maps::Providers { + erase_regions_ty, + ..*providers + }; +} + +fn erase_regions_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + // NB: use `super_fold_with` here. If we used `fold_with`, it + // could invoke the `erase_regions_ty` query recursively. + ty.super_fold_with(&mut RegionEraserVisitor { tcx }) +} + +impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { + /// Returns an equivalent value with all free regions removed (note + /// that late-bound regions remain, because they are important for + /// subtyping, but they are anonymized and normalized as well).. + pub fn erase_regions(self, value: &T) -> T + where T : TypeFoldable<'tcx> + { + let value1 = value.fold_with(&mut RegionEraserVisitor { tcx: self }); + debug!("erase_regions({:?}) = {:?}", value, value1); + value1 + } +} + +struct RegionEraserVisitor<'a, 'gcx: 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, +} + +impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let Some(ty_lifted) = self.tcx.lift_to_global(&ty) { + self.tcx.erase_regions_ty(ty_lifted) + } else { + ty.super_fold_with(self) + } + } + + fn fold_binder(&mut self, t: &ty::Binder) -> ty::Binder + where T : TypeFoldable<'tcx> + { + let u = self.tcx.anonymize_late_bound_regions(t); + u.super_fold_with(self) + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + // because late-bound regions affect subtyping, we can't + // erase the bound/free distinction, but we can replace + // all free regions with 'erased. + // + // Note that we *CAN* replace early-bound regions -- the + // type system never "sees" those, they get substituted + // away. In trans, they will always be erased to 'erased + // whenever a substitution occurs. + match *r { + ty::ReLateBound(..) => r, + _ => self.tcx.types.re_erased + } + } +} + diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 52a8389bd8f5f..cb68e576e5af9 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -49,11 +49,17 @@ pub enum TypeError<'tcx> { FloatMismatch(ExpectedFound), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), - CyclicTy, + + /// Instantiating a type variable with the given type would have + /// created a cycle (because it appears somewhere within that + /// type). + CyclicTy(Ty<'tcx>), ProjectionMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), TyParamDefaultMismatch(ExpectedFound>), ExistentialMismatch(ExpectedFound<&'tcx ty::Slice>>), + + OldStyleLUB(Box>), } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] @@ -82,7 +88,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { } match *self { - CyclicTy => write!(f, "cyclic type of infinite size"), + CyclicTy(_) => write!(f, "cyclic type of infinite size"), Mismatch => write!(f, "types differ"), UnsafetyMismatch(values) => { write!(f, "expected {} fn, found {} fn", @@ -170,6 +176,9 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { report_maybe_different(f, format!("trait `{}`", values.expected), format!("trait `{}`", values.found)) } + OldStyleLUB(ref err) => { + write!(f, "{}", err) + } } } } @@ -182,6 +191,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyTuple(ref tys, _) if tys.is_empty() => self.to_string(), ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)), + ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)), ty::TyArray(_, n) => { if let ConstVal::Integral(ConstInt::Usize(n)) = n.val { format!("array of {} elements", n) @@ -292,6 +302,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { db.span_note(found.origin_span, "...that also applies to the same type variable here"); } + OldStyleLUB(err) => { + db.note("this was previously accepted by the compiler but has been phased out"); + db.note("for more information, see https://github.com/rust-lang/rust/issues/45852"); + + self.note_and_explain_type_err(db, &err, sp); + } + CyclicTy(ty) => { + // Watch out for various cases of cyclic types and try to explain. + if ty.is_closure() || ty.is_generator() { + db.note("closures cannot capture themselves or take themselves as argument;\n\ + this error may be the result of a recent compiler bug-fix,\n\ + see https://github.com/rust-lang/rust/issues/46062 for more details"); + } + } _ => {} } } diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 490bfe78a9a1f..138f6af77c658 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -49,6 +49,7 @@ pub enum SimplifiedTypeGen AnonSimplifiedType(D), FunctionSimplifiedType(usize), ParameterSimplifiedType, + ForeignSimplifiedType(DefId), } /// Tries to simplify a type by dropping type parameters, deref'ing away any reference types, etc. @@ -113,6 +114,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::TyAnon(def_id, _) => { Some(AnonSimplifiedType(def_id)) } + ty::TyForeign(def_id) => { + Some(ForeignSimplifiedType(def_id)) + } ty::TyInfer(_) | ty::TyError => None, } } @@ -140,6 +144,7 @@ impl SimplifiedTypeGen { AnonSimplifiedType(d) => AnonSimplifiedType(map(d)), FunctionSimplifiedType(n) => FunctionSimplifiedType(n), ParameterSimplifiedType => ParameterSimplifiedType, + ForeignSimplifiedType(d) => ForeignSimplifiedType(d), } } } @@ -172,6 +177,7 @@ impl<'gcx, D> HashStable> for SimplifiedTypeGen GeneratorSimplifiedType(d) => d.hash_stable(hcx, hasher), AnonSimplifiedType(d) => d.hash_stable(hcx, hasher), FunctionSimplifiedType(n) => n.hash_stable(hcx, hasher), + ForeignSimplifiedType(d) => d.hash_stable(hcx, hasher), } } } diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 9ece719c76470..63c646dbd2310 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -63,7 +63,8 @@ impl FlagComputation { &ty::TyFloat(_) | &ty::TyUint(_) | &ty::TyNever | - &ty::TyStr => { + &ty::TyStr | + &ty::TyForeign(..) => { } // You might think that we could just return TyError for diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 543e8f3e2f04d..069dc0275cbb1 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -40,10 +40,12 @@ //! and does not need to visit anything else. use middle::const_val::ConstVal; +use hir::def_id::DefId; use ty::{self, Binder, Ty, TyCtxt, TypeFlags}; use std::fmt; -use util::nodemap::{FxHashMap, FxHashSet}; +use std::collections::BTreeMap; +use util::nodemap::FxHashSet; /// The TypeFoldable trait is implemented for every type that can be folded. /// Basically, every type that has a corresponding method in TypeFolder. @@ -218,6 +220,43 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { { value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f)) } + + pub fn for_each_free_region(self, + value: &T, + callback: F) + where F: FnMut(ty::Region<'tcx>), + T: TypeFoldable<'tcx>, + { + value.visit_with(&mut RegionVisitor { current_depth: 0, callback }); + + struct RegionVisitor { + current_depth: u32, + callback: F, + } + + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor + where F : FnMut(ty::Region<'tcx>) + { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.current_depth += 1; + t.skip_binder().visit_with(self); + self.current_depth -= 1; + + false // keep visiting + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + match *r { + ty::ReLateBound(debruijn, _) if debruijn.depth <= self.current_depth => { + /* ignore bound regions */ + } + _ => (self.callback)(r), + } + + false // keep visiting + } + } + } } /// Folds over the substructure of a type, visiting its component @@ -287,14 +326,22 @@ struct RegionReplacer<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, current_depth: u32, fld_r: &'a mut (FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), - map: FxHashMap> + map: BTreeMap> } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { + /// Replace all regions bound by the given `Binder` with the + /// results returned by the closure; the closure is expected to + /// return a free region (relative to this binder), and hence the + /// binder is removed in the return type. The closure is invoked + /// once for each unique `BoundRegion`; multiple references to the + /// same `BoundRegion` will reuse the previous result. A map is + /// returned at the end with each bound region and the free region + /// that replaced it. pub fn replace_late_bound_regions(self, value: &Binder, mut f: F) - -> (T, FxHashMap>) + -> (T, BTreeMap>) where F : FnMut(ty::BoundRegion) -> ty::Region<'tcx>, T : TypeFoldable<'tcx>, { @@ -303,6 +350,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { (result, replacer.map) } + /// Replace any late-bound regions bound in `value` with + /// free variants attached to `all_outlive_scope`. + pub fn liberate_late_bound_regions( + &self, + all_outlive_scope: DefId, + value: &ty::Binder + ) -> T + where T: TypeFoldable<'tcx> { + self.replace_late_bound_regions(value, |br| { + self.mk_region(ty::ReFree(ty::FreeRegion { + scope: all_outlive_scope, + bound_region: br + })) + }).0 + } + /// Flattens two binding levels into one. So `for<'a> for<'b> Foo` /// becomes `for<'a,'b> Foo`. pub fn flatten_late_bound_regions(self, bound2_value: &Binder>) @@ -326,16 +389,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Binder(value) } - pub fn no_late_bound_regions(self, value: &Binder) -> Option - where T : TypeFoldable<'tcx> - { - if value.0.has_escaping_regions() { - None - } else { - Some(value.0.clone()) - } - } - /// Returns a set of all late-bound regions that are constrained /// by `value`, meaning that if we instantiate those LBR with /// variables and equate `value` with something else, those @@ -401,7 +454,7 @@ impl<'a, 'gcx, 'tcx> RegionReplacer<'a, 'gcx, 'tcx> { tcx, current_depth: 1, fld_r, - map: FxHashMap() + map: BTreeMap::default() } } } @@ -444,67 +497,6 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionReplacer<'a, 'gcx, 'tcx> { } } -/////////////////////////////////////////////////////////////////////////// -// Region eraser - -impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { - /// Returns an equivalent value with all free regions removed (note - /// that late-bound regions remain, because they are important for - /// subtyping, but they are anonymized and normalized as well).. - pub fn erase_regions(self, value: &T) -> T - where T : TypeFoldable<'tcx> - { - let value1 = value.fold_with(&mut RegionEraser(self)); - debug!("erase_regions({:?}) = {:?}", - value, value1); - return value1; - - struct RegionEraser<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(TyCtxt<'a, 'gcx, 'tcx>); - - impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraser<'a, 'gcx, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.0 } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let Some(u) = self.tcx().normalized_cache.borrow().get(&ty).cloned() { - return u; - } - - // FIXME(eddyb) should local contexts have a cache too? - if let Some(ty_lifted) = self.tcx().lift_to_global(&ty) { - let tcx = self.tcx().global_tcx(); - let t_norm = ty_lifted.super_fold_with(&mut RegionEraser(tcx)); - tcx.normalized_cache.borrow_mut().insert(ty_lifted, t_norm); - t_norm - } else { - ty.super_fold_with(self) - } - } - - fn fold_binder(&mut self, t: &ty::Binder) -> ty::Binder - where T : TypeFoldable<'tcx> - { - let u = self.tcx().anonymize_late_bound_regions(t); - u.super_fold_with(self) - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - // because late-bound regions affect subtyping, we can't - // erase the bound/free distinction, but we can replace - // all free regions with 'erased. - // - // Note that we *CAN* replace early-bound regions -- the - // type system never "sees" those, they get substituted - // away. In trans, they will always be erased to 'erased - // whenever a substitution occurs. - match *r { - ty::ReLateBound(..) => r, - _ => self.tcx().types.re_erased - } - } - } - } -} - /////////////////////////////////////////////////////////////////////////// // Region shifter // diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index a829814e0905b..0072512464a0e 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -10,7 +10,7 @@ use util::nodemap::{FxHashMap, FxHashSet}; use ty::context::TyCtxt; -use ty::{AdtDef, VariantDef, FieldDef, TyS}; +use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS}; use ty::{DefId, Substs}; use ty::{AdtKind, Visibility}; use ty::TypeVariants::*; @@ -62,13 +62,95 @@ mod def_id_forest; // This code should only compile in modules where the uninhabitedness of Foo is // visible. +impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { + /// 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_ty_uninhabited_from(self, module: DefId, ty: Ty<'tcx>) -> bool { + // 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() + // ``` + self.ty_inhabitedness_forest(ty).contains(self, module) + } + + pub fn is_ty_uninhabited_from_all_modules(self, ty: Ty<'tcx>) -> bool { + !self.ty_inhabitedness_forest(ty).is_empty() + } + + fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest { + ty.uninhabited_from(&mut FxHashMap(), self) + } + + pub fn is_enum_variant_uninhabited_from(self, + module: DefId, + variant: &'tcx VariantDef, + substs: &'tcx Substs<'tcx>) + -> bool + { + self.variant_inhabitedness_forest(variant, substs).contains(self, module) + } + + pub fn is_variant_uninhabited_from_all_modules(self, + variant: &'tcx VariantDef, + substs: &'tcx Substs<'tcx>) + -> bool + { + !self.variant_inhabitedness_forest(variant, substs).is_empty() + } + + fn variant_inhabitedness_forest(self, variant: &'tcx VariantDef, substs: &'tcx Substs<'tcx>) + -> DefIdForest { + // Determine the ADT kind: + let adt_def_id = self.adt_def_id_of_variant(variant); + let adt_kind = self.adt_def(adt_def_id).adt_kind(); + + // Compute inhabitedness forest: + variant.uninhabited_from(&mut FxHashMap(), self, substs, adt_kind) + } +} + impl<'a, 'gcx, 'tcx> AdtDef { /// Calculate the forest of DefIds from which this adt is visibly uninhabited. - pub fn uninhabited_from( - &self, - visited: &mut FxHashMap>>, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> DefIdForest + fn uninhabited_from( + &self, + visited: &mut FxHashMap>>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>) -> DefIdForest { DefIdForest::intersection(tcx, self.variants.iter().map(|v| { v.uninhabited_from(visited, tcx, substs, self.adt_kind()) @@ -78,12 +160,12 @@ impl<'a, 'gcx, 'tcx> AdtDef { impl<'a, 'gcx, 'tcx> VariantDef { /// Calculate the forest of DefIds from which this variant is visibly uninhabited. - pub fn uninhabited_from( - &self, - visited: &mut FxHashMap>>, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - adt_kind: AdtKind) -> DefIdForest + fn uninhabited_from( + &self, + visited: &mut FxHashMap>>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + adt_kind: AdtKind) -> DefIdForest { match adt_kind { AdtKind::Union => { @@ -107,12 +189,12 @@ impl<'a, 'gcx, 'tcx> VariantDef { impl<'a, 'gcx, 'tcx> FieldDef { /// Calculate the forest of DefIds from which this field is visibly uninhabited. - pub fn uninhabited_from( - &self, - visited: &mut FxHashMap>>, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - is_enum: bool) -> DefIdForest + fn uninhabited_from( + &self, + visited: &mut FxHashMap>>, + 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) @@ -138,35 +220,10 @@ impl<'a, 'gcx, 'tcx> FieldDef { 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 FxHashMap>>, - 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 FxHashMap>>, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest + fn uninhabited_from( + &self, + visited: &mut FxHashMap>>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { match self.sty { TyAdt(def, substs) => { diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 871a23863ac21..6884302a9cd52 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -10,6 +10,9 @@ use hir::def_id::DefId; use ty::{self, Ty, TypeFoldable, Substs, TyCtxt}; +use ty::subst::{Kind, Subst}; +use traits; +use syntax::abi::Abi; use util::ppaux; use std::fmt; @@ -97,7 +100,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { impl<'a, 'b, 'tcx> Instance<'tcx> { pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> Instance<'tcx> { - assert!(substs.is_normalized_for_trans() && !substs.has_escaping_regions(), + assert!(!substs.has_escaping_regions(), "substs of instance {:?} not normalized for trans: {:?}", def_id, substs); Instance { def: InstanceDef::Item(def_id), substs: substs } @@ -111,4 +114,211 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { pub fn def_id(&self) -> DefId { self.def.def_id() } + + /// Resolve a (def_id, substs) pair to an (optional) instance -- most commonly, + /// this is used to find the precise code that will run for a trait method invocation, + /// if known. + /// + /// Returns `None` if we cannot resolve `Instance` to a specific instance. + /// For example, in a context like this, + /// + /// ``` + /// fn foo(t: T) { ... } + /// ``` + /// + /// trying to resolve `Debug::fmt` applied to `T` will yield `None`, because we do not + /// know what code ought to run. (Note that this setting is also affected by the + /// `RevealMode` in the parameter environment.) + /// + /// Presuming that coherence and type-check have succeeded, if this method is invoked + /// in a monomorphic context (i.e., like during trans), then it is guaranteed to return + /// `Some`. + pub fn resolve(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) -> Option> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); + let item = tcx.associated_item(def_id); + resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) + } else { + let ty = tcx.type_of(def_id); + let item_type = tcx.trans_apply_param_substs_env(substs, param_env, &ty); + + let def = match item_type.sty { + ty::TyFnDef(..) if { + let f = item_type.fn_sig(tcx); + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic + } => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def_id) + } + _ => { + if Some(def_id) == tcx.lang_items().drop_in_place_fn() { + let ty = substs.type_at(0); + if ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) { + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } else { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } + } + }; + Some(Instance { + def: def, + substs: substs + }) + }; + debug!("resolve(def_id={:?}, substs={:?}) = {:?}", def_id, substs, result); + result + } + + pub fn resolve_closure( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind) + -> Instance<'tcx> + { + let actual_kind = substs.closure_kind(def_id, tcx); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), + _ => Instance::new(def_id, substs.substs) + } + } +} + +fn resolve_associated_item<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + trait_item: &ty::AssociatedItem, + param_env: ty::ParamEnv<'tcx>, + trait_id: DefId, + rcvr_substs: &'tcx Substs<'tcx>, +) -> Option> { + let def_id = trait_item.def_id; + debug!("resolve_associated_item(trait_item={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, trait_id, rcvr_substs); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = tcx.trans_fulfill_obligation((param_env, ty::Binder(trait_ref))); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(impl_data) => { + let (def_id, substs) = traits::find_associated_item( + tcx, trait_item, rcvr_substs, &impl_data); + let substs = tcx.erase_regions(&substs); + Some(ty::Instance::new(def_id, substs)) + } + traits::VtableGenerator(closure_data) => { + Some(Instance { + def: ty::InstanceDef::Item(closure_data.closure_def_id), + substs: closure_data.substs.substs + }) + } + traits::VtableClosure(closure_data) => { + let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap(); + Some(Instance::resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs, + trait_closure_kind)) + } + traits::VtableFnPointer(ref data) => { + Some(Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs + }) + } + traits::VtableObject(ref data) => { + let index = tcx.get_vtable_index_of_object_method(data, def_id); + Some(Instance { + def: ty::InstanceDef::Virtual(def_id, index), + substs: rcvr_substs + }) + } + traits::VtableBuiltin(..) => { + if let Some(_) = tcx.lang_items().clone_trait() { + Some(Instance { + def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), + substs: rcvr_substs + }) + } else { + None + } + } + traits::VtableAutoImpl(..) | traits::VtableParam(..) => None + } +} + +fn needs_fn_once_adapter_shim<'a, 'tcx>(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. + 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. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + (ty::ClosureKind::FnMut, _) | + (ty::ClosureKind::FnOnce, _) => Err(()) + } +} + +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items().fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } } diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index a8ccb3e269ffc..0fecb5314bf44 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -151,9 +151,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - cur_path.push(self.def_key(cur_def) - .disambiguated_data.data.get_opt_name().unwrap_or_else(|| - Symbol::intern("").as_str())); + let mut cur_def_key = self.def_key(cur_def); + + // For a UnitStruct or TupleStruct we want the name of its parent rather than . + if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: cur_def.krate, + index: cur_def_key.parent.expect("DefPathData::StructCtor missing a parent"), + }; + + cur_def_key = self.def_key(parent); + } + + let data = cur_def_key.disambiguated_data.data; + let symbol = + data.get_opt_name().unwrap_or_else(|| Symbol::intern("").as_str()); + cur_path.push(symbol); + match visible_parent_map.get(&cur_def) { Some(&def) => cur_def = def, None => return false, @@ -218,7 +232,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // Always use types for non-local impls, where types are always // available, and filename/line-number is mostly uninteresting. - let use_types = !self.is_default_impl(impl_def_id) && (!impl_def_id.is_local() || { + let use_types = !self.is_auto_impl(impl_def_id) && (!impl_def_id.is_local() || { // Otherwise, use filename/line-number if forced. let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); !force_no_types @@ -281,6 +295,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + ty::TyForeign(did) => self.push_item_path(buffer, did), + ty::TyBool | ty::TyChar | ty::TyInt(_) | @@ -344,8 +360,9 @@ pub fn characteristic_def_id_of_type(ty: Ty) -> Option { .next(), ty::TyFnDef(def_id, _) | - ty::TyClosure(def_id, _) => Some(def_id), - ty::TyGenerator(def_id, _, _) => Some(def_id), + ty::TyClosure(def_id, _) | + ty::TyGenerator(def_id, _, _) | + ty::TyForeign(def_id) => Some(def_id), ty::TyBool | ty::TyChar | diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 1709f9ed2df1c..b2ed34cb91305 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -9,7 +9,6 @@ // except according to those terms. pub use self::Integer::*; -pub use self::Layout::*; pub use self::Primitive::*; use session::{self, DataTypeKind, Session}; @@ -21,10 +20,10 @@ use syntax_pos::DUMMY_SP; use std::cmp; use std::fmt; -use std::i64; +use std::i128; use std::iter; use std::mem; -use std::ops::Deref; +use std::ops::{Add, Sub, Mul, AddAssign, Deref, RangeInclusive}; use ich::StableHashingContext; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, @@ -203,6 +202,18 @@ impl TargetDataLayout { bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits) } } + + pub fn vector_align(&self, vec_size: Size) -> Align { + for &(size, align) in &self.vector_align { + if size == vec_size { + return align; + } + } + // Default to natural alignment, which is what LLVM does. + // That is, use the size, rounded up to a power of 2. + let align = vec_size.bytes().next_power_of_two(); + Align::from_bytes(align, align).unwrap() + } } pub trait HasDataLayout: Copy { @@ -215,12 +226,6 @@ impl<'a> HasDataLayout for &'a TargetDataLayout { } } -impl<'a, 'tcx> HasDataLayout for TyCtxt<'a, 'tcx, 'tcx> { - fn data_layout(&self) -> &TargetDataLayout { - &self.data_layout - } -} - /// Endianness of the target, which must match cfg(target-endian). #[derive(Copy, Clone)] pub enum Endian { @@ -236,7 +241,8 @@ pub struct Size { impl Size { pub fn from_bits(bits: u64) -> Size { - Size::from_bytes((bits + 7) / 8) + // Avoid potential overflow from `bits + 7`. + Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) } pub fn from_bytes(bytes: u64) -> Size { @@ -261,6 +267,11 @@ impl Size { Size::from_bytes((self.bytes() + mask) & !mask) } + pub fn is_abi_aligned(self, align: Align) -> bool { + let mask = align.abi() - 1; + self.bytes() & mask == 0 + } + pub fn checked_add(self, offset: Size, cx: C) -> Option { let dl = cx.data_layout(); @@ -278,8 +289,6 @@ impl Size { pub fn checked_mul(self, count: u64, cx: C) -> Option { let dl = cx.data_layout(); - // Each Size is less than dl.obj_size_bound(), so the sum is - // also less than 1 << 62 (and therefore can't overflow). match self.bytes().checked_mul(count) { Some(bytes) if bytes < dl.obj_size_bound() => { Some(Size::from_bytes(bytes)) @@ -289,6 +298,46 @@ impl Size { } } +// Panicking addition, subtraction and multiplication for convenience. +// Avoid during layout computation, return `LayoutError` instead. + +impl Add for Size { + type Output = Size; + fn add(self, other: Size) -> Size { + // Each Size is less than 1 << 61, so the sum is + // less than 1 << 62 (and therefore can't overflow). + Size::from_bytes(self.bytes() + other.bytes()) + } +} + +impl Sub for Size { + type Output = Size; + fn sub(self, other: Size) -> Size { + // Each Size is less than 1 << 61, so an underflow + // would result in a value larger than 1 << 61, + // which Size::from_bytes will catch for us. + Size::from_bytes(self.bytes() - other.bytes()) + } +} + +impl Mul for Size { + type Output = Size; + fn mul(self, count: u64) -> Size { + match self.bytes().checked_mul(count) { + Some(bytes) => Size::from_bytes(bytes), + None => { + bug!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count) + } + } + } +} + +impl AddAssign for Size { + fn add_assign(&mut self, other: Size) { + *self = *self + other; + } +} + /// Alignment of a type in bytes, both ABI-mandated and preferred. /// Each field is a power of two, giving the alignment a maximum /// value of 2^(2^8 - 1), which is limited by LLVM to a i32, with @@ -301,7 +350,8 @@ pub struct Align { impl Align { pub fn from_bits(abi: u64, pref: u64) -> Result { - Align::from_bytes((abi + 7) / 8, (pref + 7) / 8) + Align::from_bytes(Size::from_bits(abi).bytes(), + Size::from_bits(pref).bytes()) } pub fn from_bytes(abi: u64, pref: u64) -> Result { @@ -340,6 +390,14 @@ impl Align { 1 << self.pref } + pub fn abi_bits(self) -> u64 { + self.abi() * 8 + } + + pub fn pref_bits(self) -> u64 { + self.pref() * 8 + } + pub fn min(self, other: Align) -> Align { Align { abi: cmp::min(self.abi, other.abi), @@ -358,7 +416,6 @@ impl Align { /// Integers, also used for enum discriminants. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Integer { - I1, I8, I16, I32, @@ -366,10 +423,9 @@ pub enum Integer { I128, } -impl Integer { +impl<'a, 'tcx> Integer { pub fn size(&self) -> Size { match *self { - I1 => Size::from_bits(1), I8 => Size::from_bytes(1), I16 => Size::from_bytes(2), I32 => Size::from_bytes(4), @@ -382,7 +438,6 @@ impl Integer { let dl = cx.data_layout(); match *self { - I1 => dl.i1_align, I8 => dl.i8_align, I16 => dl.i16_align, I32 => dl.i32_align, @@ -391,16 +446,13 @@ impl Integer { } } - pub fn to_ty<'a, 'tcx>(&self, tcx: &TyCtxt<'a, 'tcx, 'tcx>, - signed: bool) -> Ty<'tcx> { + pub fn to_ty(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, signed: bool) -> Ty<'tcx> { match (*self, signed) { - (I1, false) => tcx.types.u8, (I8, false) => tcx.types.u8, (I16, false) => tcx.types.u16, (I32, false) => tcx.types.u32, (I64, false) => tcx.types.u64, (I128, false) => tcx.types.u128, - (I1, true) => tcx.types.i8, (I8, true) => tcx.types.i8, (I16, true) => tcx.types.i16, (I32, true) => tcx.types.i32, @@ -410,9 +462,8 @@ impl Integer { } /// Find the smallest Integer type which can represent the signed value. - pub fn fit_signed(x: i64) -> Integer { + pub fn fit_signed(x: i128) -> Integer { match x { - -0x0000_0000_0000_0001...0x0000_0000_0000_0000 => I1, -0x0000_0000_0000_0080...0x0000_0000_0000_007f => I8, -0x0000_0000_0000_8000...0x0000_0000_0000_7fff => I16, -0x0000_0000_8000_0000...0x0000_0000_7fff_ffff => I32, @@ -422,9 +473,8 @@ impl Integer { } /// Find the smallest Integer type which can represent the unsigned value. - pub fn fit_unsigned(x: u64) -> Integer { + pub fn fit_unsigned(x: u128) -> Integer { match x { - 0...0x0000_0000_0000_0001 => I1, 0...0x0000_0000_0000_00ff => I8, 0...0x0000_0000_0000_ffff => I16, 0...0x0000_0000_ffff_ffff => I32, @@ -438,8 +488,8 @@ impl Integer { let dl = cx.data_layout(); let wanted = align.abi(); - for &candidate in &[I8, I16, I32, I64] { - let ty = Int(candidate); + for &candidate in &[I8, I16, I32, I64, I128] { + let ty = Int(candidate, false); if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() { return Some(candidate); } @@ -465,19 +515,19 @@ impl Integer { /// Find the appropriate Integer type and signedness for the given /// signed discriminant range and #[repr] attribute. - /// N.B.: u64 values above i64::MAX will be treated as signed, but + /// N.B.: u128 values above i128::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - repr: &ReprOptions, - min: i64, - max: i64) - -> (Integer, bool) { + fn repr_discr(tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128) + -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there - // are any negative values, the only valid unsigned representation is u64 - // which can fit all i64 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64)); + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); let mut min_from_extern = None; @@ -518,22 +568,27 @@ impl Integer { /// Fundamental unit of memory access and layout. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Primitive { - Int(Integer), + /// The `bool` is the signedness of the `Integer` type. + /// + /// One would think we would not care about such details this low down, + /// but some ABIs are described in terms of C types and ISAs where the + /// integer arithmetic is done on {sign,zero}-extended registers, e.g. + /// a negative integer passed by zero-extension will appear positive in + /// the callee, and most operations on it will produce the wrong values. + Int(Integer, bool), F32, F64, Pointer } -impl Primitive { +impl<'a, 'tcx> Primitive { pub fn size(self, cx: C) -> Size { let dl = cx.data_layout(); match self { - Int(I1) | Int(I8) => Size::from_bits(8), - Int(I16) => Size::from_bits(16), - Int(I32) | F32 => Size::from_bits(32), - Int(I64) | F64 => Size::from_bits(64), - Int(I128) => Size::from_bits(128), + Int(i, _) => i.size(), + F32 => Size::from_bits(32), + F64 => Size::from_bits(64), Pointer => dl.pointer_size } } @@ -542,567 +597,228 @@ impl Primitive { let dl = cx.data_layout(); match self { - Int(I1) => dl.i1_align, - Int(I8) => dl.i8_align, - Int(I16) => dl.i16_align, - Int(I32) => dl.i32_align, - Int(I64) => dl.i64_align, - Int(I128) => dl.i128_align, + Int(i, _) => i.align(dl), F32 => dl.f32_align, F64 => dl.f64_align, Pointer => dl.pointer_align } } -} - -/// Path through fields of nested structures. -// FIXME(eddyb) use small vector optimization for the common case. -pub type FieldPath = Vec; - -/// A structure, a product type in ADT terms. -#[derive(PartialEq, Eq, Hash, Debug)] -pub struct Struct { - /// Maximum alignment of fields and repr alignment. - pub align: Align, - /// Primitive alignment of fields without repr alignment. - pub primitive_align: Align, - - /// If true, no alignment padding is used. - pub packed: bool, - - /// If true, the size is exact, otherwise it's only a lower bound. - pub sized: bool, - - /// Offsets for the first byte of each field, ordered to match the source definition order. - /// This vector does not go in increasing order. - /// FIXME(eddyb) use small vector optimization for the common case. - pub offsets: Vec, - - /// Maps source order field indices to memory order indices, depending how fields were permuted. - /// FIXME (camlorn) also consider small vector optimization here. - pub memory_index: Vec, - - pub min_size: Size, + pub fn to_ty(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + F32 => tcx.types.f32, + F64 => tcx.types.f64, + Pointer => tcx.mk_mut_ptr(tcx.mk_nil()), + } + } } -/// Info required to optimize struct layout. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] -enum StructKind { - /// A tuple, closure, or univariant which cannot be coerced to unsized. - AlwaysSizedUnivariant, - /// A univariant, the last field of which may be coerced to unsized. - MaybeUnsizedUnivariant, - /// A univariant, but part of an enum. - EnumVariant, +/// Information about one scalar component of a Rust type. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct Scalar { + pub value: Primitive, + + /// Inclusive wrap-around range of valid values, that is, if + /// min > max, it represents min..=u128::MAX followed by 0..=max. + // FIXME(eddyb) always use the shortest range, e.g. by finding + // the largest space between two consecutive valid values and + // taking everything else as the (shortest) valid range. + pub valid_range: RangeInclusive, } -impl<'a, 'tcx> Struct { - fn new(dl: &TargetDataLayout, - fields: &Vec<&'a Layout>, - repr: &ReprOptions, - kind: StructKind, - scapegoat: Ty<'tcx>) - -> Result> { - if repr.packed() && repr.align > 0 { - bug!("Struct cannot be packed and aligned"); - } - - let align = if repr.packed() { - dl.i8_align +impl Scalar { + pub fn is_bool(&self) -> bool { + if let Int(I8, _) = self.value { + self.valid_range == (0..=1) } else { - dl.aggregate_align - }; - - let mut ret = Struct { - align, - primitive_align: align, - packed: repr.packed(), - sized: true, - offsets: vec![], - memory_index: vec![], - min_size: Size::from_bytes(0), - }; - - // Anything with repr(C) or repr(packed) doesn't optimize. - // Neither do 1-member and 2-member structs. - // In addition, code in trans assume that 2-element structs can become pairs. - // It's easier to just short-circuit here. - let can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind) - && (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty(); - - let (optimize, sort_ascending) = match kind { - StructKind::AlwaysSizedUnivariant => (can_optimize, false), - StructKind::MaybeUnsizedUnivariant => (can_optimize, false), - StructKind::EnumVariant => { - assert!(fields.len() >= 1, "Enum variants must have discriminants."); - (can_optimize && fields[0].size(dl).bytes() == 1, true) - } - }; - - ret.offsets = vec![Size::from_bytes(0); fields.len()]; - let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); - - if optimize { - let start = if let StructKind::EnumVariant = kind { 1 } else { 0 }; - let end = if let StructKind::MaybeUnsizedUnivariant = kind { - fields.len() - 1 - } else { - fields.len() - }; - if end > start { - let optimizing = &mut inverse_memory_index[start..end]; - if sort_ascending { - optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi()); - } else { - optimizing.sort_by(| &a, &b | { - let a = fields[a as usize].align(dl).abi(); - let b = fields[b as usize].align(dl).abi(); - b.cmp(&a) - }); - } - } + false } + } +} - // inverse_memory_index holds field indices by increasing memory offset. - // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. - // We now write field offsets to the corresponding offset slot; - // field 5 with offset 0 puts 0 in offsets[5]. - // At the bottom of this function, we use inverse_memory_index to produce memory_index. +/// The first half of a fat pointer. +/// - For a trait object, this is the address of the box. +/// - For a slice, this is the base address. +pub const FAT_PTR_ADDR: usize = 0; - if let StructKind::EnumVariant = kind { - assert_eq!(inverse_memory_index[0], 0, - "Enum variant discriminants must have the lowest offset."); - } +/// The second half of a fat pointer. +/// - For a trait object, this is the address of the vtable. +/// - For a slice, this is the length. +pub const FAT_PTR_EXTRA: usize = 1; - let mut offset = Size::from_bytes(0); +/// Describes how the fields of a type are located in memory. +#[derive(PartialEq, Eq, Hash, Debug)] +pub enum FieldPlacement { + /// All fields start at no offset. The `usize` is the field count. + Union(usize), - for i in inverse_memory_index.iter() { - let field = fields[*i as usize]; - if !ret.sized { - bug!("Struct::new: field #{} of `{}` comes after unsized field", - ret.offsets.len(), scapegoat); - } + /// Array/vector-like placement, with all fields of identical types. + Array { + stride: Size, + count: u64 + }, - if field.is_unsized() { - ret.sized = false; - } + /// Struct-like placement, with precomputed offsets. + /// + /// Fields are guaranteed to not overlap, but note that gaps + /// before, between and after all the fields are NOT always + /// padding, and as such their contents may not be discarded. + /// For example, enum variants leave a gap at the start, + /// where the discriminant field in the enum layout goes. + Arbitrary { + /// Offsets for the first byte of each field, + /// ordered to match the source definition order. + /// This vector does not go in increasing order. + // FIXME(eddyb) use small vector optimization for the common case. + offsets: Vec, + + /// Maps source order field indices to memory order indices, + /// depending how fields were permuted. + // FIXME(camlorn) also consider small vector optimization here. + memory_index: Vec + } +} - // Invariant: offset < dl.obj_size_bound() <= 1<<61 - if !ret.packed { - let align = field.align(dl); - let primitive_align = field.primitive_align(dl); - ret.align = ret.align.max(align); - ret.primitive_align = ret.primitive_align.max(primitive_align); - offset = offset.abi_align(align); +impl FieldPlacement { + pub fn count(&self) -> usize { + match *self { + FieldPlacement::Union(count) => count, + FieldPlacement::Array { count, .. } => { + let usize_count = count as usize; + assert_eq!(usize_count as u64, count); + usize_count } - - debug!("Struct::new offset: {:?} field: {:?} {:?}", offset, field, field.size(dl)); - ret.offsets[*i as usize] = offset; - - offset = offset.checked_add(field.size(dl), dl) - .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; + FieldPlacement::Arbitrary { ref offsets, .. } => offsets.len() } + } - if repr.align > 0 { - let repr_align = repr.align as u64; - ret.align = ret.align.max(Align::from_bytes(repr_align, repr_align).unwrap()); - debug!("Struct::new repr_align: {:?}", repr_align); - } - - debug!("Struct::new min_size: {:?}", offset); - ret.min_size = offset; - - // As stated above, inverse_memory_index holds field indices by increasing offset. - // This makes it an already-sorted view of the offsets vec. - // To invert it, consider: - // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. - // Field 5 would be the first element, so memory_index is i: - // Note: if we didn't optimize, it's already right. - - if optimize { - ret.memory_index = vec![0; inverse_memory_index.len()]; - - for i in 0..inverse_memory_index.len() { - ret.memory_index[inverse_memory_index[i] as usize] = i as u32; + pub fn offset(&self, i: usize) -> Size { + match *self { + FieldPlacement::Union(_) => Size::from_bytes(0), + FieldPlacement::Array { stride, count } => { + let i = i as u64; + assert!(i < count); + stride * i } - } else { - ret.memory_index = inverse_memory_index; + FieldPlacement::Arbitrary { ref offsets, .. } => offsets[i] } - - Ok(ret) - } - - /// Get the size with trailing alignment padding. - pub fn stride(&self) -> Size { - self.min_size.abi_align(self.align) } - /// Determine whether a structure would be zero-sized, given its fields. - fn would_be_zero_sized(dl: &TargetDataLayout, fields: I) - -> Result> - where I: Iterator>> { - for field in fields { - let field = field?; - if field.is_unsized() || field.size(dl).bytes() > 0 { - return Ok(false); + pub fn memory_index(&self, i: usize) -> usize { + match *self { + FieldPlacement::Union(_) | + FieldPlacement::Array { .. } => i, + FieldPlacement::Arbitrary { ref memory_index, .. } => { + let r = memory_index[i]; + assert_eq!(r as usize as u32, r); + r as usize } } - Ok(true) } - /// Get indices of the tys that made this struct by increasing offset. + /// Get source indices of the fields by increasing offsets. #[inline] - pub fn field_index_by_increasing_offset<'b>(&'b self) -> impl iter::Iterator+'b { + pub fn index_by_increasing_offset<'a>(&'a self) -> impl iter::Iterator+'a { let mut inverse_small = [0u8; 64]; let mut inverse_big = vec![]; - let use_small = self.memory_index.len() <= inverse_small.len(); + let use_small = self.count() <= inverse_small.len(); // We have to write this logic twice in order to keep the array small. - if use_small { - for i in 0..self.memory_index.len() { - inverse_small[self.memory_index[i] as usize] = i as u8; - } - } else { - inverse_big = vec![0; self.memory_index.len()]; - for i in 0..self.memory_index.len() { - inverse_big[self.memory_index[i] as usize] = i as u32; - } - } - - (0..self.memory_index.len()).map(move |i| { - if use_small { inverse_small[i] as usize } - else { inverse_big[i] as usize } - }) - } - - /// Find the path leading to a non-zero leaf field, starting from - /// the given type and recursing through aggregates. - /// The tuple is `(path, source_path)`, - /// where `path` is in memory order and `source_path` in source order. - // FIXME(eddyb) track value ranges and traverse already optimized enums. - fn non_zero_field_in_type(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>) - -> Result, LayoutError<'tcx>> { - match (ty.layout(tcx, param_env)?, &ty.sty) { - (&Scalar { non_zero: true, .. }, _) | - (&CEnum { non_zero: true, .. }, _) => Ok(Some((vec![], vec![]))), - (&FatPointer { non_zero: true, .. }, _) => { - Ok(Some((vec![FAT_PTR_ADDR as u32], vec![FAT_PTR_ADDR as u32]))) - } - - // Is this the NonZero lang item wrapping a pointer or integer type? - (&Univariant { non_zero: true, .. }, &ty::TyAdt(def, substs)) => { - let fields = &def.struct_variant().fields; - assert_eq!(fields.len(), 1); - match *fields[0].ty(tcx, substs).layout(tcx, param_env)? { - // FIXME(eddyb) also allow floating-point types here. - Scalar { value: Int(_), non_zero: false } | - Scalar { value: Pointer, non_zero: false } => { - Ok(Some((vec![0], vec![0]))) - } - FatPointer { non_zero: false, .. } => { - let tmp = vec![FAT_PTR_ADDR as u32, 0]; - Ok(Some((tmp.clone(), tmp))) - } - _ => Ok(None) - } - } - - // Perhaps one of the fields of this struct is non-zero - // let's recurse and find out - (&Univariant { ref variant, .. }, &ty::TyAdt(def, substs)) if def.is_struct() => { - Struct::non_zero_field_paths( - tcx, - param_env, - def.struct_variant().fields.iter().map(|field| { - field.ty(tcx, substs) - }), - Some(&variant.memory_index[..])) - } - - // Perhaps one of the upvars of this closure is non-zero - (&Univariant { ref variant, .. }, &ty::TyClosure(def, substs)) => { - let upvar_tys = substs.upvar_tys(def, tcx); - Struct::non_zero_field_paths( - tcx, - param_env, - upvar_tys, - Some(&variant.memory_index[..])) - } - // Can we use one of the fields in this tuple? - (&Univariant { ref variant, .. }, &ty::TyTuple(tys, _)) => { - Struct::non_zero_field_paths( - tcx, - param_env, - tys.iter().cloned(), - Some(&variant.memory_index[..])) - } - - // Is this a fixed-size array of something non-zero - // with at least one element? - (_, &ty::TyArray(ety, mut count)) => { - if count.has_projections() { - count = tcx.normalize_associated_type_in_env(&count, param_env); - if count.has_projections() { - return Err(LayoutError::Unknown(ty)); - } + if let FieldPlacement::Arbitrary { ref memory_index, .. } = *self { + if use_small { + for i in 0..self.count() { + inverse_small[memory_index[i] as usize] = i as u8; } - if count.val.to_const_int().unwrap().to_u64().unwrap() != 0 { - Struct::non_zero_field_paths( - tcx, - param_env, - Some(ety).into_iter(), - None) - } else { - Ok(None) - } - } - - (_, &ty::TyProjection(_)) | (_, &ty::TyAnon(..)) => { - let normalized = tcx.normalize_associated_type_in_env(&ty, param_env); - if ty == normalized { - return Ok(None); + } else { + inverse_big = vec![0; self.count()]; + for i in 0..self.count() { + inverse_big[memory_index[i] as usize] = i as u32; } - return Struct::non_zero_field_in_type(tcx, param_env, normalized); } - - // Anything else is not a non-zero type. - _ => Ok(None) } - } - /// Find the path leading to a non-zero leaf field, starting from - /// the given set of fields and recursing through aggregates. - /// Returns Some((path, source_path)) on success. - /// `path` is translated to memory order. `source_path` is not. - fn non_zero_field_paths(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fields: I, - permutation: Option<&[u32]>) - -> Result, LayoutError<'tcx>> - where I: Iterator> { - for (i, ty) in fields.enumerate() { - let r = Struct::non_zero_field_in_type(tcx, param_env, ty)?; - if let Some((mut path, mut source_path)) = r { - source_path.push(i as u32); - let index = if let Some(p) = permutation { - p[i] as usize - } else { - i - }; - path.push(index as u32); - return Ok(Some((path, source_path))); + (0..self.count()).map(move |i| { + match *self { + FieldPlacement::Union(_) | + FieldPlacement::Array { .. } => i, + FieldPlacement::Arbitrary { .. } => { + if use_small { inverse_small[i] as usize } + else { inverse_big[i] as usize } + } } - } - Ok(None) - } - - pub fn over_align(&self) -> Option { - let align = self.align.abi(); - let primitive_align = self.primitive_align.abi(); - if align > primitive_align { - Some(align as u32) - } else { - None - } + }) } } -/// An untagged union. -#[derive(PartialEq, Eq, Hash, Debug)] -pub struct Union { - pub align: Align, - pub primitive_align: Align, - - pub min_size: Size, - - /// If true, no alignment padding is used. - pub packed: bool, -} - -impl<'a, 'tcx> Union { - fn new(dl: &TargetDataLayout, repr: &ReprOptions) -> Union { - if repr.packed() && repr.align > 0 { - bug!("Union cannot be packed and aligned"); - } - - let primitive_align = if repr.packed() { - dl.i8_align - } else { - dl.aggregate_align - }; - - let align = if repr.align > 0 { - let repr_align = repr.align as u64; - debug!("Union::new repr_align: {:?}", repr_align); - primitive_align.max(Align::from_bytes(repr_align, repr_align).unwrap()) - } else { - primitive_align - }; - - Union { - align, - primitive_align, - min_size: Size::from_bytes(0), - packed: repr.packed(), - } +/// Describes how values of the type are passed by target ABIs, +/// in terms of categories of C types there are ABI rules for. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum Abi { + Uninhabited, + Scalar(Scalar), + ScalarPair(Scalar, Scalar), + Vector, + Aggregate { + /// If true, the size is exact, otherwise it's only a lower bound. + sized: bool, + packed: bool } +} - /// Extend the Struct with more fields. - fn extend(&mut self, dl: &TargetDataLayout, - fields: I, - scapegoat: Ty<'tcx>) - -> Result<(), LayoutError<'tcx>> - where I: Iterator>> { - for (index, field) in fields.enumerate() { - let field = field?; - if field.is_unsized() { - bug!("Union::extend: field #{} of `{}` is unsized", - index, scapegoat); - } - - debug!("Union::extend field: {:?} {:?}", field, field.size(dl)); - - if !self.packed { - self.align = self.align.max(field.align(dl)); - self.primitive_align = self.primitive_align.max(field.primitive_align(dl)); - } - self.min_size = cmp::max(self.min_size, field.size(dl)); +impl Abi { + /// Returns true if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + match *self { + Abi::Uninhabited | + Abi::Scalar(_) | + Abi::ScalarPair(..) | + Abi::Vector => false, + Abi::Aggregate { sized, .. } => !sized } - - debug!("Union::extend min-size: {:?}", self.min_size); - - Ok(()) } - /// Get the size with trailing alignment padding. - pub fn stride(&self) -> Size { - self.min_size.abi_align(self.align) - } - - pub fn over_align(&self) -> Option { - let align = self.align.abi(); - let primitive_align = self.primitive_align.abi(); - if align > primitive_align { - Some(align as u32) - } else { - None + /// Returns true if the fields of the layout are packed. + pub fn is_packed(&self) -> bool { + match *self { + Abi::Uninhabited | + Abi::Scalar(_) | + Abi::ScalarPair(..) | + Abi::Vector => false, + Abi::Aggregate { packed, .. } => packed } } } -/// The first half of a fat pointer. -/// - For a trait object, this is the address of the box. -/// - For a slice, this is the base address. -pub const FAT_PTR_ADDR: usize = 0; - -/// The second half of a fat pointer. -/// - For a trait object, this is the address of the vtable. -/// - For a slice, this is the length. -pub const FAT_PTR_EXTRA: usize = 1; - -/// Type layout, from which size and alignment can be cheaply computed. -/// For ADTs, it also includes field placement and enum optimizations. -/// NOTE: Because Layout is interned, redundant information should be -/// kept to a minimum, e.g. it includes no sub-component Ty or Layout. -#[derive(Debug, PartialEq, Eq, Hash)] -pub enum Layout { - /// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr. - Scalar { - value: Primitive, - // If true, the value cannot represent a bit pattern of all zeroes. - non_zero: bool - }, - - /// SIMD vectors, from structs marked with #[repr(simd)]. - Vector { - element: Primitive, - count: u64 - }, - - /// TyArray, TySlice or TyStr. - Array { - /// If true, the size is exact, otherwise it's only a lower bound. - sized: bool, - align: Align, - primitive_align: Align, - element_size: Size, - count: u64 - }, - - /// TyRawPtr or TyRef with a !Sized pointee. - FatPointer { - metadata: Primitive, - /// If true, the pointer cannot be null. - non_zero: bool - }, - - // Remaining variants are all ADTs such as structs, enums or tuples. - - /// C-like enums; basically an integer. - CEnum { - discr: Integer, - signed: bool, - non_zero: bool, - /// Inclusive discriminant range. - /// If min > max, it represents min...u64::MAX followed by 0...max. - // FIXME(eddyb) always use the shortest range, e.g. by finding - // the largest space between two consecutive discriminants and - // taking everything else as the (shortest) discriminant range. - min: u64, - max: u64 - }, - - /// Single-case enums, and structs/tuples. - Univariant { - variant: Struct, - /// If true, the structure is NonZero. - // FIXME(eddyb) use a newtype Layout kind for this. - non_zero: bool - }, - - /// Untagged unions. - UntaggedUnion { - variants: Union, +#[derive(PartialEq, Eq, Hash, Debug)] +pub enum Variants { + /// Single enum variants, structs/tuples, unions, and all non-ADTs. + Single { + index: usize }, - /// General-case enums: for each case there is a struct, and they - /// all start with a field for the discriminant. - General { - discr: Integer, - variants: Vec, - size: Size, - align: Align, - primitive_align: Align, + /// General-case enums: for each case there is a struct, and they all have + /// all space reserved for the discriminant, and their first field starts + /// at a non-0 offset, after where the discriminant would go. + Tagged { + discr: Scalar, + variants: Vec, }, - /// Two cases distinguished by a nullable pointer: the case with discriminant - /// `nndiscr` must have single field which is known to be nonnull due to its type. - /// The other case is known to be zero sized. Hence we represent the enum - /// as simply a nullable pointer: if not null it indicates the `nndiscr` variant, - /// otherwise it indicates the other case. + /// Multiple cases distinguished by a niche (values invalid for a type): + /// the variant `dataful_variant` contains a niche at an arbitrary + /// offset (field 0 of the enum), which for a variant with discriminant + /// `d` is set to `(d - niche_variants.start).wrapping_add(niche_start)`. /// - /// For example, `std::option::Option` instantiated at a safe pointer type - /// is represented such that `None` is a null pointer and `Some` is the - /// identity function. - RawNullablePointer { - nndiscr: u64, - value: Primitive - }, - - /// Two cases distinguished by a nullable pointer: the case with discriminant - /// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th - /// field is known to be nonnull due to its type; if that field is null, then - /// it represents the other case, which is known to be zero sized. - StructWrappedNullablePointer { - nndiscr: u64, - nonnull: Struct, - /// N.B. There is a 0 at the start, for LLVM GEP through a pointer. - discrfield: FieldPath, - /// Like discrfield, but in source order. For debuginfo. - discrfield_source: FieldPath + /// For example, `Option<(usize, &T)>` is represented such that + /// `None` has a null pointer for the second tuple field, and + /// `Some` is the identity function (with a non-null reference). + NicheFilling { + dataful_variant: usize, + niche_variants: RangeInclusive, + niche: Scalar, + niche_start: u128, + variants: Vec, } } @@ -1125,67 +841,381 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { } } -impl<'a, 'tcx> Layout { - pub fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>) - -> Result<&'tcx Layout, LayoutError<'tcx>> { - let success = |layout| Ok(tcx.intern_layout(layout)); - let dl = &tcx.data_layout; - assert!(!ty.has_infer_types()); +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct LayoutDetails { + pub variants: Variants, + pub fields: FieldPlacement, + pub abi: Abi, + pub align: Align, + pub size: Size +} - let ptr_layout = |pointee: Ty<'tcx>| { - let non_zero = !ty.is_unsafe_ptr(); - let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env); - if pointee.is_sized(tcx, param_env, DUMMY_SP) { - Ok(Scalar { value: Pointer, non_zero: non_zero }) - } else { - let unsized_part = tcx.struct_tail(pointee); - let meta = match unsized_part.sty { - ty::TySlice(_) | ty::TyStr => { - Int(dl.ptr_sized_integer()) - } - ty::TyDynamic(..) => Pointer, - _ => return Err(LayoutError::Unknown(unsized_part)) - }; - Ok(FatPointer { metadata: meta, non_zero: non_zero }) +impl LayoutDetails { + fn scalar(cx: C, scalar: Scalar) -> Self { + let size = scalar.value.size(cx); + let align = scalar.value.align(cx); + LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Union(0), + abi: Abi::Scalar(scalar), + size, + align, + } + } + + fn uninhabited(field_count: usize) -> Self { + let align = Align::from_bytes(1, 1).unwrap(); + LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Union(field_count), + abi: Abi::Uninhabited, + align, + size: Size::from_bytes(0) + } + } +} + +fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) + -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> +{ + let (param_env, ty) = query.into_parts(); + + let rec_limit = tcx.sess.recursion_limit.get(); + let depth = tcx.layout_depth.get(); + if depth > rec_limit { + tcx.sess.fatal( + &format!("overflow representing the type `{}`", ty)); + } + + tcx.layout_depth.set(depth+1); + let layout = LayoutDetails::compute_uncached(tcx, param_env, ty); + tcx.layout_depth.set(depth); + + layout +} + +pub fn provide(providers: &mut ty::maps::Providers) { + *providers = ty::maps::Providers { + layout_raw, + ..*providers + }; +} + +impl<'a, 'tcx> LayoutDetails { + fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>) + -> Result<&'tcx Self, LayoutError<'tcx>> { + let cx = (tcx, param_env); + let dl = cx.data_layout(); + let scalar_unit = |value: Primitive| { + let bits = value.size(dl).bits(); + assert!(bits <= 128); + Scalar { + value, + valid_range: 0..=(!0 >> (128 - bits)) } }; - - let layout = match ty.sty { - // Basic scalars. - ty::TyBool => Scalar { value: Int(I1), non_zero: false }, - ty::TyChar => Scalar { value: Int(I32), non_zero: false }, - ty::TyInt(ity) => { - Scalar { - value: Int(Integer::from_attr(dl, attr::SignedInt(ity))), - non_zero: false - } + let scalar = |value: Primitive| { + tcx.intern_layout(LayoutDetails::scalar(cx, scalar_unit(value))) + }; + let scalar_pair = |a: Scalar, b: Scalar| { + let align = a.value.align(dl).max(b.value.align(dl)).max(dl.aggregate_align); + let b_offset = a.value.size(dl).abi_align(b.value.align(dl)); + let size = (b_offset + b.value.size(dl)).abi_align(align); + LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Arbitrary { + offsets: vec![Size::from_bytes(0), b_offset], + memory_index: vec![0, 1] + }, + abi: Abi::ScalarPair(a, b), + align, + size } - ty::TyUint(ity) => { - Scalar { - value: Int(Integer::from_attr(dl, attr::UnsignedInt(ity))), - non_zero: false - } + }; + + #[derive(Copy, Clone, Debug)] + enum StructKind { + /// A tuple, closure, or univariant which cannot be coerced to unsized. + AlwaysSized, + /// A univariant, the last field of which may be coerced to unsized. + MaybeUnsized, + /// A univariant, but with a prefix of an arbitrary size & alignment (e.g. enum tag). + Prefixed(Size, Align), + } + let univariant_uninterned = |fields: &[TyLayout], repr: &ReprOptions, kind| { + let packed = repr.packed(); + if packed && repr.align > 0 { + bug!("struct cannot be packed and aligned"); } - ty::TyFloat(FloatTy::F32) => Scalar { value: F32, non_zero: false }, - ty::TyFloat(FloatTy::F64) => Scalar { value: F64, non_zero: false }, - ty::TyFnPtr(_) => Scalar { value: Pointer, non_zero: true }, - // The never type. - ty::TyNever => Univariant { - variant: Struct::new(dl, &vec![], &ReprOptions::default(), - StructKind::AlwaysSizedUnivariant, ty)?, - non_zero: false - }, + let mut align = if packed { + dl.i8_align + } else { + dl.aggregate_align + }; - // Potentially-fat pointers. - ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - ptr_layout(pointee)? + let mut sized = true; + let mut offsets = vec![Size::from_bytes(0); fields.len()]; + let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); + + // Anything with repr(C) or repr(packed) doesn't optimize. + let mut optimize = (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty(); + if let StructKind::Prefixed(_, align) = kind { + optimize &= align.abi() == 1; } - ty::TyAdt(def, _) if def.is_box() => { - ptr_layout(ty.boxed_ty())? + + if optimize { + let end = if let StructKind::MaybeUnsized = kind { + fields.len() - 1 + } else { + fields.len() + }; + let optimizing = &mut inverse_memory_index[..end]; + match kind { + StructKind::AlwaysSized | + StructKind::MaybeUnsized => { + optimizing.sort_by_key(|&x| { + // Place ZSTs first to avoid "interesting offsets", + // especially with only one or two non-ZST fields. + let f = &fields[x as usize]; + (!f.is_zst(), cmp::Reverse(f.align.abi())) + }) + } + StructKind::Prefixed(..) => { + optimizing.sort_by_key(|&x| fields[x as usize].align.abi()); + } + } + } + + // inverse_memory_index holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // We now write field offsets to the corresponding offset slot; + // field 5 with offset 0 puts 0 in offsets[5]. + // At the bottom of this function, we use inverse_memory_index to produce memory_index. + + let mut offset = Size::from_bytes(0); + + if let StructKind::Prefixed(prefix_size, prefix_align) = kind { + if !packed { + align = align.max(prefix_align); + } + offset = prefix_size.abi_align(prefix_align); + } + + for &i in &inverse_memory_index { + let field = fields[i as usize]; + if !sized { + bug!("univariant: field #{} of `{}` comes after unsized field", + offsets.len(), ty); + } + + if field.abi == Abi::Uninhabited { + return Ok(LayoutDetails::uninhabited(fields.len())); + } + + if field.is_unsized() { + sized = false; + } + + // Invariant: offset < dl.obj_size_bound() <= 1<<61 + if !packed { + offset = offset.abi_align(field.align); + align = align.max(field.align); + } + + debug!("univariant offset: {:?} field: {:#?}", offset, field); + offsets[i as usize] = offset; + + offset = offset.checked_add(field.size, dl) + .ok_or(LayoutError::SizeOverflow(ty))?; + } + + if repr.align > 0 { + let repr_align = repr.align as u64; + align = align.max(Align::from_bytes(repr_align, repr_align).unwrap()); + debug!("univariant repr_align: {:?}", repr_align); + } + + debug!("univariant min_size: {:?}", offset); + let min_size = offset; + + // As stated above, inverse_memory_index holds field indices by increasing offset. + // This makes it an already-sorted view of the offsets vec. + // To invert it, consider: + // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. + // Field 5 would be the first element, so memory_index is i: + // Note: if we didn't optimize, it's already right. + + let mut memory_index; + if optimize { + memory_index = vec![0; inverse_memory_index.len()]; + + for i in 0..inverse_memory_index.len() { + memory_index[inverse_memory_index[i] as usize] = i as u32; + } + } else { + memory_index = inverse_memory_index; + } + + let size = min_size.abi_align(align); + let mut abi = Abi::Aggregate { + sized, + packed + }; + + // Unpack newtype ABIs and find scalar pairs. + if sized && size.bytes() > 0 { + // All other fields must be ZSTs, and we need them to all start at 0. + let mut zst_offsets = + offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); + if zst_offsets.all(|(_, o)| o.bytes() == 0) { + let mut non_zst_fields = + fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); + + match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { + // We have exactly one non-ZST field. + (Some((i, field)), None, None) => { + // Field fills the struct and it has a scalar or scalar pair ABI. + if offsets[i].bytes() == 0 && + align.abi() == field.align.abi() && + size == field.size { + match field.abi { + // For plain scalars we can't unpack newtypes + // for `#[repr(C)]`, as that affects C ABIs. + Abi::Scalar(_) if optimize => { + abi = field.abi.clone(); + } + // But scalar pairs are Rust-specific and get + // treated as aggregates by C ABIs anyway. + Abi::ScalarPair(..) => { + abi = field.abi.clone(); + } + _ => {} + } + } + } + + // Two non-ZST fields, and they're both scalars. + (Some((i, &TyLayout { + details: &LayoutDetails { abi: Abi::Scalar(ref a), .. }, .. + })), Some((j, &TyLayout { + details: &LayoutDetails { abi: Abi::Scalar(ref b), .. }, .. + })), None) => { + // Order by the memory placement, not source order. + let ((i, a), (j, b)) = if offsets[i] < offsets[j] { + ((i, a), (j, b)) + } else { + ((j, b), (i, a)) + }; + let pair = scalar_pair(a.clone(), b.clone()); + let pair_offsets = match pair.fields { + FieldPlacement::Arbitrary { + ref offsets, + ref memory_index + } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!() + }; + if offsets[i] == pair_offsets[0] && + offsets[j] == pair_offsets[1] && + align == pair.align && + size == pair.size { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + + _ => {} + } + } + } + + Ok(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Arbitrary { + offsets, + memory_index + }, + abi, + align, + size + }) + }; + let univariant = |fields: &[TyLayout], repr: &ReprOptions, kind| { + Ok(tcx.intern_layout(univariant_uninterned(fields, repr, kind)?)) + }; + assert!(!ty.has_infer_types()); + + Ok(match ty.sty { + // Basic scalars. + ty::TyBool => { + tcx.intern_layout(LayoutDetails::scalar(cx, Scalar { + value: Int(I8, false), + valid_range: 0..=1 + })) + } + ty::TyChar => { + tcx.intern_layout(LayoutDetails::scalar(cx, Scalar { + value: Int(I32, false), + valid_range: 0..=0x10FFFF + })) + } + ty::TyInt(ity) => { + scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)) + } + ty::TyUint(ity) => { + scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)) + } + ty::TyFloat(FloatTy::F32) => scalar(F32), + ty::TyFloat(FloatTy::F64) => scalar(F64), + ty::TyFnPtr(_) => { + let mut ptr = scalar_unit(Pointer); + ptr.valid_range.start = 1; + tcx.intern_layout(LayoutDetails::scalar(cx, ptr)) + } + + // The never type. + ty::TyNever => { + tcx.intern_layout(LayoutDetails::uninhabited(0)) + } + + // Potentially-fat pointers. + ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let mut data_ptr = scalar_unit(Pointer); + if !ty.is_unsafe_ptr() { + data_ptr.valid_range.start = 1; + } + + let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env); + if pointee.is_sized(tcx, param_env, DUMMY_SP) { + return Ok(tcx.intern_layout(LayoutDetails::scalar(cx, data_ptr))); + } + + let unsized_part = tcx.struct_tail(pointee); + let metadata = match unsized_part.sty { + ty::TyForeign(..) => { + return Ok(tcx.intern_layout(LayoutDetails::scalar(cx, data_ptr))); + } + ty::TySlice(_) | ty::TyStr => { + scalar_unit(Int(dl.ptr_sized_integer(), false)) + } + ty::TyDynamic(..) => { + let mut vtable = scalar_unit(Pointer); + vtable.valid_range.start = 1; + vtable + } + _ => return Err(LayoutError::Unknown(unsized_part)) + }; + + // Effectively a (ptr, meta) tuple. + tcx.intern_layout(scalar_pair(data_ptr, metadata)) } // Arrays and slices. @@ -1197,284 +1227,364 @@ impl<'a, 'tcx> Layout { } } - let element = element.layout(tcx, param_env)?; - let element_size = element.size(dl); + let element = cx.layout_of(element)?; let count = count.val.to_const_int().unwrap().to_u64().unwrap(); - if element_size.checked_mul(count, dl).is_none() { - return Err(LayoutError::SizeOverflow(ty)); - } - Array { - sized: true, - align: element.align(dl), - primitive_align: element.primitive_align(dl), - element_size, - count, - } + let size = element.size.checked_mul(count, dl) + .ok_or(LayoutError::SizeOverflow(ty))?; + + tcx.intern_layout(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Array { + stride: element.size, + count + }, + abi: Abi::Aggregate { + sized: true, + packed: false + }, + align: element.align, + size + }) } ty::TySlice(element) => { - let element = element.layout(tcx, param_env)?; - Array { - sized: false, - align: element.align(dl), - primitive_align: element.primitive_align(dl), - element_size: element.size(dl), - count: 0 - } + let element = cx.layout_of(element)?; + tcx.intern_layout(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Array { + stride: element.size, + count: 0 + }, + abi: Abi::Aggregate { + sized: false, + packed: false + }, + align: element.align, + size: Size::from_bytes(0) + }) } ty::TyStr => { - Array { - sized: false, + tcx.intern_layout(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Array { + stride: Size::from_bytes(1), + count: 0 + }, + abi: Abi::Aggregate { + sized: false, + packed: false + }, align: dl.i8_align, - primitive_align: dl.i8_align, - element_size: Size::from_bytes(1), - count: 0 - } + size: Size::from_bytes(0) + }) } // Odd unit types. ty::TyFnDef(..) => { - Univariant { - variant: Struct::new(dl, &vec![], - &ReprOptions::default(), StructKind::AlwaysSizedUnivariant, ty)?, - non_zero: false + univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)? + } + ty::TyDynamic(..) | ty::TyForeign(..) => { + let mut unit = univariant_uninterned(&[], &ReprOptions::default(), + StructKind::AlwaysSized)?; + match unit.abi { + Abi::Aggregate { ref mut sized, .. } => *sized = false, + _ => bug!() } - } - ty::TyDynamic(..) => { - let mut unit = Struct::new(dl, &vec![], &ReprOptions::default(), - StructKind::AlwaysSizedUnivariant, ty)?; - unit.sized = false; - Univariant { variant: unit, non_zero: false } + tcx.intern_layout(unit) } // Tuples, generators and closures. ty::TyGenerator(def_id, ref substs, _) => { let tys = substs.field_tys(def_id, tcx); - let st = Struct::new(dl, - &tys.map(|ty| ty.layout(tcx, param_env)) - .collect::, _>>()?, + univariant(&tys.map(|ty| cx.layout_of(ty)).collect::, _>>()?, &ReprOptions::default(), - StructKind::AlwaysSizedUnivariant, ty)?; - Univariant { variant: st, non_zero: false } + StructKind::AlwaysSized)? } ty::TyClosure(def_id, ref substs) => { let tys = substs.upvar_tys(def_id, tcx); - let st = Struct::new(dl, - &tys.map(|ty| ty.layout(tcx, param_env)) - .collect::, _>>()?, + univariant(&tys.map(|ty| cx.layout_of(ty)).collect::, _>>()?, &ReprOptions::default(), - StructKind::AlwaysSizedUnivariant, ty)?; - Univariant { variant: st, non_zero: false } + StructKind::AlwaysSized)? } ty::TyTuple(tys, _) => { let kind = if tys.len() == 0 { - StructKind::AlwaysSizedUnivariant + StructKind::AlwaysSized } else { - StructKind::MaybeUnsizedUnivariant + StructKind::MaybeUnsized }; - let st = Struct::new(dl, - &tys.iter().map(|ty| ty.layout(tcx, param_env)) - .collect::, _>>()?, - &ReprOptions::default(), kind, ty)?; - Univariant { variant: st, non_zero: false } + univariant(&tys.iter().map(|ty| cx.layout_of(ty)).collect::, _>>()?, + &ReprOptions::default(), kind)? } // SIMD vector types. ty::TyAdt(def, ..) if def.repr.simd() => { - let element = ty.simd_type(tcx); - match *element.layout(tcx, param_env)? { - Scalar { value, .. } => { - return success(Vector { - element: value, - count: ty.simd_size(tcx) as u64 - }); - } + let count = ty.simd_size(tcx) as u64; + let element = cx.layout_of(ty.simd_type(tcx))?; + match element.abi { + Abi::Scalar(_) => {} _ => { tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \ a non-machine element type `{}`", - ty, element)); + ty, element.ty)); } } + let size = element.size.checked_mul(count, dl) + .ok_or(LayoutError::SizeOverflow(ty))?; + let align = dl.vector_align(size); + let size = size.abi_align(align); + + tcx.intern_layout(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Array { + stride: element.size, + count + }, + abi: Abi::Vector, + size, + align, + }) } // ADTs. ty::TyAdt(def, substs) => { - if def.variants.is_empty() { - // Uninhabitable; represent as unit - // (Typechecking will reject discriminant-sizing attrs.) + // Cache the field layouts. + let variants = def.variants.iter().map(|v| { + v.fields.iter().map(|field| { + cx.layout_of(field.ty(tcx, substs)) + }).collect::, _>>() + }).collect::, _>>()?; - return success(Univariant { - variant: Struct::new(dl, &vec![], - &def.repr, StructKind::AlwaysSizedUnivariant, ty)?, - non_zero: false + let (inh_first, inh_second) = { + let mut inh_variants = (0..variants.len()).filter(|&v| { + variants[v].iter().all(|f| f.abi != Abi::Uninhabited) }); + (inh_variants.next(), inh_variants.next()) + }; + if inh_first.is_none() { + // Uninhabited because it has no variants, or only uninhabited ones. + return Ok(tcx.intern_layout(LayoutDetails::uninhabited(0))); } - if def.is_enum() && def.variants.iter().all(|v| v.fields.is_empty()) { - // All bodies empty -> intlike - let (mut min, mut max, mut non_zero) = (i64::max_value(), - i64::min_value(), - true); - for discr in def.discriminants(tcx) { - let x = discr.to_u128_unchecked() as i64; - if x == 0 { non_zero = false; } - if x < min { min = x; } - if x > max { max = x; } + if def.is_union() { + let packed = def.repr.packed(); + if packed && def.repr.align > 0 { + bug!("Union cannot be packed and aligned"); } - // FIXME: should handle i128? signed-value based impl is weird and hard to - // grok. - let (discr, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); - return success(CEnum { - discr, - signed, - non_zero, - // FIXME: should be u128? - min: min as u64, - max: max as u64 - }); + let mut align = if def.repr.packed() { + dl.i8_align + } else { + dl.aggregate_align + }; + + if def.repr.align > 0 { + let repr_align = def.repr.align as u64; + align = align.max( + Align::from_bytes(repr_align, repr_align).unwrap()); + } + + let mut size = Size::from_bytes(0); + for field in &variants[0] { + assert!(!field.is_unsized()); + + if !packed { + align = align.max(field.align); + } + size = cmp::max(size, field.size); + } + + return Ok(tcx.intern_layout(LayoutDetails { + variants: Variants::Single { index: 0 }, + fields: FieldPlacement::Union(variants[0].len()), + abi: Abi::Aggregate { + sized: true, + packed + }, + align, + size: size.abi_align(align) + })); } - if !def.is_enum() || (def.variants.len() == 1 && - !def.repr.inhibit_enum_layout_opt()) { - // Struct, or union, or univariant enum equivalent to a struct. + let is_struct = !def.is_enum() || + // Only one variant is inhabited. + (inh_second.is_none() && + // Representation optimizations are allowed. + !def.repr.inhibit_enum_layout_opt() && + // Inhabited variant either has data ... + (!variants[inh_first.unwrap()].is_empty() || + // ... or there other, uninhabited, variants. + variants.len() > 1)); + if is_struct { + // Struct, or univariant enum equivalent to a struct. // (Typechecking will reject discriminant-sizing attrs.) - let kind = if def.is_enum() || def.variants[0].fields.len() == 0{ - StructKind::AlwaysSizedUnivariant + let v = inh_first.unwrap(); + let kind = if def.is_enum() || variants[v].len() == 0 { + StructKind::AlwaysSized } else { let param_env = tcx.param_env(def.did); - let fields = &def.variants[0].fields; - let last_field = &fields[fields.len()-1]; + let last_field = def.variants[v].fields.last().unwrap(); let always_sized = tcx.type_of(last_field.did) .is_sized(tcx, param_env, DUMMY_SP); - if !always_sized { StructKind::MaybeUnsizedUnivariant } - else { StructKind::AlwaysSizedUnivariant } + if !always_sized { StructKind::MaybeUnsized } + else { StructKind::AlwaysSized } }; - let fields = def.variants[0].fields.iter().map(|field| { - field.ty(tcx, substs).layout(tcx, param_env) - }).collect::, _>>()?; - let layout = if def.is_union() { - let mut un = Union::new(dl, &def.repr); - un.extend(dl, fields.iter().map(|&f| Ok(f)), ty)?; - UntaggedUnion { variants: un } - } else { - let st = Struct::new(dl, &fields, &def.repr, - kind, ty)?; - let non_zero = Some(def.did) == tcx.lang_items().non_zero(); - Univariant { variant: st, non_zero: non_zero } - }; - return success(layout); - } - - // Since there's at least one - // non-empty body, explicit discriminants should have - // been rejected by a checker before this point. - for (i, v) in def.variants.iter().enumerate() { - if v.discr != ty::VariantDiscr::Relative(i) { - bug!("non-C-like enum {} with specified discriminants", - tcx.item_path_str(def.did)); + let mut st = univariant_uninterned(&variants[v], &def.repr, kind)?; + st.variants = Variants::Single { index: v }; + // Exclude 0 from the range of a newtype ABI NonZero. + if Some(def.did) == cx.tcx().lang_items().non_zero() { + match st.abi { + Abi::Scalar(ref mut scalar) | + Abi::ScalarPair(ref mut scalar, _) => { + if scalar.valid_range.start == 0 { + scalar.valid_range.start = 1; + } + } + _ => {} + } } + return Ok(tcx.intern_layout(st)); } - // Cache the substituted and normalized variant field types. - let variants = def.variants.iter().map(|v| { - v.fields.iter().map(|field| field.ty(tcx, substs)).collect::>() - }).collect::>(); - - if variants.len() == 2 && !def.repr.inhibit_enum_layout_opt() { - // Nullable pointer optimization - for discr in 0..2 { - let other_fields = variants[1 - discr].iter().map(|ty| { - ty.layout(tcx, param_env) - }); - if !Struct::would_be_zero_sized(dl, other_fields)? { - continue; + let no_explicit_discriminants = def.variants.iter().enumerate() + .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i)); + + // Niche-filling enum optimization. + if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { + let mut dataful_variant = None; + let mut niche_variants = usize::max_value()..=0; + + // Find one non-ZST variant. + 'variants: for (v, fields) in variants.iter().enumerate() { + for f in fields { + if f.abi == Abi::Uninhabited { + continue 'variants; + } + if !f.is_zst() { + if dataful_variant.is_none() { + dataful_variant = Some(v); + continue 'variants; + } else { + dataful_variant = None; + break 'variants; + } + } + } + if niche_variants.start > v { + niche_variants.start = v; } - let paths = Struct::non_zero_field_paths(tcx, - param_env, - variants[discr].iter().cloned(), - None)?; - let (mut path, mut path_source) = if let Some(p) = paths { p } - else { continue }; - - // FIXME(eddyb) should take advantage of a newtype. - if path == &[0] && variants[discr].len() == 1 { - let value = match *variants[discr][0].layout(tcx, param_env)? { - Scalar { value, .. } => value, - CEnum { discr, .. } => Int(discr), - _ => bug!("Layout::compute: `{}`'s non-zero \ - `{}` field not scalar?!", - ty, variants[discr][0]) + niche_variants.end = v; + } + + if niche_variants.start > niche_variants.end { + dataful_variant = None; + } + + if let Some(i) = dataful_variant { + let count = (niche_variants.end - niche_variants.start + 1) as u128; + for (field_index, field) in variants[i].iter().enumerate() { + let (offset, niche, niche_start) = + match field.find_niche(cx, count)? { + Some(niche) => niche, + None => continue + }; + let st = variants.iter().enumerate().map(|(j, v)| { + let mut st = univariant_uninterned(v, + &def.repr, StructKind::AlwaysSized)?; + st.variants = Variants::Single { index: j }; + Ok(st) + }).collect::, _>>()?; + + let offset = st[i].fields.offset(field_index) + offset; + let LayoutDetails { size, mut align, .. } = st[i]; + + let mut niche_align = niche.value.align(dl); + let abi = if offset.bytes() == 0 && niche.value.size(dl) == size { + Abi::Scalar(niche.clone()) + } else { + let mut packed = st[i].abi.is_packed(); + if offset.abi_align(niche_align) != offset { + packed = true; + niche_align = dl.i8_align; + } + Abi::Aggregate { + sized: true, + packed + } }; - return success(RawNullablePointer { - nndiscr: discr as u64, - value, - }); + align = align.max(niche_align); + + return Ok(tcx.intern_layout(LayoutDetails { + variants: Variants::NicheFilling { + dataful_variant: i, + niche_variants, + niche, + niche_start, + variants: st, + }, + fields: FieldPlacement::Arbitrary { + offsets: vec![offset], + memory_index: vec![0] + }, + abi, + size, + align, + })); } + } + } - let st = Struct::new(dl, - &variants[discr].iter().map(|ty| ty.layout(tcx, param_env)) - .collect::, _>>()?, - &def.repr, StructKind::AlwaysSizedUnivariant, ty)?; - - // We have to fix the last element of path here. - let mut i = *path.last().unwrap(); - i = st.memory_index[i as usize]; - *path.last_mut().unwrap() = i; - path.push(0); // For GEP through a pointer. - path.reverse(); - path_source.push(0); - path_source.reverse(); - - return success(StructWrappedNullablePointer { - nndiscr: discr as u64, - nonnull: st, - discrfield: path, - discrfield_source: path_source - }); + let (mut min, mut max) = (i128::max_value(), i128::min_value()); + for (i, discr) in def.discriminants(tcx).enumerate() { + if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) { + continue; } + let x = discr.to_u128_unchecked() as i128; + if x < min { min = x; } + if x > max { max = x; } } + assert!(min <= max, "discriminant range is {}...{}", min, max); + let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); - // The general case. - let discr_max = (variants.len() - 1) as i64; - assert!(discr_max >= 0); - let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max); let mut align = dl.aggregate_align; - let mut primitive_align = dl.aggregate_align; let mut size = Size::from_bytes(0); // We're interested in the smallest alignment, so start large. let mut start_align = Align::from_bytes(256, 256).unwrap(); + assert_eq!(Integer::for_abi_align(dl, start_align), None); + + // repr(C) on an enum tells us to make a (tag, union) layout, + // so we need to grow the prefix alignment to be at least + // the alignment of the union. (This value is used both for + // determining the alignment of the overall enum, and the + // determining the alignment of the payload after the tag.) + let mut prefix_align = min_ity.align(dl); + if def.repr.c() { + for fields in &variants { + for field in fields { + prefix_align = prefix_align.max(field.align); + } + } + } - // Create the set of structs that represent each variant - // Use the minimum integer type we figured out above - let discr = Scalar { value: Int(min_ity), non_zero: false }; - let mut variants = variants.into_iter().map(|fields| { - let mut fields = fields.into_iter().map(|field| { - field.layout(tcx, param_env) - }).collect::, _>>()?; - fields.insert(0, &discr); - let st = Struct::new(dl, - &fields, - &def.repr, StructKind::EnumVariant, ty)?; + // Create the set of structs that represent each variant. + let mut variants = variants.into_iter().enumerate().map(|(i, field_layouts)| { + let mut st = univariant_uninterned(&field_layouts, + &def.repr, StructKind::Prefixed(min_ity.size(), prefix_align))?; + st.variants = Variants::Single { index: i }; // Find the first field we can't move later // to make room for a larger discriminant. - // It is important to skip the first field. - for i in st.field_index_by_increasing_offset().skip(1) { - let field = fields[i]; - let field_align = field.align(dl); - if field.size(dl).bytes() != 0 || field_align.abi() != 1 { - start_align = start_align.min(field_align); + for field in st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) { + if !field.is_zst() || field.align.abi() != 1 { + start_align = start_align.min(field.align); break; } } - size = cmp::max(size, st.min_size); + size = cmp::max(size, st.size); align = align.max(st.align); - primitive_align = primitive_align.max(st.primitive_align); Ok(st) }).collect::, _>>()?; @@ -1520,30 +1630,55 @@ impl<'a, 'tcx> Layout { ity = min_ity; } else { // Patch up the variants' first few fields. - let old_ity_size = Int(min_ity).size(dl); - let new_ity_size = Int(ity).size(dl); + let old_ity_size = min_ity.size(); + let new_ity_size = ity.size(); for variant in &mut variants { - for i in variant.offsets.iter_mut() { - // The first field is the discrimminant, at offset 0. - // These aren't in order, and we need to skip it. - if *i <= old_ity_size && *i > Size::from_bytes(0) { - *i = new_ity_size; - } + if variant.abi == Abi::Uninhabited { + continue; } - // We might be making the struct larger. - if variant.min_size <= old_ity_size { - variant.min_size = new_ity_size; + match variant.fields { + FieldPlacement::Arbitrary { ref mut offsets, .. } => { + for i in offsets { + if *i <= old_ity_size { + assert_eq!(*i, old_ity_size); + *i = new_ity_size; + } + } + // We might be making the struct larger. + if variant.size <= old_ity_size { + variant.size = new_ity_size; + } + } + _ => bug!() } } } - General { - discr: ity, - variants, - size, + let discr = Scalar { + value: Int(ity, signed), + valid_range: (min as u128)..=(max as u128) + }; + let abi = if discr.value.size(dl) == size { + Abi::Scalar(discr.clone()) + } else { + Abi::Aggregate { + sized: true, + packed: false + } + }; + tcx.intern_layout(LayoutDetails { + variants: Variants::Tagged { + discr, + variants + }, + // FIXME(eddyb): using `FieldPlacement::Arbitrary` here results + // in lost optimizations, specifically around allocations, see + // `test/codegen/{alloc-optimisation,vec-optimizes-away}.rs`. + fields: FieldPlacement::Union(1), + abi, align, - primitive_align, - } + size + }) } // Types with no meaningful known layout. @@ -1552,204 +1687,24 @@ impl<'a, 'tcx> Layout { if ty == normalized { return Err(LayoutError::Unknown(ty)); } - return normalized.layout(tcx, param_env); + tcx.layout_raw(param_env.and(normalized))? } ty::TyParam(_) => { return Err(LayoutError::Unknown(ty)); } ty::TyInfer(_) | ty::TyError => { - bug!("Layout::compute: unexpected type `{}`", ty) - } - }; - - success(layout) - } - - /// Returns true if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - match *self { - Scalar {..} | Vector {..} | FatPointer {..} | - CEnum {..} | UntaggedUnion {..} | General {..} | - RawNullablePointer {..} | - StructWrappedNullablePointer {..} => false, - - Array { sized, .. } | - Univariant { variant: Struct { sized, .. }, .. } => !sized - } - } - - pub fn size(&self, cx: C) -> Size { - let dl = cx.data_layout(); - - match *self { - Scalar { value, .. } | RawNullablePointer { value, .. } => { - value.size(dl) - } - - Vector { element, count } => { - let element_size = element.size(dl); - let vec_size = match element_size.checked_mul(count, dl) { - Some(size) => size, - None => bug!("Layout::size({:?}): {} * {} overflowed", - self, element_size.bytes(), count) - }; - vec_size.abi_align(self.align(dl)) - } - - Array { element_size, count, .. } => { - match element_size.checked_mul(count, dl) { - Some(size) => size, - None => bug!("Layout::size({:?}): {} * {} overflowed", - self, element_size.bytes(), count) - } - } - - FatPointer { metadata, .. } => { - // Effectively a (ptr, meta) tuple. - Pointer.size(dl).abi_align(metadata.align(dl)) - .checked_add(metadata.size(dl), dl).unwrap() - .abi_align(self.align(dl)) - } - - CEnum { discr, .. } => Int(discr).size(dl), - General { size, .. } => size, - UntaggedUnion { ref variants } => variants.stride(), - - Univariant { ref variant, .. } | - StructWrappedNullablePointer { nonnull: ref variant, .. } => { - variant.stride() - } - } - } - - pub fn align(&self, cx: C) -> Align { - let dl = cx.data_layout(); - - match *self { - Scalar { value, .. } | RawNullablePointer { value, .. } => { - value.align(dl) - } - - Vector { element, count } => { - let elem_size = element.size(dl); - let vec_size = match elem_size.checked_mul(count, dl) { - Some(size) => size, - None => bug!("Layout::align({:?}): {} * {} overflowed", - self, elem_size.bytes(), count) - }; - for &(size, align) in &dl.vector_align { - if size == vec_size { - return align; - } - } - // Default to natural alignment, which is what LLVM does. - // That is, use the size, rounded up to a power of 2. - let align = vec_size.bytes().next_power_of_two(); - Align::from_bytes(align, align).unwrap() - } - - FatPointer { metadata, .. } => { - // Effectively a (ptr, meta) tuple. - Pointer.align(dl).max(metadata.align(dl)) - } - - CEnum { discr, .. } => Int(discr).align(dl), - Array { align, .. } | General { align, .. } => align, - UntaggedUnion { ref variants } => variants.align, - - Univariant { ref variant, .. } | - StructWrappedNullablePointer { nonnull: ref variant, .. } => { - variant.align - } - } - } - - /// Returns alignment before repr alignment is applied - pub fn primitive_align(&self, dl: &TargetDataLayout) -> Align { - match *self { - Array { primitive_align, .. } | General { primitive_align, .. } => primitive_align, - Univariant { ref variant, .. } | - StructWrappedNullablePointer { nonnull: ref variant, .. } => { - variant.primitive_align - }, - - _ => self.align(dl) - } - } - - /// Returns repr alignment if it is greater than the primitive alignment. - pub fn over_align(&self, dl: &TargetDataLayout) -> Option { - let align = self.align(dl); - let primitive_align = self.primitive_align(dl); - if align.abi() > primitive_align.abi() { - Some(align.abi() as u32) - } else { - None - } - } - - pub fn field_offset(&self, - cx: C, - i: usize, - variant_index: Option) - -> Size { - let dl = cx.data_layout(); - - match *self { - Scalar { .. } | - CEnum { .. } | - UntaggedUnion { .. } | - RawNullablePointer { .. } => { - Size::from_bytes(0) - } - - Vector { element, count } => { - let element_size = element.size(dl); - let i = i as u64; - assert!(i < count); - Size::from_bytes(element_size.bytes() * count) - } - - Array { element_size, count, .. } => { - let i = i as u64; - assert!(i < count); - Size::from_bytes(element_size.bytes() * count) - } - - FatPointer { metadata, .. } => { - // Effectively a (ptr, meta) tuple. - assert!(i < 2); - if i == 0 { - Size::from_bytes(0) - } else { - Pointer.size(dl).abi_align(metadata.align(dl)) - } - } - - Univariant { ref variant, .. } => variant.offsets[i], - - General { ref variants, .. } => { - let v = variant_index.expect("variant index required"); - variants[v].offsets[i + 1] - } - - StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { - if Some(nndiscr as usize) == variant_index { - nonnull.offsets[i] - } else { - Size::from_bytes(0) - } + bug!("LayoutDetails::compute: unexpected type `{}`", ty) } - } + }) } /// This is invoked by the `layout_raw` query to record the final /// layout of each type. #[inline] - pub fn record_layout_for_printing(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - layout: &Layout) { + fn record_layout_for_printing(tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + layout: TyLayout<'tcx>) { // If we are running with `-Zprint-type-sizes`, record layouts for // dumping later. Ignore layouts that are done with non-empty // environments or non-monomorphic layouts, as the user only wants @@ -1769,24 +1724,23 @@ impl<'a, 'tcx> Layout { fn record_layout_for_printing_outlined(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, - layout: &Layout) { + layout: TyLayout<'tcx>) { + let cx = (tcx, param_env); // (delay format until we actually need it) let record = |kind, opt_discr_size, variants| { let type_desc = format!("{:?}", ty); - let overall_size = layout.size(tcx); - let align = layout.align(tcx); tcx.sess.code_stats.borrow_mut().record_type_size(kind, type_desc, - align, - overall_size, + layout.align, + layout.size, opt_discr_size, variants); }; - let (adt_def, substs) = match ty.sty { - ty::TyAdt(ref adt_def, substs) => { + let adt_def = match ty.sty { + ty::TyAdt(ref adt_def, _) => { debug!("print-type-size t: `{:?}` process adt", ty); - (adt_def, substs) + adt_def } ty::TyClosure(..) => { @@ -1803,106 +1757,61 @@ impl<'a, 'tcx> Layout { let adt_kind = adt_def.adt_kind(); - let build_field_info = |(field_name, field_ty): (ast::Name, Ty<'tcx>), offset: &Size| { - let layout = field_ty.layout(tcx, param_env); - match layout { - Err(_) => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty), - Ok(field_layout) => { - session::FieldInfo { - name: field_name.to_string(), - offset: offset.bytes(), - size: field_layout.size(tcx).bytes(), - align: field_layout.align(tcx).abi(), + let build_variant_info = |n: Option, + flds: &[ast::Name], + layout: TyLayout<'tcx>| { + let mut min_size = Size::from_bytes(0); + let field_info: Vec<_> = flds.iter().enumerate().map(|(i, &name)| { + match layout.field(cx, i) { + Err(err) => { + bug!("no layout found for field {}: `{:?}`", name, err); + } + Ok(field_layout) => { + let offset = layout.fields.offset(i); + let field_end = offset + field_layout.size; + if min_size < field_end { + min_size = field_end; + } + session::FieldInfo { + name: name.to_string(), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi(), + } } } - } - }; - - let build_primitive_info = |name: ast::Name, value: &Primitive| { - session::VariantInfo { - name: Some(name.to_string()), - kind: session::SizeKind::Exact, - align: value.align(tcx).abi(), - size: value.size(tcx).bytes(), - fields: vec![], - } - }; - - enum Fields<'a> { - WithDiscrim(&'a Struct), - NoDiscrim(&'a Struct), - } - - let build_variant_info = |n: Option, - flds: &[(ast::Name, Ty<'tcx>)], - layout: Fields| { - let (s, field_offsets) = match layout { - Fields::WithDiscrim(s) => (s, &s.offsets[1..]), - Fields::NoDiscrim(s) => (s, &s.offsets[0..]), - }; - let field_info: Vec<_> = - flds.iter() - .zip(field_offsets.iter()) - .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset)) - .collect(); + }).collect(); session::VariantInfo { name: n.map(|n|n.to_string()), - kind: if s.sized { + kind: if layout.is_unsized() { + session::SizeKind::Min + } else { session::SizeKind::Exact + }, + align: layout.align.abi(), + size: if min_size.bytes() == 0 { + layout.size.bytes() } else { - session::SizeKind::Min + min_size.bytes() }, - align: s.align.abi(), - size: s.min_size.bytes(), fields: field_info, } }; - match *layout { - Layout::StructWrappedNullablePointer { nonnull: ref variant_layout, - nndiscr, - discrfield: _, - discrfield_source: _ } => { - debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}", - ty, nndiscr, variant_layout); - let variant_def = &adt_def.variants[nndiscr as usize]; - let fields: Vec<_> = - variant_def.fields.iter() - .map(|field_def| (field_def.name, field_def.ty(tcx, substs))) - .collect(); - record(adt_kind.into(), - None, - vec![build_variant_info(Some(variant_def.name), - &fields, - Fields::NoDiscrim(variant_layout))]); - } - Layout::RawNullablePointer { nndiscr, value } => { - debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}", - ty, nndiscr, value); - let variant_def = &adt_def.variants[nndiscr as usize]; - record(adt_kind.into(), None, - vec![build_primitive_info(variant_def.name, &value)]); - } - Layout::Univariant { variant: ref variant_layout, non_zero: _ } => { - let variant_names = || { - adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::>() - }; - debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}", - ty, variant_layout, variant_names()); - assert!(adt_def.variants.len() <= 1, - "univariant with variants {:?}", variant_names()); - if adt_def.variants.len() == 1 { - let variant_def = &adt_def.variants[0]; + match layout.variants { + Variants::Single { index } => { + debug!("print-type-size `{:#?}` variant {}", + layout, adt_def.variants[index].name); + if !adt_def.variants.is_empty() { + let variant_def = &adt_def.variants[index]; let fields: Vec<_> = - variant_def.fields.iter() - .map(|f| (f.name, f.ty(tcx, substs))) - .collect(); + variant_def.fields.iter().map(|f| f.name).collect(); record(adt_kind.into(), None, vec![build_variant_info(Some(variant_def.name), &fields, - Fields::NoDiscrim(variant_layout))]); + layout)]); } else { // (This case arises for *empty* enums; so give it // zero variants.) @@ -1910,54 +1819,23 @@ impl<'a, 'tcx> Layout { } } - Layout::General { ref variants, discr, .. } => { - debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}", - ty, adt_def.variants.len(), variants.len(), variants); - let variant_infos: Vec<_> = - adt_def.variants.iter() - .zip(variants.iter()) - .map(|(variant_def, variant_layout)| { - let fields: Vec<_> = - variant_def.fields - .iter() - .map(|f| (f.name, f.ty(tcx, substs))) - .collect(); - build_variant_info(Some(variant_def.name), - &fields, - Fields::WithDiscrim(variant_layout)) - }) - .collect(); - record(adt_kind.into(), Some(discr.size()), variant_infos); - } - - Layout::UntaggedUnion { ref variants } => { - debug!("print-type-size t: `{:?}` adt union variants {:?}", - ty, variants); - // layout does not currently store info about each - // variant... - record(adt_kind.into(), None, Vec::new()); - } - - Layout::CEnum { discr, .. } => { - debug!("print-type-size t: `{:?}` adt c-like enum", ty); + Variants::NicheFilling { .. } | + Variants::Tagged { .. } => { + debug!("print-type-size `{:#?}` adt general variants def {}", + ty, adt_def.variants.len()); let variant_infos: Vec<_> = - adt_def.variants.iter() - .map(|variant_def| { - build_primitive_info(variant_def.name, - &Primitive::Int(discr)) - }) - .collect(); - record(adt_kind.into(), Some(discr.size()), variant_infos); - } - - // other cases provide little interesting (i.e. adjustable - // via representation tweaks) size info beyond total size. - Layout::Scalar { .. } | - Layout::Vector { .. } | - Layout::Array { .. } | - Layout::FatPointer { .. } => { - debug!("print-type-size t: `{:?}` adt other", ty); - record(adt_kind.into(), None, Vec::new()) + adt_def.variants.iter().enumerate().map(|(i, variant_def)| { + let fields: Vec<_> = + variant_def.fields.iter().map(|f| f.name).collect(); + build_variant_info(Some(variant_def.name), + &fields, + layout.for_variant(cx, i)) + }) + .collect(); + record(adt_kind.into(), match layout.variants { + Variants::Tagged { ref discr, .. } => Some(discr.value.size(tcx)), + _ => None + }, variant_infos); } } } @@ -1991,39 +1869,32 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> { assert!(!ty.has_infer_types()); // First try computing a static layout. - let err = match ty.layout(tcx, param_env) { + let err = match (tcx, param_env).layout_of(ty) { Ok(layout) => { - return Ok(SizeSkeleton::Known(layout.size(tcx))); + return Ok(SizeSkeleton::Known(layout.size)); } Err(err) => err }; - let ptr_skeleton = |pointee: Ty<'tcx>| { - let non_zero = !ty.is_unsafe_ptr(); - let tail = tcx.struct_tail(pointee); - match tail.sty { - ty::TyParam(_) | ty::TyProjection(_) => { - assert!(tail.has_param_types() || tail.has_self_ty()); - Ok(SizeSkeleton::Pointer { - non_zero, - tail: tcx.erase_regions(&tail) - }) - } - _ => { - bug!("SizeSkeleton::compute({}): layout errored ({}), yet \ - tail `{}` is not a type parameter or a projection", - ty, err, tail) - } - } - }; - match ty.sty { ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - ptr_skeleton(pointee) - } - ty::TyAdt(def, _) if def.is_box() => { - ptr_skeleton(ty.boxed_ty()) + let non_zero = !ty.is_unsafe_ptr(); + let tail = tcx.struct_tail(pointee); + match tail.sty { + ty::TyParam(_) | ty::TyProjection(_) => { + assert!(tail.has_param_types() || tail.has_self_ty()); + Ok(SizeSkeleton::Pointer { + non_zero, + tail: tcx.erase_regions(&tail) + }) + } + _ => { + bug!("SizeSkeleton::compute({}): layout errored ({}), yet \ + tail `{}` is not a type parameter or a projection", + ty, err, tail) + } + } } ty::TyAdt(def, substs) => { @@ -2108,142 +1979,184 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> { } } -/// A pair of a type and its layout. Implements various -/// type traversal APIs (e.g. recursing into fields). +/// The details of the layout of a type, alongside the type itself. +/// Provides various type traversal APIs (e.g. recursing into fields). +/// +/// Note that the details are NOT guaranteed to always be identical +/// to those obtained from `layout_of(ty)`, as we need to produce +/// layouts for which Rust types do not exist, such as enum variants +/// or synthetic fields of enums (i.e. discriminants) and fat pointers. #[derive(Copy, Clone, Debug)] pub struct TyLayout<'tcx> { pub ty: Ty<'tcx>, - pub layout: &'tcx Layout, - pub variant_index: Option, + details: &'tcx LayoutDetails } impl<'tcx> Deref for TyLayout<'tcx> { - type Target = Layout; - fn deref(&self) -> &Layout { - self.layout + type Target = &'tcx LayoutDetails; + fn deref(&self) -> &&'tcx LayoutDetails { + &self.details } } -pub trait LayoutTyper<'tcx>: HasDataLayout { - type TyLayout; - +pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx>; - fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout; - fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx>; } -/// Combines a tcx with the parameter environment so that you can -/// compute layout operations. -#[derive(Copy, Clone)] -pub struct LayoutCx<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, +impl<'a, 'gcx, 'tcx> HasDataLayout for TyCtxt<'a, 'gcx, 'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + &self.data_layout + } } -impl<'a, 'tcx> LayoutCx<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - LayoutCx { tcx, param_env } +impl<'a, 'gcx, 'tcx> HasTyCtxt<'gcx> for TyCtxt<'a, 'gcx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'gcx> { + self.global_tcx() } } -impl<'a, 'tcx> HasDataLayout for LayoutCx<'a, 'tcx> { +impl<'a, 'gcx, 'tcx, T: Copy> HasDataLayout for (TyCtxt<'a, 'gcx, 'tcx>, T) { fn data_layout(&self) -> &TargetDataLayout { - &self.tcx.data_layout + self.0.data_layout() } } -impl<'a, 'tcx> LayoutTyper<'tcx> for LayoutCx<'a, 'tcx> { - type TyLayout = Result, LayoutError<'tcx>>; +impl<'a, 'gcx, 'tcx, T: Copy> HasTyCtxt<'gcx> for (TyCtxt<'a, 'gcx, 'tcx>, T) { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'gcx> { + self.0.tcx() + } +} - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx +pub trait MaybeResult { + fn from_ok(x: T) -> Self; + fn map_same T>(self, f: F) -> Self; +} + +impl MaybeResult for T { + fn from_ok(x: T) -> Self { + x + } + fn map_same T>(self, f: F) -> Self { + f(self) } +} +impl MaybeResult for Result { + fn from_ok(x: T) -> Self { + Ok(x) + } + fn map_same T>(self, f: F) -> Self { + self.map(f) + } +} + +pub trait LayoutOf { + type TyLayout; + + fn layout_of(self, ty: T) -> Self::TyLayout; +} + +impl<'a, 'tcx> LayoutOf> for (TyCtxt<'a, 'tcx, 'tcx>, ty::ParamEnv<'tcx>) { + type TyLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { - let ty = self.normalize_projections(ty); + let (tcx, param_env) = self; - Ok(TyLayout { + let ty = tcx.normalize_associated_type_in_env(&ty, param_env.reveal_all()); + let details = tcx.layout_raw(param_env.reveal_all().and(ty))?; + let layout = TyLayout { ty, - layout: ty.layout(self.tcx, self.param_env)?, - variant_index: None - }) - } + details + }; + + // NB: This recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admitedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + LayoutDetails::record_layout_for_printing(tcx, ty, param_env, layout); - fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.tcx.normalize_associated_type_in_env(&ty, self.param_env) + Ok(layout) } } -impl<'a, 'tcx> TyLayout<'tcx> { - pub fn for_variant(&self, variant_index: usize) -> Self { - TyLayout { - variant_index: Some(variant_index), - ..*self - } - } +impl<'a, 'tcx> LayoutOf> for (ty::maps::TyCtxtAt<'a, 'tcx, 'tcx>, + ty::ParamEnv<'tcx>) { + type TyLayout = Result, LayoutError<'tcx>>; - pub fn field_offset(&self, cx: C, i: usize) -> Size { - self.layout.field_offset(cx, i, self.variant_index) - } + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + let (tcx_at, param_env) = self; - pub fn field_count(&self) -> usize { - // Handle enum/union through the type rather than Layout. - if let ty::TyAdt(def, _) = self.ty.sty { - let v = self.variant_index.unwrap_or(0); - if def.variants.is_empty() { - assert_eq!(v, 0); - return 0; - } else { - return def.variants[v].fields.len(); - } - } + let ty = tcx_at.tcx.normalize_associated_type_in_env(&ty, param_env.reveal_all()); + let details = tcx_at.layout_raw(param_env.reveal_all().and(ty))?; + let layout = TyLayout { + ty, + details + }; - match *self.layout { - Scalar { .. } => { - bug!("TyLayout::field_count({:?}): not applicable", self) - } + // NB: This recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admitedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + LayoutDetails::record_layout_for_printing(tcx_at.tcx, ty, param_env, layout); - // Handled above (the TyAdt case). - CEnum { .. } | - General { .. } | - UntaggedUnion { .. } | - RawNullablePointer { .. } | - StructWrappedNullablePointer { .. } => bug!(), + Ok(layout) + } +} - FatPointer { .. } => 2, +impl<'a, 'tcx> TyLayout<'tcx> { + pub fn for_variant(&self, cx: C, variant_index: usize) -> Self + where C: LayoutOf> + HasTyCtxt<'tcx>, + C::TyLayout: MaybeResult> + { + let details = match self.variants { + Variants::Single { index } if index == variant_index => self.details, + + Variants::Single { index } => { + // Deny calling for_variant more than once for non-Single enums. + cx.layout_of(self.ty).map_same(|layout| { + assert_eq!(layout.variants, Variants::Single { index }); + layout + }); + + let fields = match self.ty.sty { + ty::TyAdt(def, _) => def.variants[variant_index].fields.len(), + _ => bug!() + }; + let mut details = LayoutDetails::uninhabited(fields); + details.variants = Variants::Single { index: variant_index }; + cx.tcx().intern_layout(details) + } - Vector { count, .. } | - Array { count, .. } => { - let usize_count = count as usize; - assert_eq!(usize_count as u64, count); - usize_count + Variants::NicheFilling { ref variants, .. } | + Variants::Tagged { ref variants, .. } => { + &variants[variant_index] } + }; + + assert_eq!(details.variants, Variants::Single { index: variant_index }); - Univariant { ref variant, .. } => variant.offsets.len(), + TyLayout { + ty: self.ty, + details } } - pub fn field_type>(&self, cx: C, i: usize) -> Ty<'tcx> { + pub fn field(&self, cx: C, i: usize) -> C::TyLayout + where C: LayoutOf> + HasTyCtxt<'tcx>, + C::TyLayout: MaybeResult> + { let tcx = cx.tcx(); - - let ptr_field_type = |pointee: Ty<'tcx>| { - assert!(i < 2); - let slice = |element: Ty<'tcx>| { - if i == 0 { - tcx.mk_mut_ptr(element) - } else { - tcx.types.usize - } - }; - match tcx.struct_tail(pointee).sty { - ty::TySlice(element) => slice(element), - ty::TyStr => slice(tcx.types.u8), - ty::TyDynamic(..) => tcx.mk_mut_ptr(tcx.mk_nil()), - _ => bug!("TyLayout::field_type({:?}): not applicable", self) - } - }; - - match self.ty.sty { + cx.layout_of(match self.ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | @@ -2252,17 +2165,43 @@ impl<'a, 'tcx> TyLayout<'tcx> { ty::TyFnPtr(_) | ty::TyNever | ty::TyFnDef(..) | - ty::TyDynamic(..) => { + ty::TyDynamic(..) | + ty::TyForeign(..) => { bug!("TyLayout::field_type({:?}): not applicable", self) } // Potentially-fat pointers. ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - ptr_field_type(pointee) - } - ty::TyAdt(def, _) if def.is_box() => { - ptr_field_type(self.ty.boxed_ty()) + assert!(i < 2); + + // Reuse the fat *T type as its own thin pointer data field. + // This provides information about e.g. DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldPlacement` is checked by users. + if i == 0 { + let nil = tcx.mk_nil(); + let ptr_ty = if self.ty.is_unsafe_ptr() { + tcx.mk_mut_ptr(nil) + } else { + tcx.mk_mut_ref(tcx.types.re_static, nil) + }; + return cx.layout_of(ptr_ty).map_same(|mut ptr_layout| { + ptr_layout.ty = self.ty; + ptr_layout + }); + } + + match tcx.struct_tail(pointee).sty { + ty::TySlice(_) | + ty::TyStr => tcx.types.usize, + ty::TyDynamic(..) => { + // FIXME(eddyb) use an usize/fn() array with + // the correct number of vtables slots. + tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil()) + } + _ => bug!("TyLayout::field_type({:?}): not applicable", self) + } } // Arrays and slices. @@ -2288,94 +2227,232 @@ impl<'a, 'tcx> TyLayout<'tcx> { // ADTs. ty::TyAdt(def, substs) => { - def.variants[self.variant_index.unwrap_or(0)].fields[i].ty(tcx, substs) + match self.variants { + Variants::Single { index } => { + def.variants[index].fields[i].ty(tcx, substs) + } + + // Discriminant field for enums (where applicable). + Variants::Tagged { ref discr, .. } | + Variants::NicheFilling { niche: ref discr, .. } => { + assert_eq!(i, 0); + let layout = LayoutDetails::scalar(tcx, discr.clone()); + return MaybeResult::from_ok(TyLayout { + details: tcx.intern_layout(layout), + ty: discr.value.to_ty(tcx) + }); + } + } } ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) | ty::TyInfer(_) | ty::TyError => { bug!("TyLayout::field_type: unexpected type `{}`", self.ty) } + }) + } + + /// Returns true if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + self.abi.is_unsized() + } + + /// Returns true if the fields of the layout are packed. + pub fn is_packed(&self) -> bool { + self.abi.is_packed() + } + + /// Returns true if the type is a ZST and not unsized. + pub fn is_zst(&self) -> bool { + match self.abi { + Abi::Uninhabited => true, + Abi::Scalar(_) | Abi::ScalarPair(..) => false, + Abi::Vector => self.size.bytes() == 0, + Abi::Aggregate { sized, .. } => sized && self.size.bytes() == 0 } } - pub fn field>(&self, - cx: C, - i: usize) - -> C::TyLayout { - cx.layout_of(cx.normalize_projections(self.field_type(cx, i))) + pub fn size_and_align(&self) -> (Size, Align) { + (self.size, self.align) + } + + /// Find the offset of a niche leaf field, starting from + /// the given type and recursing through aggregates, which + /// has at least `count` consecutive invalid values. + /// The tuple is `(offset, scalar, niche_value)`. + // FIXME(eddyb) traverse already optimized enums. + fn find_niche(&self, cx: C, count: u128) + -> Result, LayoutError<'tcx>> + where C: LayoutOf, TyLayout = Result>> + + HasTyCtxt<'tcx> + { + let scalar_component = |scalar: &Scalar, offset| { + let Scalar { value, valid_range: ref v } = *scalar; + + let bits = value.size(cx).bits(); + assert!(bits <= 128); + let max_value = !0u128 >> (128 - bits); + + // Find out how many values are outside the valid range. + let niches = if v.start <= v.end { + v.start + (max_value - v.end) + } else { + v.start - v.end - 1 + }; + + // Give up if we can't fit `count` consecutive niches. + if count > niches { + return None; + } + + let niche_start = v.end.wrapping_add(1) & max_value; + let niche_end = v.end.wrapping_add(count) & max_value; + Some((offset, Scalar { + value, + valid_range: v.start..=niche_end + }, niche_start)) + }; + + match self.abi { + Abi::Scalar(ref scalar) => { + return Ok(scalar_component(scalar, Size::from_bytes(0))); + } + Abi::ScalarPair(ref a, ref b) => { + return Ok(scalar_component(a, Size::from_bytes(0)).or_else(|| { + scalar_component(b, a.value.size(cx).abi_align(b.value.align(cx))) + })); + } + _ => {} + } + + // Perhaps one of the fields is non-zero, let's recurse and find out. + if let FieldPlacement::Union(_) = self.fields { + // Only Rust enums have safe-to-inspect fields + // (a discriminant), other unions are unsafe. + if let Variants::Single { .. } = self.variants { + return Ok(None); + } + } + if let FieldPlacement::Array { .. } = self.fields { + if self.fields.count() > 0 { + return self.field(cx, 0)?.find_niche(cx, count); + } + } + for i in 0..self.fields.count() { + let r = self.field(cx, i)?.find_niche(cx, count)?; + if let Some((offset, scalar, niche_value)) = r { + let offset = self.fields.offset(i) + offset; + return Ok(Some((offset, scalar, niche_value))); + } + } + Ok(None) } } -impl<'gcx> HashStable> for Layout -{ +impl<'gcx> HashStable> for Variants { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { - use ty::layout::Layout::*; + use ty::layout::Variants::*; mem::discriminant(self).hash_stable(hcx, hasher); match *self { - Scalar { value, non_zero } => { - value.hash_stable(hcx, hasher); - non_zero.hash_stable(hcx, hasher); - } - Vector { element, count } => { - element.hash_stable(hcx, hasher); - count.hash_stable(hcx, hasher); - } - Array { sized, align, primitive_align, element_size, count } => { - sized.hash_stable(hcx, hasher); - align.hash_stable(hcx, hasher); - primitive_align.hash_stable(hcx, hasher); - element_size.hash_stable(hcx, hasher); - count.hash_stable(hcx, hasher); + Single { index } => { + index.hash_stable(hcx, hasher); } - FatPointer { ref metadata, non_zero } => { - metadata.hash_stable(hcx, hasher); - non_zero.hash_stable(hcx, hasher); - } - CEnum { discr, signed, non_zero, min, max } => { + Tagged { + ref discr, + ref variants, + } => { discr.hash_stable(hcx, hasher); - signed.hash_stable(hcx, hasher); - non_zero.hash_stable(hcx, hasher); - min.hash_stable(hcx, hasher); - max.hash_stable(hcx, hasher); - } - Univariant { ref variant, non_zero } => { - variant.hash_stable(hcx, hasher); - non_zero.hash_stable(hcx, hasher); - } - UntaggedUnion { ref variants } => { variants.hash_stable(hcx, hasher); } - General { discr, ref variants, size, align, primitive_align } => { - discr.hash_stable(hcx, hasher); + NicheFilling { + dataful_variant, + niche_variants: RangeInclusive { start, end }, + ref niche, + niche_start, + ref variants, + } => { + dataful_variant.hash_stable(hcx, hasher); + start.hash_stable(hcx, hasher); + end.hash_stable(hcx, hasher); + niche.hash_stable(hcx, hasher); + niche_start.hash_stable(hcx, hasher); variants.hash_stable(hcx, hasher); - size.hash_stable(hcx, hasher); - align.hash_stable(hcx, hasher); - primitive_align.hash_stable(hcx, hasher); } - RawNullablePointer { nndiscr, ref value } => { - nndiscr.hash_stable(hcx, hasher); + } + } +} + +impl<'gcx> HashStable> for FieldPlacement { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use ty::layout::FieldPlacement::*; + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + Union(count) => { + count.hash_stable(hcx, hasher); + } + Array { count, stride } => { + count.hash_stable(hcx, hasher); + stride.hash_stable(hcx, hasher); + } + Arbitrary { ref offsets, ref memory_index } => { + offsets.hash_stable(hcx, hasher); + memory_index.hash_stable(hcx, hasher); + } + } + } +} + +impl<'gcx> HashStable> for Abi { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use ty::layout::Abi::*; + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + Uninhabited => {} + Scalar(ref value) => { value.hash_stable(hcx, hasher); } - StructWrappedNullablePointer { - nndiscr, - ref nonnull, - ref discrfield, - ref discrfield_source - } => { - nndiscr.hash_stable(hcx, hasher); - nonnull.hash_stable(hcx, hasher); - discrfield.hash_stable(hcx, hasher); - discrfield_source.hash_stable(hcx, hasher); + ScalarPair(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + Vector => {} + Aggregate { packed, sized } => { + packed.hash_stable(hcx, hasher); + sized.hash_stable(hcx, hasher); } } } } +impl<'gcx> HashStable> for Scalar { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let Scalar { value, valid_range: RangeInclusive { start, end } } = *self; + value.hash_stable(hcx, hasher); + start.hash_stable(hcx, hasher); + end.hash_stable(hcx, hasher); + } +} + +impl_stable_hash_for!(struct ::ty::layout::LayoutDetails { + variants, + fields, + abi, + size, + align +}); + impl_stable_hash_for!(enum ::ty::layout::Integer { - I1, I8, I16, I32, @@ -2384,7 +2461,7 @@ impl_stable_hash_for!(enum ::ty::layout::Integer { }); impl_stable_hash_for!(enum ::ty::layout::Primitive { - Int(integer), + Int(integer, signed), F32, F64, Pointer @@ -2413,20 +2490,3 @@ impl<'gcx> HashStable> for LayoutError<'gcx> } } } - -impl_stable_hash_for!(struct ::ty::layout::Struct { - align, - primitive_align, - packed, - sized, - offsets, - memory_index, - min_size -}); - -impl_stable_hash_for!(struct ::ty::layout::Union { - align, - primitive_align, - min_size, - packed -}); diff --git a/src/librustc/ty/maps/README.md b/src/librustc/ty/maps/README.md index 8abc68d431a53..8207c18e67791 100644 --- a/src/librustc/ty/maps/README.md +++ b/src/librustc/ty/maps/README.md @@ -169,7 +169,7 @@ That is, they take an `&mut Providers` and mutate it in place. Usually we use the formulation above just because it looks nice, but you could as well do `providers.type_of = type_of`, which would be equivalent. (Here, `type_of` would be a top-level function, defined as we saw -before.) So, if we wanted to have add a provider for some other query, +before.) So, if we want to add a provider for some other query, let's call it `fubar`, into the crate above, we might modify the `provide()` function like so: @@ -185,7 +185,7 @@ pub fn provide(providers: &mut Providers) { fn fubar<'cx, 'tcx>(tcx: TyCtxt<'cx, 'tcx>, key: DefId) -> Fubar<'tcx> { .. } ``` -NB. Most of the `rustc_*` crate only provide **local +NB. Most of the `rustc_*` crates only provide **local providers**. Almost all **extern providers** wind up going through the `rustc_metadata` crate, which loads the information from the crate metadata. But in some cases there are crates that provide queries for @@ -201,7 +201,7 @@ Well, defining a query takes place in two steps: 1. first, you have to specify the query name and arguments; and then, 2. you have to supply query providers where needed. -The specify the query name and arguments, you simply add an entry +To specify the query name and arguments, you simply add an entry to the big macro invocation in `mod.rs`. This will probably have changed by the time you read this README, but at present it looks something like: diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index c8520c5be2df5..496284ad9c9f4 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; use ty::{self, Ty, TyCtxt}; use ty::maps::queries; @@ -23,54 +24,75 @@ pub trait QueryConfig { type Value; } -pub(super) trait QueryDescription: QueryConfig { +pub(super) trait QueryDescription<'tcx>: QueryConfig { fn describe(tcx: TyCtxt, key: Self::Key) -> String; + + fn cache_on_disk(_: Self::Key) -> bool { + false + } + + fn try_load_from_disk(_: TyCtxt<'_, 'tcx, 'tcx>, + _: SerializedDepNodeIndex) + -> Option { + bug!("QueryDescription::load_from_disk() called for unsupport query.") + } } -impl> QueryDescription for M { +impl<'tcx, M: QueryConfig> QueryDescription<'tcx> for M { default fn describe(tcx: TyCtxt, def_id: DefId) -> String { - format!("processing `{}`", tcx.item_path_str(def_id)) + if !tcx.sess.verbose() { + format!("processing `{}`", tcx.item_path_str(def_id)) + } else { + let name = unsafe { ::std::intrinsics::type_name::() }; + format!("processing `{}` applied to `{:?}`", name, def_id) + } } } -impl<'tcx> QueryDescription for queries::is_copy_raw<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Copy`", env.value) } } -impl<'tcx> QueryDescription for queries::is_sized_raw<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_sized_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Sized`", env.value) } } -impl<'tcx> QueryDescription for queries::is_freeze_raw<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_freeze_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is freeze", env.value) } } -impl<'tcx> QueryDescription for queries::needs_drop_raw<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::needs_drop_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` needs drop", env.value) } } -impl<'tcx> QueryDescription for queries::layout_raw<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::layout_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing layout of `{}`", env.value) } } -impl<'tcx> QueryDescription for queries::super_predicates_of<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::super_predicates_of<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("computing the supertraits of `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::type_param_predicates<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::erase_regions_ty<'tcx> { + fn describe(_tcx: TyCtxt, ty: Ty<'tcx>) -> String { + format!("erasing regions from `{:?}`", ty) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::type_param_predicates<'tcx> { fn describe(tcx: TyCtxt, (_, def_id): (DefId, DefId)) -> String { let id = tcx.hir.as_local_node_id(def_id).unwrap(); format!("computing the bounds for type parameter `{}`", @@ -78,427 +100,470 @@ impl<'tcx> QueryDescription for queries::type_param_predicates<'tcx> { } } -impl<'tcx> QueryDescription for queries::coherent_trait<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::coherent_trait<'tcx> { fn describe(tcx: TyCtxt, (_, def_id): (CrateNum, DefId)) -> String { format!("coherence checking all impls of trait `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::crate_inherent_impls<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_inherent_impls<'tcx> { fn describe(_: TyCtxt, k: CrateNum) -> String { format!("all inherent impls defined in crate `{:?}`", k) } } -impl<'tcx> QueryDescription for queries::crate_inherent_impls_overlap_check<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_inherent_impls_overlap_check<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { format!("check for overlap between inherent impls defined in this crate") } } -impl<'tcx> QueryDescription for queries::crate_variances<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_variances<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("computing the variances for items in this crate") } } -impl<'tcx> QueryDescription for queries::mir_shims<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::mir_shims<'tcx> { fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { format!("generating MIR shim for `{}`", tcx.item_path_str(def.def_id())) } } -impl<'tcx> QueryDescription for queries::privacy_access_levels<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::privacy_access_levels<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { format!("privacy access levels") } } -impl<'tcx> QueryDescription for queries::typeck_item_bodies<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::typeck_item_bodies<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { format!("type-checking all item bodies") } } -impl<'tcx> QueryDescription for queries::reachable_set<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::reachable_set<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { format!("reachability") } } -impl<'tcx> QueryDescription for queries::const_eval<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::const_eval<'tcx> { fn describe(tcx: TyCtxt, key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> String { format!("const-evaluating `{}`", tcx.item_path_str(key.value.0)) } } -impl<'tcx> QueryDescription for queries::mir_keys<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::mir_keys<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { format!("getting a list of all mir_keys") } } -impl<'tcx> QueryDescription for queries::symbol_name<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::symbol_name<'tcx> { fn describe(_tcx: TyCtxt, instance: ty::Instance<'tcx>) -> String { format!("computing the symbol for `{}`", instance) } } -impl<'tcx> QueryDescription for queries::describe_def<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::describe_def<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("describe_def") } } -impl<'tcx> QueryDescription for queries::def_span<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::def_span<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("def_span") } } -impl<'tcx> QueryDescription for queries::lookup_stability<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::lookup_stability<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("stability") } } -impl<'tcx> QueryDescription for queries::lookup_deprecation_entry<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::lookup_deprecation_entry<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("deprecation") } } -impl<'tcx> QueryDescription for queries::item_attrs<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::item_attrs<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("item_attrs") } } -impl<'tcx> QueryDescription for queries::is_exported_symbol<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_exported_symbol<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("is_exported_symbol") } } -impl<'tcx> QueryDescription for queries::fn_arg_names<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::fn_arg_names<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("fn_arg_names") } } -impl<'tcx> QueryDescription for queries::impl_parent<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::impl_parent<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("impl_parent") } } -impl<'tcx> QueryDescription for queries::trait_of_item<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::trait_of_item<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { bug!("trait_of_item") } } -impl<'tcx> QueryDescription for queries::item_body_nested_bodies<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::item_body_nested_bodies<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("nested item bodies of `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::const_is_rvalue_promotable_to_static<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::const_is_rvalue_promotable_to_static<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("const checking if rvalue is promotable to static `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::is_mir_available<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::rvalue_promotable_map<'tcx> { + fn describe(tcx: TyCtxt, def_id: DefId) -> String { + format!("checking which parts of `{}` are promotable to static", + tcx.item_path_str(def_id)) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::is_mir_available<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("checking if item is mir available: `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::trait_impls_of<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::trans_fulfill_obligation<'tcx> { + fn describe(tcx: TyCtxt, key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> String { + format!("checking if `{}` fulfills its obligations", tcx.item_path_str(key.1.def_id())) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::trait_impls_of<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("trait impls of `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::is_object_safe<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_object_safe<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("determine object safety of trait `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::is_const_fn<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn<'tcx> { fn describe(tcx: TyCtxt, def_id: DefId) -> String { format!("checking if item is const fn: `{}`", tcx.item_path_str(def_id)) } } -impl<'tcx> QueryDescription for queries::dylib_dependency_formats<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::dylib_dependency_formats<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { "dylib dependency formats of crate".to_string() } } -impl<'tcx> QueryDescription for queries::is_panic_runtime<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_panic_runtime<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { "checking if the crate is_panic_runtime".to_string() } } -impl<'tcx> QueryDescription for queries::is_compiler_builtins<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_compiler_builtins<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { "checking if the crate is_compiler_builtins".to_string() } } -impl<'tcx> QueryDescription for queries::has_global_allocator<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::has_global_allocator<'tcx> { fn describe(_: TyCtxt, _: CrateNum) -> String { "checking if the crate has_global_allocator".to_string() } } -impl<'tcx> QueryDescription for queries::extern_crate<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::extern_crate<'tcx> { fn describe(_: TyCtxt, _: DefId) -> String { "getting crate's ExternCrateData".to_string() } } -impl<'tcx> QueryDescription for queries::lint_levels<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::lint_levels<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("computing the lint levels for items in this crate") } } -impl<'tcx> QueryDescription for queries::specializes<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::specializes<'tcx> { fn describe(_tcx: TyCtxt, _: (DefId, DefId)) -> String { format!("computing whether impls specialize one another") } } -impl<'tcx> QueryDescription for queries::in_scope_traits_map<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::in_scope_traits_map<'tcx> { fn describe(_tcx: TyCtxt, _: DefIndex) -> String { format!("traits in scope at a block") } } -impl<'tcx> QueryDescription for queries::is_no_builtins<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_no_builtins<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("test whether a crate has #![no_builtins]") } } -impl<'tcx> QueryDescription for queries::panic_strategy<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::panic_strategy<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("query a crate's configured panic strategy") } } -impl<'tcx> QueryDescription for queries::is_profiler_runtime<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_profiler_runtime<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("query a crate is #![profiler_runtime]") } } -impl<'tcx> QueryDescription for queries::is_sanitizer_runtime<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_sanitizer_runtime<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("query a crate is #![sanitizer_runtime]") } } -impl<'tcx> QueryDescription for queries::exported_symbol_ids<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::exported_symbol_ids<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the exported symbols of a crate") } } -impl<'tcx> QueryDescription for queries::native_libraries<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::native_libraries<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the native libraries of a linked crate") } } -impl<'tcx> QueryDescription for queries::plugin_registrar_fn<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::plugin_registrar_fn<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the plugin registrar for a crate") } } -impl<'tcx> QueryDescription for queries::derive_registrar_fn<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::derive_registrar_fn<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the derive registrar for a crate") } } -impl<'tcx> QueryDescription for queries::crate_disambiguator<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_disambiguator<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the disambiguator a crate") } } -impl<'tcx> QueryDescription for queries::crate_hash<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_hash<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the hash a crate") } } -impl<'tcx> QueryDescription for queries::original_crate_name<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::original_crate_name<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up the original name a crate") } } -impl<'tcx> QueryDescription for queries::implementations_of_trait<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::implementations_of_trait<'tcx> { fn describe(_tcx: TyCtxt, _: (CrateNum, DefId)) -> String { format!("looking up implementations of a trait in a crate") } } -impl<'tcx> QueryDescription for queries::all_trait_implementations<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::all_trait_implementations<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up all (?) trait implementations") } } -impl<'tcx> QueryDescription for queries::link_args<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::link_args<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up link arguments for a crate") } } -impl<'tcx> QueryDescription for queries::named_region_map<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::named_region_map<'tcx> { fn describe(_tcx: TyCtxt, _: DefIndex) -> String { format!("looking up a named region") } } -impl<'tcx> QueryDescription for queries::is_late_bound_map<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::is_late_bound_map<'tcx> { fn describe(_tcx: TyCtxt, _: DefIndex) -> String { format!("testing if a region is late boudn") } } -impl<'tcx> QueryDescription for queries::object_lifetime_defaults_map<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::object_lifetime_defaults_map<'tcx> { fn describe(_tcx: TyCtxt, _: DefIndex) -> String { format!("looking up lifetime defaults for a region") } } -impl<'tcx> QueryDescription for queries::dep_kind<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::dep_kind<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("fetching what a dependency looks like") } } -impl<'tcx> QueryDescription for queries::crate_name<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::crate_name<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("fetching what a crate is named") } } -impl<'tcx> QueryDescription for queries::get_lang_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::get_lang_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("calculating the lang items map") } } -impl<'tcx> QueryDescription for queries::defined_lang_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::defined_lang_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("calculating the lang items defined in a crate") } } -impl<'tcx> QueryDescription for queries::missing_lang_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::missing_lang_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("calculating the missing lang items in a crate") } } -impl<'tcx> QueryDescription for queries::visible_parent_map<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::visible_parent_map<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("calculating the visible parent map") } } -impl<'tcx> QueryDescription for queries::missing_extern_crate_item<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::missing_extern_crate_item<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("seeing if we're missing an `extern crate` item for this crate") } } -impl<'tcx> QueryDescription for queries::used_crate_source<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::used_crate_source<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking at the source for a crate") } } -impl<'tcx> QueryDescription for queries::postorder_cnums<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::postorder_cnums<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("generating a postorder list of CrateNums") } } -impl<'tcx> QueryDescription for queries::maybe_unused_extern_crates<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::maybe_unused_extern_crates<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up all possibly unused extern crates") } } -impl<'tcx> QueryDescription for queries::stability_index<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::stability_index<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("calculating the stability index for the local crate") } } -impl<'tcx> QueryDescription for queries::all_crate_nums<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::all_crate_nums<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("fetching all foreign CrateNum instances") } } -impl<'tcx> QueryDescription for queries::exported_symbols<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::exported_symbols<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("exported_symbols") } } -impl<'tcx> QueryDescription for queries::collect_and_partition_translation_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::collect_and_partition_translation_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("collect_and_partition_translation_items") } } -impl<'tcx> QueryDescription for queries::codegen_unit<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::codegen_unit<'tcx> { fn describe(_tcx: TyCtxt, _: InternedString) -> String { format!("codegen_unit") } } -impl<'tcx> QueryDescription for queries::compile_codegen_unit<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::compile_codegen_unit<'tcx> { fn describe(_tcx: TyCtxt, _: InternedString) -> String { format!("compile_codegen_unit") } } -impl<'tcx> QueryDescription for queries::output_filenames<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::output_filenames<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("output_filenames") } } -impl<'tcx> QueryDescription for queries::has_clone_closures<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::has_clone_closures<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("seeing if the crate has enabled `Clone` closures") } } -impl<'tcx> QueryDescription for queries::has_copy_closures<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::vtable_methods<'tcx> { + fn describe(tcx: TyCtxt, key: ty::PolyTraitRef<'tcx> ) -> String { + format!("finding all methods for trait {}", tcx.item_path_str(key.def_id())) + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::has_copy_closures<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("seeing if the crate has enabled `Copy` closures") } } + +impl<'tcx> QueryDescription<'tcx> for queries::fully_normalize_monormophic_ty<'tcx> { + fn describe(_tcx: TyCtxt, _: Ty) -> String { + format!("normalizing types") + } +} + +impl<'tcx> QueryDescription<'tcx> for queries::typeck_tables_of<'tcx> { + #[inline] + fn cache_on_disk(def_id: Self::Key) -> bool { + def_id.is_local() + } + + fn try_load_from_disk(tcx: TyCtxt<'_, 'tcx, 'tcx>, + id: SerializedDepNodeIndex) + -> Option { + let typeck_tables: Option> = tcx + .on_disk_query_result_cache + .try_load_query_result(tcx, id); + + typeck_tables.map(|tables| tcx.alloc_tables(tables)) + } +} + diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index e37cf66979781..b7b64c9761a8e 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -11,7 +11,6 @@ //! Defines the set of legal keys that can be used in queries. use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; -use mir::transform::{MirSuite, MirPassIndex}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; @@ -116,21 +115,21 @@ impl<'tcx> Key for (DefId, &'tcx Substs<'tcx>) { } } -impl Key for (MirSuite, DefId) { +impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) { fn map_crate(&self) -> CrateNum { - self.1.map_crate() + self.1.def_id().krate } fn default_span(&self, tcx: TyCtxt) -> Span { - self.1.default_span(tcx) + tcx.def_span(self.1.def_id()) } } -impl Key for (MirSuite, MirPassIndex, DefId) { +impl<'tcx> Key for ty::PolyTraitRef<'tcx>{ fn map_crate(&self) -> CrateNum { - self.2.map_crate() + self.def_id().krate } fn default_span(&self, tcx: TyCtxt) -> Span { - self.2.default_span(tcx) + tcx.def_span(self.def_id()) } } diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 4bd2d5be6d716..fb3600182d8a6 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -15,6 +15,7 @@ use hir::def::{Def, Export}; use hir::{self, TraitCandidate, ItemLocalId}; use hir::svh::Svh; use lint; +use middle::borrowck::BorrowCheckResult; use middle::const_val; use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, ExternBodyNestedBodies}; @@ -28,22 +29,21 @@ use middle::lang_items::{LanguageItems, LangItem}; use middle::exported_symbols::SymbolExportLevel; use middle::trans::{CodegenUnit, Stats}; use mir; -use session::CompileResult; +use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; +use traits::Vtable; use traits::specialization_graph; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; -use ty::layout::{Layout, LayoutError}; use ty::steal::Steal; use ty::subst::Substs; -use util::nodemap::{DefIdSet, DefIdMap}; -use util::common::{profq_msg, ProfileQueriesMsg}; +use util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet}; +use util::common::{profq_msg, ErrorReported, ProfileQueriesMsg}; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_back::PanicStrategy; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::StableVec; -use std::cell::{RefCell, Cell}; use std::ops::Deref; use std::rc::Rc; @@ -57,6 +57,7 @@ use syntax::symbol::Symbol; #[macro_use] mod plumbing; use self::plumbing::*; +pub use self::plumbing::force_from_dep_node; mod keys; pub use self::keys::Key; @@ -68,6 +69,9 @@ mod config; pub use self::config::QueryConfig; use self::config::QueryDescription; +mod on_disk_cache; +pub use self::on_disk_cache::OnDiskCache; + // Each of these maps also corresponds to a method on a // `Provider` trait for requesting a value of that type, // and a method on `Maps` itself for doing that in a @@ -108,8 +112,8 @@ define_maps! { <'tcx> /// True if this is a foreign item (i.e., linked via `extern { ... }`). [] fn is_foreign_item: IsForeignItem(DefId) -> bool, - /// True if this is a default impl (aka impl Foo for ..) - [] fn is_default_impl: IsDefaultImpl(DefId) -> bool, + /// True if this is an auto impl (aka impl Foo for ..) + [] fn is_auto_impl: IsAutoImpl(DefId) -> bool, /// Get a map with the variance of every item; use `item_variance` /// instead. @@ -119,6 +123,9 @@ define_maps! { <'tcx> /// (inferred) variance. [] fn variances_of: ItemVariances(DefId) -> Rc>, + /// Maps from def-id of a type to its (inferred) outlives. + [] fn inferred_outlives_of: InferredOutlivesOf(DefId) -> Vec>, + /// Maps from an impl/trait def-id to a list of the def-ids of its items [] fn associated_item_def_ids: AssociatedItemDefIds(DefId) -> Rc>, @@ -143,6 +150,10 @@ define_maps! { <'tcx> /// the value isn't known except to the pass itself. [] fn mir_const_qualif: MirConstQualif(DefId) -> (u8, Rc>), + /// Fetch the MIR for a given def-id right after it's built - this includes + /// unreachable code. + [] fn mir_built: MirBuilt(DefId) -> &'tcx Steal>, + /// Fetch the MIR for a given def-id up till the point where it is /// ready for const evaluation. /// @@ -155,21 +166,15 @@ define_maps! { <'tcx> /// for trans. This is also the only query that can fetch non-local MIR, at present. [] fn optimized_mir: MirOptimized(DefId) -> &'tcx mir::Mir<'tcx>, - /// Type of each closure. The def ID is the ID of the - /// expression defining the closure. - [] fn closure_kind: ClosureKind(DefId) -> ty::ClosureKind, + /// The result of unsafety-checking this def-id. + [] fn unsafety_check_result: UnsafetyCheckResult(DefId) -> mir::UnsafetyCheckResult, - /// Unsafety violations for this def ID. - [] fn unsafety_violations: UnsafetyViolations(DefId) - -> Rc<[mir::UnsafetyViolation]>, + /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error + [] fn unsafe_derive_on_repr_packed: UnsafeDeriveOnReprPacked(DefId) -> (), /// The signature of functions and closures. [] fn fn_sig: FnSignature(DefId) -> ty::PolyFnSig<'tcx>, - /// Records the signature of each generator. The def ID is the ID of the - /// expression defining the closure. - [] fn generator_sig: GenSignature(DefId) -> Option>, - /// Caches CoerceUnsized kinds for impls on custom types. [] fn coerce_unsized_info: CoerceUnsizedInfo(DefId) -> ty::adjustment::CoerceUnsizedInfo, @@ -178,11 +183,13 @@ define_maps! { <'tcx> [] fn typeck_tables_of: TypeckTables(DefId) -> &'tcx ty::TypeckTables<'tcx>, + [] fn used_trait_imports: UsedTraitImports(DefId) -> Rc, + [] fn has_typeck_tables: HasTypeckTables(DefId) -> bool, [] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (), - [] fn borrowck: BorrowCheck(DefId) -> (), + [] fn borrowck: BorrowCheck(DefId) -> Rc, // FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead? [] fn mir_borrowck: MirBorrowCheck(DefId) -> (), @@ -201,6 +208,9 @@ define_maps! { <'tcx> [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> const_val::EvalResult<'tcx>, + [] fn check_match: CheckMatch(DefId) + -> Result<(), ErrorReported>, + /// Performs the privacy check and computes "access levels". [] fn privacy_access_levels: PrivacyAccessLevels(CrateNum) -> Rc, @@ -226,8 +236,13 @@ define_maps! { <'tcx> [] fn is_exported_symbol: IsExportedSymbol(DefId) -> bool, [] fn item_body_nested_bodies: ItemBodyNestedBodies(DefId) -> ExternBodyNestedBodies, [] fn const_is_rvalue_promotable_to_static: ConstIsRvaluePromotableToStatic(DefId) -> bool, + [] fn rvalue_promotable_map: RvaluePromotableMap(DefId) -> Rc, [] fn is_mir_available: IsMirAvailable(DefId) -> bool, + [] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>) + -> Rc)>>>, + [] fn trans_fulfill_obligation: fulfill_obligation_dep_node( + (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> Vtable<'tcx, ()>, [] fn trait_impls_of: TraitImpls(DefId) -> Rc, [] fn specialization_graph_of: SpecializationGraph(DefId) -> Rc, [] fn is_object_safe: ObjectSafety(DefId) -> bool, @@ -247,7 +262,8 @@ define_maps! { <'tcx> [] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool, [] fn layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) - -> Result<&'tcx Layout, LayoutError<'tcx>>, + -> Result<&'tcx ty::layout::LayoutDetails, + ty::layout::LayoutError<'tcx>>, [] fn dylib_dependency_formats: DylibDepFormats(CrateNum) -> Rc>, @@ -273,7 +289,7 @@ define_maps! { <'tcx> [] fn native_libraries: NativeLibraries(CrateNum) -> Rc>, [] fn plugin_registrar_fn: PluginRegistrarFn(CrateNum) -> Option, [] fn derive_registrar_fn: DeriveRegistrarFn(CrateNum) -> Option, - [] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> Symbol, + [] fn crate_disambiguator: CrateDisambiguator(CrateNum) -> CrateDisambiguator, [] fn crate_hash: CrateHash(CrateNum) -> Svh, [] fn original_crate_name: OriginalCrateName(CrateNum) -> Symbol, @@ -334,12 +350,22 @@ define_maps! { <'tcx> [] fn has_copy_closures: HasCopyClosures(CrateNum) -> bool, [] fn has_clone_closures: HasCloneClosures(CrateNum) -> bool, + + // Erases regions from `ty` to yield a new type. + // Normally you would just use `tcx.erase_regions(&value)`, + // however, which uses this query as a kind of cache. + [] fn erase_regions_ty: erase_regions_ty(Ty<'tcx>) -> Ty<'tcx>, + [] fn fully_normalize_monormophic_ty: normalize_ty_node(Ty<'tcx>) -> Ty<'tcx>, } ////////////////////////////////////////////////////////////////////// // These functions are little shims used to find the dep-node for a // given query when there is not a *direct* mapping: +fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> { + DepConstructor::EraseRegionsTy { ty } +} + fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> { DepConstructor::TypeParamPredicates { item_id, @@ -347,6 +373,14 @@ fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstr } } +fn fulfill_obligation_dep_node<'tcx>((param_env, trait_ref): + (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> DepConstructor<'tcx> { + DepConstructor::FulfillObligation { + param_env, + trait_ref + } +} + fn coherent_trait_dep_node<'tcx>((_, def_id): (CrateNum, DefId)) -> DepConstructor<'tcx> { DepConstructor::CoherenceCheckTrait(def_id) } @@ -377,9 +411,9 @@ fn typeck_item_bodies_dep_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::TypeckBodiesKrate } -fn const_eval_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) +fn const_eval_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>) -> DepConstructor<'tcx> { - DepConstructor::ConstEval + DepConstructor::ConstEval { param_env } } fn mir_keys<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { @@ -390,24 +424,24 @@ fn crate_variances<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::CrateVariances } -fn is_copy_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { - DepConstructor::IsCopy +fn is_copy_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::IsCopy { param_env } } -fn is_sized_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { - DepConstructor::IsSized +fn is_sized_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::IsSized { param_env } } -fn is_freeze_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { - DepConstructor::IsFreeze +fn is_freeze_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::IsFreeze { param_env } } -fn needs_drop_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { - DepConstructor::NeedsDrop +fn needs_drop_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::NeedsDrop { param_env } } -fn layout_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { - DepConstructor::Layout +fn layout_dep_node<'tcx>(param_env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> { + DepConstructor::Layout { param_env } } fn lint_levels_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { @@ -459,3 +493,10 @@ fn collect_and_partition_translation_items_node<'tcx>(_: CrateNum) -> DepConstru fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::OutputFilenames } + +fn vtable_methods_node<'tcx>(trait_ref: ty::PolyTraitRef<'tcx>) -> DepConstructor<'tcx> { + DepConstructor::VtableMethods{ trait_ref } +} +fn normalize_ty_node<'tcx>(_: Ty<'tcx>) -> DepConstructor<'tcx> { + DepConstructor::NormalizeTy +} diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs new file mode 100644 index 0000000000000..8dc9b0877a01c --- /dev/null +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -0,0 +1,993 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use errors::Diagnostic; +use hir; +use hir::def_id::{CrateNum, DefIndex, DefId, LocalDefId, + RESERVED_FOR_INCR_COMP_CACHE, LOCAL_CRATE}; +use hir::map::definitions::DefPathHash; +use ich::CachingCodemapView; +use mir; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque, + SpecializedDecoder, SpecializedEncoder, + UseSpecializedDecodable, UseSpecializedEncodable}; +use session::{CrateDisambiguator, Session}; +use std::cell::RefCell; +use std::mem; +use std::rc::Rc; +use syntax::ast::NodeId; +use syntax::codemap::{CodeMap, StableFilemapId}; +use syntax_pos::{BytePos, Span, DUMMY_SP, FileMap}; +use syntax_pos::hygiene::{Mark, SyntaxContext, ExpnInfo}; +use ty; +use ty::codec::{self as ty_codec, TyDecoder, TyEncoder}; +use ty::context::TyCtxt; + +const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; + +const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; +const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; + +const TAG_NO_EXPANSION_INFO: u8 = 0; +const TAG_EXPANSION_INFO_SHORTHAND: u8 = 1; +const TAG_EXPANSION_INFO_INLINE: u8 = 2; + +const TAG_VALID_SPAN: u8 = 0; +const TAG_INVALID_SPAN: u8 = 1; + +/// `OnDiskCache` provides an interface to incr. comp. data cached from the +/// previous compilation session. This data will eventually include the results +/// of a few selected queries (like `typeck_tables_of` and `mir_optimized`) and +/// any diagnostics that have been emitted during a query. +pub struct OnDiskCache<'sess> { + + // The complete cache data in serialized form. + serialized_data: Vec, + + // This field collects all Diagnostics emitted during the current + // compilation session. + current_diagnostics: RefCell>>, + + prev_cnums: Vec<(u32, String, CrateDisambiguator)>, + cnum_map: RefCell>>>, + + codemap: &'sess CodeMap, + file_index_to_stable_id: FxHashMap, + + // These two fields caches that are populated lazily during decoding. + file_index_to_file: RefCell>>, + synthetic_expansion_infos: RefCell>, + + // A map from dep-node to the position of the cached query result in + // `serialized_data`. + query_result_index: FxHashMap, + + // A map from dep-node to the position of any associated diagnostics in + // `serialized_data`. + prev_diagnostics_index: FxHashMap, +} + +// This type is used only for (de-)serialization. +#[derive(RustcEncodable, RustcDecodable)] +struct Footer { + file_index_to_stable_id: FxHashMap, + prev_cnums: Vec<(u32, String, CrateDisambiguator)>, + query_result_index: EncodedQueryResultIndex, + diagnostics_index: EncodedQueryResultIndex, +} + +type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; +type EncodedDiagnosticsIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; +type EncodedDiagnostics = Vec; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +struct FileMapIndex(u32); + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, RustcEncodable, RustcDecodable)] +struct AbsoluteBytePos(u32); + +impl AbsoluteBytePos { + fn new(pos: usize) -> AbsoluteBytePos { + debug_assert!(pos <= ::std::u32::MAX as usize); + AbsoluteBytePos(pos as u32) + } + + fn to_usize(self) -> usize { + self.0 as usize + } +} + +impl<'sess> OnDiskCache<'sess> { + /// Create a new OnDiskCache instance from the serialized data in `data`. + pub fn new(sess: &'sess Session, data: Vec, start_pos: usize) -> OnDiskCache<'sess> { + debug_assert!(sess.opts.incremental.is_some()); + + // Wrapping in a scope so we can borrow `data` + let footer: Footer = { + let mut decoder = opaque::Decoder::new(&data[..], start_pos); + + // Decode the *position* of the footer which can be found in the + // last 8 bytes of the file. + decoder.set_position(data.len() - IntEncodedWithFixedSize::ENCODED_SIZE); + let query_result_index_pos = IntEncodedWithFixedSize::decode(&mut decoder) + .expect("Error while trying to decode query result index position.") + .0 as usize; + + // Decoder the file footer which contains all the lookup tables, etc. + decoder.set_position(query_result_index_pos); + decode_tagged(&mut decoder, TAG_FILE_FOOTER) + .expect("Error while trying to decode query result index position.") + }; + + OnDiskCache { + serialized_data: data, + file_index_to_stable_id: footer.file_index_to_stable_id, + file_index_to_file: RefCell::new(FxHashMap()), + prev_cnums: footer.prev_cnums, + cnum_map: RefCell::new(None), + codemap: sess.codemap(), + current_diagnostics: RefCell::new(FxHashMap()), + query_result_index: footer.query_result_index.into_iter().collect(), + prev_diagnostics_index: footer.diagnostics_index.into_iter().collect(), + synthetic_expansion_infos: RefCell::new(FxHashMap()), + } + } + + pub fn new_empty(codemap: &'sess CodeMap) -> OnDiskCache<'sess> { + OnDiskCache { + serialized_data: Vec::new(), + file_index_to_stable_id: FxHashMap(), + file_index_to_file: RefCell::new(FxHashMap()), + prev_cnums: vec![], + cnum_map: RefCell::new(None), + codemap, + current_diagnostics: RefCell::new(FxHashMap()), + query_result_index: FxHashMap(), + prev_diagnostics_index: FxHashMap(), + synthetic_expansion_infos: RefCell::new(FxHashMap()), + } + } + + pub fn serialize<'a, 'tcx, E>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + encoder: &mut E) + -> Result<(), E::Error> + where E: ty_codec::TyEncoder + { + // Serializing the DepGraph should not modify it: + let _in_ignore = tcx.dep_graph.in_ignore(); + + // Allocate FileMapIndices + let (file_to_file_index, file_index_to_stable_id) = { + let mut file_to_file_index = FxHashMap(); + let mut file_index_to_stable_id = FxHashMap(); + + for (index, file) in tcx.sess.codemap().files().iter().enumerate() { + let index = FileMapIndex(index as u32); + let file_ptr: *const FileMap = &**file as *const _; + file_to_file_index.insert(file_ptr, index); + file_index_to_stable_id.insert(index, StableFilemapId::new(&file)); + } + + (file_to_file_index, file_index_to_stable_id) + }; + + let mut encoder = CacheEncoder { + tcx, + encoder, + type_shorthands: FxHashMap(), + predicate_shorthands: FxHashMap(), + expn_info_shorthands: FxHashMap(), + codemap: CachingCodemapView::new(tcx.sess.codemap()), + file_to_file_index, + }; + + // Load everything into memory so we can write it out to the on-disk + // cache. The vast majority of cacheable query results should already + // be in memory, so this should be a cheap operation. + tcx.dep_graph.exec_cache_promotions(tcx); + + // Encode query results + let mut query_result_index = EncodedQueryResultIndex::new(); + + { + use ty::maps::queries::*; + let enc = &mut encoder; + let qri = &mut query_result_index; + + // Encode TypeckTables + encode_query_results::(tcx, enc, qri)?; + } + + // Encode diagnostics + let diagnostics_index = { + let mut diagnostics_index = EncodedDiagnosticsIndex::new(); + + for (dep_node_index, diagnostics) in self.current_diagnostics + .borrow() + .iter() { + let pos = AbsoluteBytePos::new(encoder.position()); + // Let's make sure we get the expected type here: + let diagnostics: &EncodedDiagnostics = diagnostics; + let dep_node_index = + SerializedDepNodeIndex::new(dep_node_index.index()); + encoder.encode_tagged(dep_node_index, diagnostics)?; + diagnostics_index.push((dep_node_index, pos)); + } + + diagnostics_index + }; + + let sorted_cnums = sorted_cnums_including_local_crate(tcx); + let prev_cnums: Vec<_> = sorted_cnums.iter().map(|&cnum| { + let crate_name = tcx.original_crate_name(cnum).as_str().to_string(); + let crate_disambiguator = tcx.crate_disambiguator(cnum); + (cnum.as_u32(), crate_name, crate_disambiguator) + }).collect(); + + // Encode the file footer + let footer_pos = encoder.position() as u64; + encoder.encode_tagged(TAG_FILE_FOOTER, &Footer { + file_index_to_stable_id, + prev_cnums, + query_result_index, + diagnostics_index, + })?; + + // Encode the position of the footer as the last 8 bytes of the + // file so we know where to look for it. + IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder)?; + + // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address + // of the footer must be the last thing in the data stream. + + return Ok(()); + + fn sorted_cnums_including_local_crate(tcx: TyCtxt) -> Vec { + let mut cnums = vec![LOCAL_CRATE]; + cnums.extend_from_slice(&tcx.crates()[..]); + cnums.sort_unstable(); + // Just to be sure... + cnums.dedup(); + cnums + } + } + + /// Load a diagnostic emitted during the previous compilation session. + pub fn load_diagnostics<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + dep_node_index: SerializedDepNodeIndex) + -> Vec { + let diagnostics: Option = self.load_indexed( + tcx, + dep_node_index, + &self.prev_diagnostics_index, + "diagnostics"); + + diagnostics.unwrap_or(Vec::new()) + } + + /// Store a diagnostic emitted during the current compilation session. + /// Anything stored like this will be available via `load_diagnostics` in + /// the next compilation session. + pub fn store_diagnostics(&self, + dep_node_index: DepNodeIndex, + diagnostics: Vec) { + let mut current_diagnostics = self.current_diagnostics.borrow_mut(); + let prev = current_diagnostics.insert(dep_node_index, diagnostics); + debug_assert!(prev.is_none()); + } + + /// Returns the cached query result if there is something in the cache for + /// the given SerializedDepNodeIndex. Otherwise returns None. + pub fn try_load_query_result<'tcx, T>(&self, + tcx: TyCtxt<'_, 'tcx, 'tcx>, + dep_node_index: SerializedDepNodeIndex) + -> Option + where T: Decodable + { + self.load_indexed(tcx, + dep_node_index, + &self.query_result_index, + "query result") + } + + /// Store a diagnostic emitted during computation of an anonymous query. + /// Since many anonymous queries can share the same `DepNode`, we aggregate + /// them -- as opposed to regular queries where we assume that there is a + /// 1:1 relationship between query-key and `DepNode`. + pub fn store_diagnostics_for_anon_node(&self, + dep_node_index: DepNodeIndex, + mut diagnostics: Vec) { + let mut current_diagnostics = self.current_diagnostics.borrow_mut(); + + let x = current_diagnostics.entry(dep_node_index).or_insert_with(|| { + mem::replace(&mut diagnostics, Vec::new()) + }); + + x.extend(diagnostics.into_iter()); + } + + fn load_indexed<'tcx, T>(&self, + tcx: TyCtxt<'_, 'tcx, 'tcx>, + dep_node_index: SerializedDepNodeIndex, + index: &FxHashMap, + debug_tag: &'static str) + -> Option + where T: Decodable + { + let pos = if let Some(&pos) = index.get(&dep_node_index) { + pos + } else { + return None + }; + + let mut cnum_map = self.cnum_map.borrow_mut(); + if cnum_map.is_none() { + *cnum_map = Some(Self::compute_cnum_map(tcx, &self.prev_cnums[..])); + } + + let mut synthetic_expansion_infos = self.synthetic_expansion_infos.borrow_mut(); + let mut file_index_to_file = self.file_index_to_file.borrow_mut(); + + let mut decoder = CacheDecoder { + tcx, + opaque: opaque::Decoder::new(&self.serialized_data[..], pos.to_usize()), + codemap: self.codemap, + cnum_map: cnum_map.as_ref().unwrap(), + file_index_to_file: &mut file_index_to_file, + file_index_to_stable_id: &self.file_index_to_stable_id, + synthetic_expansion_infos: &mut synthetic_expansion_infos, + }; + + match decode_tagged(&mut decoder, dep_node_index) { + Ok(value) => { + Some(value) + } + Err(e) => { + bug!("Could not decode cached {}: {}", debug_tag, e) + } + } + } + + // This function builds mapping from previous-session-CrateNum to + // current-session-CrateNum. There might be CrateNums from the previous + // Session that don't occur in the current one. For these, the mapping + // maps to None. + fn compute_cnum_map(tcx: TyCtxt, + prev_cnums: &[(u32, String, CrateDisambiguator)]) + -> IndexVec> + { + let _in_ignore = tcx.dep_graph.in_ignore(); + + let current_cnums = tcx.all_crate_nums(LOCAL_CRATE).iter().map(|&cnum| { + let crate_name = tcx.original_crate_name(cnum) + .as_str() + .to_string(); + let crate_disambiguator = tcx.crate_disambiguator(cnum); + ((crate_name, crate_disambiguator), cnum) + }).collect::>(); + + let map_size = prev_cnums.iter() + .map(|&(cnum, ..)| cnum) + .max() + .unwrap_or(0) + 1; + let mut map = IndexVec::new(); + map.resize(map_size as usize, None); + + for &(prev_cnum, ref crate_name, crate_disambiguator) in prev_cnums { + let key = (crate_name.clone(), crate_disambiguator); + map[CrateNum::from_u32(prev_cnum)] = current_cnums.get(&key).cloned(); + } + + map[LOCAL_CRATE] = Some(LOCAL_CRATE); + map + } +} + + +//- DECODING ------------------------------------------------------------------- + +/// A decoder that can read the incr. comp. cache. It is similar to the one +/// we use for crate metadata decoding in that it can rebase spans and +/// eventually will also handle things that contain `Ty` instances. +struct CacheDecoder<'a, 'tcx: 'a, 'x> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + opaque: opaque::Decoder<'x>, + codemap: &'x CodeMap, + cnum_map: &'x IndexVec>, + synthetic_expansion_infos: &'x mut FxHashMap, + file_index_to_file: &'x mut FxHashMap>, + file_index_to_stable_id: &'x FxHashMap, +} + +impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { + fn file_index_to_file(&mut self, index: FileMapIndex) -> Rc { + let CacheDecoder { + ref mut file_index_to_file, + ref file_index_to_stable_id, + ref codemap, + .. + } = *self; + + file_index_to_file.entry(index).or_insert_with(|| { + let stable_id = file_index_to_stable_id[&index]; + codemap.filemap_by_stable_id(stable_id) + .expect("Failed to lookup FileMap in new context.") + }).clone() + } +} + +trait DecoderWithPosition: Decoder { + fn position(&self) -> usize; +} + +impl<'enc> DecoderWithPosition for opaque::Decoder<'enc> { + fn position(&self) -> usize { + self.position() + } +} + +impl<'a, 'tcx, 'x> DecoderWithPosition for CacheDecoder<'a, 'tcx, 'x> { + fn position(&self) -> usize { + self.opaque.position() + } +} + +// Decode something that was encoded with encode_tagged() and verify that the +// tag matches and the correct amount of bytes was read. +fn decode_tagged<'a, 'tcx, D, T, V>(decoder: &mut D, + expected_tag: T) + -> Result + where T: Decodable + Eq + ::std::fmt::Debug, + V: Decodable, + D: DecoderWithPosition, + 'tcx: 'a, +{ + let start_pos = decoder.position(); + + let actual_tag = T::decode(decoder)?; + assert_eq!(actual_tag, expected_tag); + let value = V::decode(decoder)?; + let end_pos = decoder.position(); + + let expected_len: u64 = Decodable::decode(decoder)?; + assert_eq!((end_pos - start_pos) as u64, expected_len); + + Ok(value) +} + + +impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx, 'x> { + + #[inline] + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } + + #[inline] + fn position(&self) -> usize { + self.opaque.position() + } + + #[inline] + fn peek_byte(&self) -> u8 { + self.opaque.data[self.opaque.position()] + } + + fn cached_ty_for_shorthand(&mut self, + shorthand: usize, + or_insert_with: F) + -> Result, Self::Error> + where F: FnOnce(&mut Self) -> Result, Self::Error> + { + let tcx = self.tcx(); + + let cache_key = ty::CReaderCacheKey { + cnum: RESERVED_FOR_INCR_COMP_CACHE, + pos: shorthand, + }; + + if let Some(&ty) = tcx.rcache.borrow().get(&cache_key) { + return Ok(ty); + } + + let ty = or_insert_with(self)?; + tcx.rcache.borrow_mut().insert(cache_key, ty); + Ok(ty) + } + + fn with_position(&mut self, pos: usize, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + debug_assert!(pos < self.opaque.data.len()); + + let new_opaque = opaque::Decoder::new(self.opaque.data, pos); + let old_opaque = mem::replace(&mut self.opaque, new_opaque); + let r = f(self); + self.opaque = old_opaque; + r + } + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { + self.cnum_map[cnum].unwrap_or_else(|| { + bug!("Could not find new CrateNum for {:?}", cnum) + }) + } +} + +implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); + +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + let tag: u8 = Decodable::decode(self)?; + + if tag == TAG_INVALID_SPAN { + return Ok(DUMMY_SP); + } else { + debug_assert_eq!(tag, TAG_VALID_SPAN); + } + + let file_lo_index = FileMapIndex::decode(self)?; + let line_lo = usize::decode(self)?; + let col_lo = BytePos::decode(self)?; + let len = BytePos::decode(self)?; + + let file_lo = self.file_index_to_file(file_lo_index); + let lo = file_lo.lines.borrow()[line_lo - 1] + col_lo; + let hi = lo + len; + + let expn_info_tag = u8::decode(self)?; + + let ctxt = match expn_info_tag { + TAG_NO_EXPANSION_INFO => { + SyntaxContext::empty() + } + TAG_EXPANSION_INFO_INLINE => { + let pos = AbsoluteBytePos::new(self.opaque.position()); + let expn_info: ExpnInfo = Decodable::decode(self)?; + let ctxt = SyntaxContext::allocate_directly(expn_info); + self.synthetic_expansion_infos.insert(pos, ctxt); + ctxt + } + TAG_EXPANSION_INFO_SHORTHAND => { + let pos = AbsoluteBytePos::decode(self)?; + if let Some(ctxt) = self.synthetic_expansion_infos.get(&pos).cloned() { + ctxt + } else { + let expn_info = self.with_position(pos.to_usize(), |this| { + ExpnInfo::decode(this) + })?; + let ctxt = SyntaxContext::allocate_directly(expn_info); + self.synthetic_expansion_infos.insert(pos, ctxt); + ctxt + } + } + _ => { + unreachable!() + } + }; + + Ok(Span::new(lo, hi, ctxt)) + } +} + +// This impl makes sure that we get a runtime error when we try decode a +// DefIndex that is not contained in a DefId. Such a case would be problematic +// because we would not know how to transform the DefIndex to the current +// context. +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + bug!("Trying to decode DefIndex outside the context of a DefId") + } +} + +// Both the CrateNum and the DefIndex of a DefId can change in between two +// compilation sessions. We use the DefPathHash, which is stable across +// sessions, to map the old DefId to the new one. +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + #[inline] + fn specialized_decode(&mut self) -> Result { + // Load the DefPathHash which is was we encoded the DefId as. + let def_path_hash = DefPathHash::decode(self)?; + + // Using the DefPathHash, we can lookup the new DefId + Ok(self.tcx().def_path_hash_to_def_id.as_ref().unwrap()[&def_path_hash]) + } +} + +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + #[inline] + fn specialized_decode(&mut self) -> Result { + Ok(LocalDefId::from_def_id(DefId::decode(self)?)) + } +} + +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + fn specialized_decode(&mut self) -> Result { + // Load the DefPathHash which is was we encoded the DefIndex as. + let def_path_hash = DefPathHash::decode(self)?; + + // Use the DefPathHash to map to the current DefId. + let def_id = self.tcx() + .def_path_hash_to_def_id + .as_ref() + .unwrap()[&def_path_hash]; + + debug_assert!(def_id.is_local()); + + // The ItemLocalId needs no remapping. + let local_id = hir::ItemLocalId::decode(self)?; + + // Reconstruct the HirId and look up the corresponding NodeId in the + // context of the current session. + Ok(hir::HirId { + owner: def_id.index, + local_id + }) + } +} + +// NodeIds are not stable across compilation sessions, so we store them in their +// HirId representation. This allows use to map them to the current NodeId. +impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { + #[inline] + fn specialized_decode(&mut self) -> Result { + let hir_id = hir::HirId::decode(self)?; + Ok(self.tcx().hir.hir_to_node_id(hir_id)) + } +} + +impl<'a, 'tcx, 'x, T: Decodable> SpecializedDecoder> +for CacheDecoder<'a, 'tcx, 'x> { + #[inline] + fn specialized_decode(&mut self) -> Result, Self::Error> { + let discr = u8::decode(self)?; + + match discr { + TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(mir::ClearCrossCrate::Clear), + TAG_CLEAR_CROSS_CRATE_SET => { + let val = T::decode(self)?; + Ok(mir::ClearCrossCrate::Set(val)) + } + _ => { + unreachable!() + } + } + } +} + +//- ENCODING ------------------------------------------------------------------- + +struct CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder, + 'tcx: 'a, +{ + tcx: TyCtxt<'a, 'tcx, 'tcx>, + encoder: &'enc mut E, + type_shorthands: FxHashMap, usize>, + predicate_shorthands: FxHashMap, usize>, + expn_info_shorthands: FxHashMap, + codemap: CachingCodemapView<'tcx>, + file_to_file_index: FxHashMap<*const FileMap, FileMapIndex>, +} + +impl<'enc, 'a, 'tcx, E> CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn filemap_index(&mut self, filemap: Rc) -> FileMapIndex { + self.file_to_file_index[&(&*filemap as *const FileMap)] + } + + /// Encode something with additional information that allows to do some + /// sanity checks when decoding the data again. This method will first + /// encode the specified tag, then the given value, then the number of + /// bytes taken up by tag and value. On decoding, we can then verify that + /// we get the expected tag and read the expected number of bytes. + fn encode_tagged(&mut self, + tag: T, + value: &V) + -> Result<(), E::Error> + { + use ty::codec::TyEncoder; + let start_pos = self.position(); + + tag.encode(self)?; + value.encode(self)?; + + let end_pos = self.position(); + ((end_pos - start_pos) as u64).encode(self) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn specialized_encode(&mut self, span: &Span) -> Result<(), Self::Error> { + + if *span == DUMMY_SP { + return TAG_INVALID_SPAN.encode(self); + } + + let span_data = span.data(); + + if span_data.hi < span_data.lo { + return TAG_INVALID_SPAN.encode(self); + } + + let (file_lo, line_lo, col_lo) = match self.codemap + .byte_pos_to_line_and_col(span_data.lo) { + Some(pos) => pos, + None => { + return TAG_INVALID_SPAN.encode(self); + } + }; + + if !file_lo.contains(span_data.hi) { + return TAG_INVALID_SPAN.encode(self); + } + + let len = span_data.hi - span_data.lo; + + let filemap_index = self.filemap_index(file_lo); + + TAG_VALID_SPAN.encode(self)?; + filemap_index.encode(self)?; + line_lo.encode(self)?; + col_lo.encode(self)?; + len.encode(self)?; + + if span_data.ctxt == SyntaxContext::empty() { + TAG_NO_EXPANSION_INFO.encode(self) + } else { + let mark = span_data.ctxt.outer(); + + if let Some(expn_info) = mark.expn_info() { + if let Some(pos) = self.expn_info_shorthands.get(&mark).cloned() { + TAG_EXPANSION_INFO_SHORTHAND.encode(self)?; + pos.encode(self) + } else { + TAG_EXPANSION_INFO_INLINE.encode(self)?; + let pos = AbsoluteBytePos::new(self.position()); + self.expn_info_shorthands.insert(mark, pos); + expn_info.encode(self) + } + } else { + TAG_NO_EXPANSION_INFO.encode(self) + } + } + } +} + +impl<'enc, 'a, 'tcx, E> ty_codec::TyEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn position(&self) -> usize { + self.encoder.position() + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, cnum: &CrateNum) -> Result<(), Self::Error> { + self.emit_u32(cnum.as_u32()) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder> for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, ty: &ty::Ty<'tcx>) -> Result<(), Self::Error> { + ty_codec::encode_with_shorthand(self, ty, + |encoder| &mut encoder.type_shorthands) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder> + for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, + predicates: &ty::GenericPredicates<'tcx>) + -> Result<(), Self::Error> { + ty_codec::encode_predicates(self, predicates, + |encoder| &mut encoder.predicate_shorthands) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, id: &hir::HirId) -> Result<(), Self::Error> { + let hir::HirId { + owner, + local_id, + } = *id; + + let def_path_hash = self.tcx.hir.definitions().def_path_hash(owner); + + def_path_hash.encode(self)?; + local_id.encode(self) + } +} + + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, id: &DefId) -> Result<(), Self::Error> { + let def_path_hash = self.tcx.def_path_hash(*id); + def_path_hash.encode(self) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, id: &LocalDefId) -> Result<(), Self::Error> { + id.to_def_id().encode(self) + } +} + +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + fn specialized_encode(&mut self, _: &DefIndex) -> Result<(), Self::Error> { + bug!("Encoding DefIndex without context.") + } +} + +// NodeIds are not stable across compilation sessions, so we store them in their +// HirId representation. This allows use to map them to the current NodeId. +impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + #[inline] + fn specialized_encode(&mut self, node_id: &NodeId) -> Result<(), Self::Error> { + let hir_id = self.tcx.hir.node_to_hir_id(*node_id); + hir_id.encode(self) + } +} + +impl<'enc, 'a, 'tcx, E, T> SpecializedEncoder> +for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder, + T: Encodable, +{ + #[inline] + fn specialized_encode(&mut self, + val: &mir::ClearCrossCrate) + -> Result<(), Self::Error> { + match *val { + mir::ClearCrossCrate::Clear => { + TAG_CLEAR_CROSS_CRATE_CLEAR.encode(self) + } + mir::ClearCrossCrate::Set(ref val) => { + TAG_CLEAR_CROSS_CRATE_SET.encode(self)?; + val.encode(self) + } + } + } +} + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + $(fn $name(&mut self, value: $ty) -> Result<(), Self::Error> { + self.encoder.$name(value) + })* + } +} + +impl<'enc, 'a, 'tcx, E> Encoder for CacheEncoder<'enc, 'a, 'tcx, E> + where E: 'enc + ty_codec::TyEncoder +{ + type Error = E::Error; + + fn emit_nil(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + emit_i8(i8); + + emit_bool(bool); + emit_f64(f64); + emit_f32(f32); + emit_char(char); + emit_str(&str); + } +} + +// An integer that will always encode to 8 bytes. +struct IntEncodedWithFixedSize(u64); + +impl IntEncodedWithFixedSize { + pub const ENCODED_SIZE: usize = 8; +} + +impl UseSpecializedEncodable for IntEncodedWithFixedSize {} +impl UseSpecializedDecodable for IntEncodedWithFixedSize {} + +impl<'enc> SpecializedEncoder for opaque::Encoder<'enc> { + fn specialized_encode(&mut self, x: &IntEncodedWithFixedSize) -> Result<(), Self::Error> { + let start_pos = self.position(); + for i in 0 .. IntEncodedWithFixedSize::ENCODED_SIZE { + ((x.0 >> i * 8) as u8).encode(self)?; + } + let end_pos = self.position(); + assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl<'enc> SpecializedDecoder for opaque::Decoder<'enc> { + fn specialized_decode(&mut self) -> Result { + let mut value: u64 = 0; + let start_pos = self.position(); + + for i in 0 .. IntEncodedWithFixedSize::ENCODED_SIZE { + let byte: u8 = Decodable::decode(self)?; + value |= (byte as u64) << (i * 8); + } + + let end_pos = self.position(); + assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + + Ok(IntEncodedWithFixedSize(value)) + } +} + +fn encode_query_results<'enc, 'a, 'tcx, Q, E>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + encoder: &mut CacheEncoder<'enc, 'a, 'tcx, E>, + query_result_index: &mut EncodedQueryResultIndex) + -> Result<(), E::Error> + where Q: super::plumbing::GetCacheInternal<'tcx>, + E: 'enc + TyEncoder, + Q::Value: Encodable, +{ + for (key, entry) in Q::get_cache_internal(tcx).map.iter() { + if Q::cache_on_disk(key.clone()) { + let dep_node = SerializedDepNodeIndex::new(entry.index.index()); + + // Record position of the cache entry + query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position()))); + + // Encode the type check tables with the SerializedDepNodeIndex + // as tag. + encoder.encode_tagged(dep_node, &entry.value)?; + } + } + + Ok(()) +} diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 581f47dc13cdf..fdaa13e7fd16f 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -12,37 +12,42 @@ //! that generate the actual methods on tcx which find and execute the //! provider, manage the caches, and so forth. -use dep_graph::{DepNodeIndex}; -use errors::{Diagnostic, DiagnosticBuilder}; +use dep_graph::{DepNodeIndex, DepNode, DepKind, DepNodeColor}; +use errors::DiagnosticBuilder; use ty::{TyCtxt}; use ty::maps::Query; // NB: actually generated by the macros in this file use ty::maps::config::QueryDescription; use ty::item_path; use rustc_data_structures::fx::{FxHashMap}; -use std::cell::{RefMut, Cell}; +use std::cell::{Ref, RefMut}; use std::marker::PhantomData; use std::mem; use syntax_pos::Span; -pub(super) struct QueryMap { - phantom: PhantomData, +pub(super) struct QueryMap<'tcx, D: QueryDescription<'tcx>> { + phantom: PhantomData<(D, &'tcx ())>, pub(super) map: FxHashMap>, } pub(super) struct QueryValue { pub(super) value: T, pub(super) index: DepNodeIndex, - pub(super) diagnostics: Option>, } -pub(super) struct QueryDiagnostics { - pub(super) diagnostics: Vec, - pub(super) emitted_diagnostics: Cell, +impl QueryValue { + pub(super) fn new(value: T, + dep_node_index: DepNodeIndex) + -> QueryValue { + QueryValue { + value, + index: dep_node_index, + } + } } -impl QueryMap { - pub(super) fn new() -> QueryMap { +impl<'tcx, M: QueryDescription<'tcx>> QueryMap<'tcx, M> { + pub(super) fn new() -> QueryMap<'tcx, M> { QueryMap { phantom: PhantomData, map: FxHashMap(), @@ -50,6 +55,11 @@ impl QueryMap { } } +pub(super) trait GetCacheInternal<'tcx>: QueryDescription<'tcx> + Sized { + fn get_cache_internal<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> Ref<'a, QueryMap<'tcx, Self>>; +} + pub(super) struct CycleError<'a, 'tcx: 'a> { span: Span, cycle: RefMut<'a, [(Span, Query<'tcx>)]>, @@ -71,17 +81,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // (And cycle errors around impls tend to occur during the // collect/coherence phases anyhow.) item_path::with_forced_impl_filename_line(|| { + let span = self.sess.codemap().def_span(span); let mut err = struct_span_err!(self.sess, span, E0391, "unsupported cyclic reference between types/traits detected"); err.span_label(span, "cyclic reference"); - err.span_note(stack[0].0, &format!("the cycle begins when {}...", - stack[0].1.describe(self))); + err.span_note(self.sess.codemap().def_span(stack[0].0), + &format!("the cycle begins when {}...", stack[0].1.describe(self))); for &(span, ref query) in &stack[1..] { - err.span_note(span, &format!("...which then requires {}...", - query.describe(self))); + err.span_note(self.sess.codemap().def_span(span), + &format!("...which then requires {}...", query.describe(self))); } err.note(&format!("...which then again requires {}, completing the cycle.", @@ -113,6 +124,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Ok(result) } + + /// Try to read a node index for the node dep_node. + /// A node will have an index, when it's already been marked green, or when we can mark it + /// green. This function will mark the current task as a reader of the specified node, when + /// the a node index can be found for that node. + pub(super) fn try_mark_green_and_read(self, dep_node: &DepNode) -> Option { + match self.dep_graph.node_color(dep_node) { + Some(DepNodeColor::Green(dep_node_index)) => { + self.dep_graph.read_index(dep_node_index); + Some(dep_node_index) + } + Some(DepNodeColor::Red) => { + None + } + None => { + // try_mark_green (called below) will panic when full incremental + // compilation is disabled. If that's the case, we can't try to mark nodes + // as green anyway, so we can safely return None here. + if !self.dep_graph.is_fully_enabled() { + return None; + } + match self.dep_graph.try_mark_green(self.global_tcx(), &dep_node) { + Some(dep_node_index) => { + debug_assert!(self.dep_graph.is_green(dep_node_index)); + self.dep_graph.read_index(dep_node_index); + Some(dep_node_index) + } + None => { + None + } + } + } + } + } } // If enabled, send a message to the profile-queries thread @@ -142,6 +187,10 @@ macro_rules! define_maps { (<$tcx:tt> $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*) => { + + use dep_graph::DepNodeIndex; + use std::cell::RefCell; + define_map_struct! { tcx: $tcx, input: ($(([$($modifiers)*] [$($attr)*] [$name]))*) @@ -199,7 +248,15 @@ macro_rules! define_maps { type Value = $V; } + impl<$tcx> GetCacheInternal<$tcx> for queries::$name<$tcx> { + fn get_cache_internal<'a>(tcx: TyCtxt<'a, $tcx, $tcx>) + -> ::std::cell::Ref<'a, QueryMap<$tcx, Self>> { + tcx.maps.$name.borrow() + } + } + impl<'a, $tcx, 'lcx> queries::$name<$tcx> { + #[allow(unused)] fn to_dep_node(tcx: TyCtxt<'a, $tcx, 'lcx>, key: &$K) -> DepNode { use dep_graph::DepConstructor::*; @@ -207,12 +264,10 @@ macro_rules! define_maps { DepNode::new(tcx, $node(*key)) } - fn try_get_with(tcx: TyCtxt<'a, $tcx, 'lcx>, - mut span: Span, - key: $K, - f: F) - -> Result> - where F: FnOnce(&$V) -> R + fn try_get_with(tcx: TyCtxt<'a, $tcx, 'lcx>, + mut span: Span, + key: $K) + -> Result<$V, CycleError<'a, $tcx>> { debug!("ty::queries::{}::try_get_with(key={:?}, span={:?})", stringify!($name), @@ -227,22 +282,10 @@ macro_rules! define_maps { ); if let Some(value) = tcx.maps.$name.borrow().map.get(&key) { - if let Some(ref d) = value.diagnostics { - if !d.emitted_diagnostics.get() { - d.emitted_diagnostics.set(true); - let handle = tcx.sess.diagnostic(); - for diagnostic in d.diagnostics.iter() { - DiagnosticBuilder::new_diagnostic(handle, diagnostic.clone()) - .emit(); - } - } - } profq_msg!(tcx, ProfileQueriesMsg::CacheHit); tcx.dep_graph.read_index(value.index); - return Ok(f(&value.value)); + return Ok((&value.value).clone()); } - // else, we are going to run the provider: - profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); // FIXME(eddyb) Get more valid Span's on queries. // def_span guard is necessary to prevent a recursive loop, @@ -251,70 +294,240 @@ macro_rules! define_maps { span = key.default_span(tcx) } + // Fast path for when incr. comp. is off. `to_dep_node` is + // expensive for some DepKinds. + if !tcx.dep_graph.is_fully_enabled() { + let null_dep_node = DepNode::new_no_params(::dep_graph::DepKind::Null); + return Self::force(tcx, key, span, null_dep_node) + .map(|(v, _)| v); + } + let dep_node = Self::to_dep_node(tcx, &key); - let res = tcx.cycle_check(span, Query::$name(key), || { - tcx.sess.diagnostic().track_diagnostics(|| { - if dep_node.kind.is_anon() { + + if dep_node.kind.is_anon() { + profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); + + let res = tcx.cycle_check(span, Query::$name(key), || { + tcx.sess.diagnostic().track_diagnostics(|| { tcx.dep_graph.with_anon_task(dep_node.kind, || { - let provider = tcx.maps.providers[key.map_crate()].$name; - provider(tcx.global_tcx(), key) + Self::compute_result(tcx.global_tcx(), key) }) + }) + })?; + + profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd); + let ((result, dep_node_index), diagnostics) = res; + + tcx.dep_graph.read_index(dep_node_index); + + tcx.on_disk_query_result_cache + .store_diagnostics_for_anon_node(dep_node_index, diagnostics); + + let value = QueryValue::new(result, dep_node_index); + + return Ok((&tcx.maps + .$name + .borrow_mut() + .map + .entry(key) + .or_insert(value) + .value).clone()); + } + + if !dep_node.kind.is_input() { + if let Some(dep_node_index) = tcx.try_mark_green_and_read(&dep_node) { + profq_msg!(tcx, ProfileQueriesMsg::CacheHit); + return Self::load_from_disk_and_cache_in_memory(tcx, + key, + span, + dep_node_index, + &dep_node) + } + } + + match Self::force(tcx, key, span, dep_node) { + Ok((result, dep_node_index)) => { + tcx.dep_graph.read_index(dep_node_index); + Ok(result) + } + Err(e) => Err(e) + } + } + + /// Ensure that either this query has all green inputs or been executed. + /// Executing query::ensure(D) is considered a read of the dep-node D. + /// + /// This function is particularly useful when executing passes for their + /// side-effects -- e.g., in order to report errors for erroneous programs. + /// + /// Note: The optimization is only available during incr. comp. + pub fn ensure(tcx: TyCtxt<'a, $tcx, 'lcx>, key: $K) -> () { + let dep_node = Self::to_dep_node(tcx, &key); + + // Ensuring an "input" or anonymous query makes no sense + assert!(!dep_node.kind.is_anon()); + assert!(!dep_node.kind.is_input()); + if tcx.try_mark_green_and_read(&dep_node).is_none() { + // A None return from `try_mark_green_and_read` means that this is either + // a new dep node or that the dep node has already been marked red. + // Either way, we can't call `dep_graph.read()` as we don't have the + // DepNodeIndex. We must invoke the query itself. The performance cost + // this introduces should be negligible as we'll immediately hit the + // in-memory cache, or another query down the line will. + let _ = tcx.$name(key); + } + } + + fn compute_result(tcx: TyCtxt<'a, $tcx, 'lcx>, key: $K) -> $V { + let provider = tcx.maps.providers[key.map_crate()].$name; + provider(tcx.global_tcx(), key) + } + + fn load_from_disk_and_cache_in_memory(tcx: TyCtxt<'a, $tcx, 'lcx>, + key: $K, + span: Span, + dep_node_index: DepNodeIndex, + dep_node: &DepNode) + -> Result<$V, CycleError<'a, $tcx>> + { + debug_assert!(tcx.dep_graph.is_green(dep_node_index)); + + // First we try to load the result from the on-disk cache + let result = if Self::cache_on_disk(key) && + tcx.sess.opts.debugging_opts.incremental_queries { + let prev_dep_node_index = + tcx.dep_graph.prev_dep_node_index_of(dep_node); + let result = Self::try_load_from_disk(tcx.global_tcx(), + prev_dep_node_index); + + // We always expect to find a cached result for things that + // can be forced from DepNode. + debug_assert!(!dep_node.kind.can_reconstruct_query_key() || + result.is_some(), + "Missing on-disk cache entry for {:?}", + dep_node); + result + } else { + // Some things are never cached on disk. + None + }; + + let result = if let Some(result) = result { + result + } else { + // We could not load a result from the on-disk cache, so + // recompute. + let (result, _ ) = tcx.cycle_check(span, Query::$name(key), || { + // The diagnostics for this query have already been + // promoted to the current session during + // try_mark_green(), so we can ignore them here. + tcx.sess.diagnostic().track_diagnostics(|| { + // The dep-graph for this computation is already in + // place + tcx.dep_graph.with_ignore(|| { + Self::compute_result(tcx, key) + }) + }) + })?; + result + }; + + // If -Zincremental-verify-ich is specified, re-hash results from + // the cache and make sure that they have the expected fingerprint. + if tcx.sess.opts.debugging_opts.incremental_verify_ich { + use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; + use ich::Fingerprint; + + assert!(Some(tcx.dep_graph.fingerprint_of(dep_node)) == + tcx.dep_graph.prev_fingerprint_of(dep_node), + "Fingerprint for green query instance not loaded \ + from cache: {:?}", dep_node); + + debug!("BEGIN verify_ich({:?})", dep_node); + let mut hcx = tcx.create_stable_hashing_context(); + let mut hasher = StableHasher::new(); + + result.hash_stable(&mut hcx, &mut hasher); + + let new_hash: Fingerprint = hasher.finish(); + debug!("END verify_ich({:?})", dep_node); + + let old_hash = tcx.dep_graph.fingerprint_of(dep_node); + + assert!(new_hash == old_hash, "Found unstable fingerprints \ + for {:?}", dep_node); + } + + if tcx.sess.opts.debugging_opts.query_dep_graph { + tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); + } + + let value = QueryValue::new(result, dep_node_index); + + Ok((&tcx.maps + .$name + .borrow_mut() + .map + .entry(key) + .or_insert(value) + .value).clone()) + } + + fn force(tcx: TyCtxt<'a, $tcx, 'lcx>, + key: $K, + span: Span, + dep_node: DepNode) + -> Result<($V, DepNodeIndex), CycleError<'a, $tcx>> { + debug_assert!(tcx.dep_graph.node_color(&dep_node).is_none()); + + profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); + let res = tcx.cycle_check(span, Query::$name(key), || { + tcx.sess.diagnostic().track_diagnostics(|| { + if dep_node.kind.is_eval_always() { + tcx.dep_graph.with_eval_always_task(dep_node, + tcx, + key, + Self::compute_result) } else { - fn run_provider<'a, 'tcx, 'lcx>(tcx: TyCtxt<'a, 'tcx, 'lcx>, - key: $K) - -> $V { - let provider = tcx.maps.providers[key.map_crate()].$name; - provider(tcx.global_tcx(), key) - } - - tcx.dep_graph.with_task(dep_node, tcx, key, run_provider) + tcx.dep_graph.with_task(dep_node, + tcx, + key, + Self::compute_result) } }) })?; profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd); + let ((result, dep_node_index), diagnostics) = res; - tcx.dep_graph.read_index(dep_node_index); + if tcx.sess.opts.debugging_opts.query_dep_graph { + tcx.dep_graph.mark_loaded_from_cache(dep_node_index, false); + } - let value = QueryValue { - value: result, - index: dep_node_index, - diagnostics: if diagnostics.len() == 0 { - None - } else { - Some(Box::new(QueryDiagnostics { - diagnostics, - emitted_diagnostics: Cell::new(true), - })) - }, - }; + if dep_node.kind != ::dep_graph::DepKind::Null { + tcx.on_disk_query_result_cache + .store_diagnostics(dep_node_index, diagnostics); + } + + let value = QueryValue::new(result, dep_node_index); - Ok(f(&tcx.maps + Ok(((&tcx.maps .$name .borrow_mut() .map .entry(key) .or_insert(value) - .value)) + .value).clone(), + dep_node_index)) } pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) -> Result<$V, DiagnosticBuilder<'a>> { - match Self::try_get_with(tcx, span, key, Clone::clone) { + match Self::try_get_with(tcx, span, key) { Ok(e) => Ok(e), Err(e) => Err(tcx.report_cycle(e)), } } - - pub fn force(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) { - // Ignore dependencies, since we not reading the computed value - let _task = tcx.dep_graph.in_ignore(); - - match Self::try_get_with(tcx, span, key, |_| ()) { - Ok(()) => {} - Err(e) => tcx.report_cycle(e).emit(), - } - } })* #[derive(Copy, Clone)] @@ -358,8 +571,7 @@ macro_rules! define_maps { define_provider_struct! { tcx: $tcx, - input: ($(([$($modifiers)*] [$name] [$K] [$V]))*), - output: () + input: ($(([$($modifiers)*] [$name] [$K] [$V]))*) } impl<$tcx> Copy for Providers<$tcx> {} @@ -370,78 +582,19 @@ macro_rules! define_maps { } macro_rules! define_map_struct { - // Initial state - (tcx: $tcx:tt, - input: $input:tt) => { - define_map_struct! { - tcx: $tcx, - input: $input, - output: () - } - }; - - // Final output (tcx: $tcx:tt, - input: (), - output: ($($output:tt)*)) => { + input: ($(([$(modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { pub struct Maps<$tcx> { providers: IndexVec>, query_stack: RefCell)>>, - $($output)* - } - }; - - // Field recognized and ready to shift into the output - (tcx: $tcx:tt, - ready: ([$($pub:tt)*] [$($attr:tt)*] [$name:ident]), - input: $input:tt, - output: ($($output:tt)*)) => { - define_map_struct! { - tcx: $tcx, - input: $input, - output: ($($output)* - $(#[$attr])* $($pub)* $name: RefCell>>,) - } - }; - - // No modifiers left? This is a private item. - (tcx: $tcx:tt, - input: (([] $attrs:tt $name:tt) $($input:tt)*), - output: $output:tt) => { - define_map_struct! { - tcx: $tcx, - ready: ([] $attrs $name), - input: ($($input)*), - output: $output - } - }; - - // Skip other modifiers - (tcx: $tcx:tt, - input: (([$other_modifier:tt $($modifiers:tt)*] $($fields:tt)*) $($input:tt)*), - output: $output:tt) => { - define_map_struct! { - tcx: $tcx, - input: (([$($modifiers)*] $($fields)*) $($input)*), - output: $output + $($(#[$attr])* $name: RefCell>>,)* } }; } macro_rules! define_provider_struct { - // Initial state: - (tcx: $tcx:tt, input: $input:tt) => { - define_provider_struct! { - tcx: $tcx, - input: $input, - output: () - } - }; - - // Final state: (tcx: $tcx:tt, - input: (), - output: ($(([$name:ident] [$K:ty] [$R:ty]))*)) => { + input: ($(([$($modifiers:tt)*] [$name:ident] [$K:ty] [$R:ty]))*)) => { pub struct Providers<$tcx> { $(pub $name: for<'a> fn(TyCtxt<'a, $tcx, $tcx>, $K) -> $R,)* } @@ -456,39 +609,369 @@ macro_rules! define_provider_struct { } } }; +} - // Something ready to shift: - (tcx: $tcx:tt, - ready: ($name:tt $K:tt $V:tt), - input: $input:tt, - output: ($($output:tt)*)) => { - define_provider_struct! { - tcx: $tcx, - input: $input, - output: ($($output)* ($name $K $V)) + +/// The red/green evaluation system will try to mark a specific DepNode in the +/// dependency graph as green by recursively trying to mark the dependencies of +/// that DepNode as green. While doing so, it will sometimes encounter a DepNode +/// where we don't know if it is red or green and we therefore actually have +/// to recompute its value in order to find out. Since the only piece of +/// information that we have at that point is the DepNode we are trying to +/// re-evaluate, we need some way to re-run a query from just that. This is what +/// `force_from_dep_node()` implements. +/// +/// In the general case, a DepNode consists of a DepKind and an opaque +/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint +/// is usually constructed by computing a stable hash of the query-key that the +/// DepNode corresponds to. Consequently, it is not in general possible to go +/// back from hash to query-key (since hash functions are not reversible). For +/// this reason `force_from_dep_node()` is expected to fail from time to time +/// because we just cannot find out, from the DepNode alone, what the +/// corresponding query-key is and therefore cannot re-run the query. +/// +/// The system deals with this case letting `try_mark_green` fail which forces +/// the root query to be re-evaluated. +/// +/// Now, if force_from_dep_node() would always fail, it would be pretty useless. +/// Fortunately, we can use some contextual information that will allow us to +/// reconstruct query-keys for certain kinds of DepNodes. In particular, we +/// enforce by construction that the GUID/fingerprint of certain DepNodes is a +/// valid DefPathHash. Since we also always build a huge table that maps every +/// DefPathHash in the current codebase to the corresponding DefId, we have +/// everything we need to re-run the query. +/// +/// Take the `mir_validated` query as an example. Like many other queries, it +/// just has a single parameter: the DefId of the item it will compute the +/// validated MIR for. Now, when we call `force_from_dep_node()` on a dep-node +/// with kind `MirValidated`, we know that the GUID/fingerprint of the dep-node +/// is actually a DefPathHash, and can therefore just look up the corresponding +/// DefId in `tcx.def_path_hash_to_def_id`. +/// +/// When you implement a new query, it will likely have a corresponding new +/// DepKind, and you'll have to support it here in `force_from_dep_node()`. As +/// a rule of thumb, if your query takes a DefId or DefIndex as sole parameter, +/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just +/// add it to the "We don't have enough information to reconstruct..." group in +/// the match below. +pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, + dep_node: &DepNode) + -> bool { + use ty::maps::keys::Key; + use hir::def_id::LOCAL_CRATE; + + // We must avoid ever having to call force_from_dep_node() for a + // DepNode::CodegenUnit: + // Since we cannot reconstruct the query key of a DepNode::CodegenUnit, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-translating the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!(dep_node.kind != DepKind::CodegenUnit, + "calling force_from_dep_node() on DepKind::CodegenUnit"); + + if !dep_node.kind.can_reconstruct_query_key() { + return false + } + + macro_rules! def_id { + () => { + if let Some(def_id) = dep_node.extract_def_id(tcx) { + def_id + } else { + // return from the whole function + return false + } } }; - // Regular queries produce a `V` only. - (tcx: $tcx:tt, - input: (([] $name:tt $K:tt $V:tt) $($input:tt)*), - output: $output:tt) => { - define_provider_struct! { - tcx: $tcx, - ready: ($name $K $V), - input: ($($input)*), - output: $output - } + macro_rules! krate { + () => { (def_id!()).krate } }; - // Skip modifiers. - (tcx: $tcx:tt, - input: (([$other_modifier:tt $($modifiers:tt)*] $($fields:tt)*) $($input:tt)*), - output: $output:tt) => { - define_provider_struct! { - tcx: $tcx, - input: (([$($modifiers)*] $($fields)*) $($input)*), - output: $output + macro_rules! force { + ($query:ident, $key:expr) => { + { + use $crate::util::common::{ProfileQueriesMsg, profq_msg}; + + // FIXME(eddyb) Get more valid Span's on queries. + // def_span guard is necessary to prevent a recursive loop, + // default_span calls def_span query internally. + let span = if stringify!($query) != "def_span" { + $key.default_span(tcx) + } else { + ::syntax_pos::DUMMY_SP + }; + + profq_msg!(tcx, + ProfileQueriesMsg::QueryBegin( + span.data(), + ::ty::maps::QueryMsg::$query(profq_key!(tcx, $key)) + ) + ); + + match ::ty::maps::queries::$query::force(tcx, $key, span, *dep_node) { + Ok(_) => {}, + Err(e) => { + tcx.report_cycle(e).emit(); + } + } + } } }; + + // FIXME(#45015): We should try move this boilerplate code into a macro + // somehow. + match dep_node.kind { + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already + DepKind::AllLocalTraitImpls | + DepKind::Krate | + DepKind::CrateMetadata | + DepKind::HirBody | + DepKind::Hir | + + // This are anonymous nodes + DepKind::TraitSelect | + + // We don't have enough information to reconstruct the query key of + // these + DepKind::IsCopy | + DepKind::IsSized | + DepKind::IsFreeze | + DepKind::NeedsDrop | + DepKind::Layout | + DepKind::ConstEval | + DepKind::InstanceSymbolName | + DepKind::MirShim | + DepKind::BorrowCheckKrate | + DepKind::Specializes | + DepKind::ImplementationsOfTrait | + DepKind::TypeParamPredicates | + DepKind::CodegenUnit | + DepKind::CompileCodegenUnit | + DepKind::FulfillObligation | + DepKind::VtableMethods | + DepKind::EraseRegionsTy | + DepKind::NormalizeTy | + + // This one should never occur in this context + DepKind::Null => { + bug!("force_from_dep_node() - Encountered {:?}", dep_node) + } + + // These are not queries + DepKind::CoherenceCheckTrait | + DepKind::ItemVarianceConstraints => { + return false + } + + DepKind::RegionScopeTree => { force!(region_scope_tree, def_id!()); } + + DepKind::Coherence => { force!(crate_inherent_impls, LOCAL_CRATE); } + DepKind::CoherenceInherentImplOverlapCheck => { + force!(crate_inherent_impls_overlap_check, LOCAL_CRATE) + }, + DepKind::PrivacyAccessLevels => { force!(privacy_access_levels, LOCAL_CRATE); } + DepKind::MirBuilt => { force!(mir_built, def_id!()); } + DepKind::MirConstQualif => { force!(mir_const_qualif, def_id!()); } + DepKind::MirConst => { force!(mir_const, def_id!()); } + DepKind::MirValidated => { force!(mir_validated, def_id!()); } + DepKind::MirOptimized => { force!(optimized_mir, def_id!()); } + + DepKind::BorrowCheck => { force!(borrowck, def_id!()); } + DepKind::MirBorrowCheck => { force!(mir_borrowck, def_id!()); } + DepKind::UnsafetyCheckResult => { force!(unsafety_check_result, def_id!()); } + DepKind::UnsafeDeriveOnReprPacked => { force!(unsafe_derive_on_repr_packed, def_id!()); } + DepKind::Reachability => { force!(reachable_set, LOCAL_CRATE); } + DepKind::MirKeys => { force!(mir_keys, LOCAL_CRATE); } + DepKind::CrateVariances => { force!(crate_variances, LOCAL_CRATE); } + DepKind::AssociatedItems => { force!(associated_item, def_id!()); } + DepKind::TypeOfItem => { force!(type_of, def_id!()); } + DepKind::GenericsOfItem => { force!(generics_of, def_id!()); } + DepKind::PredicatesOfItem => { force!(predicates_of, def_id!()); } + DepKind::InferredOutlivesOf => { force!(inferred_outlives_of, def_id!()); } + DepKind::SuperPredicatesOfItem => { force!(super_predicates_of, def_id!()); } + DepKind::TraitDefOfItem => { force!(trait_def, def_id!()); } + DepKind::AdtDefOfItem => { force!(adt_def, def_id!()); } + DepKind::IsAutoImpl => { force!(is_auto_impl, def_id!()); } + DepKind::ImplTraitRef => { force!(impl_trait_ref, def_id!()); } + DepKind::ImplPolarity => { force!(impl_polarity, def_id!()); } + DepKind::FnSignature => { force!(fn_sig, def_id!()); } + DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); } + DepKind::ItemVariances => { force!(variances_of, def_id!()); } + DepKind::IsConstFn => { force!(is_const_fn, def_id!()); } + DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); } + DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); } + DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); } + DepKind::AdtDestructor => { force!(adt_destructor, def_id!()); } + DepKind::AssociatedItemDefIds => { force!(associated_item_def_ids, def_id!()); } + DepKind::InherentImpls => { force!(inherent_impls, def_id!()); } + DepKind::TypeckBodiesKrate => { force!(typeck_item_bodies, LOCAL_CRATE); } + DepKind::TypeckTables => { force!(typeck_tables_of, def_id!()); } + DepKind::UsedTraitImports => { force!(used_trait_imports, def_id!()); } + DepKind::HasTypeckTables => { force!(has_typeck_tables, def_id!()); } + DepKind::SymbolName => { force!(def_symbol_name, def_id!()); } + DepKind::SpecializationGraph => { force!(specialization_graph_of, def_id!()); } + DepKind::ObjectSafety => { force!(is_object_safe, def_id!()); } + DepKind::TraitImpls => { force!(trait_impls_of, def_id!()); } + DepKind::CheckMatch => { force!(check_match, def_id!()); } + + DepKind::ParamEnv => { force!(param_env, def_id!()); } + DepKind::DescribeDef => { force!(describe_def, def_id!()); } + DepKind::DefSpan => { force!(def_span, def_id!()); } + DepKind::LookupStability => { force!(lookup_stability, def_id!()); } + DepKind::LookupDeprecationEntry => { + force!(lookup_deprecation_entry, def_id!()); + } + DepKind::ItemBodyNestedBodies => { force!(item_body_nested_bodies, def_id!()); } + DepKind::ConstIsRvaluePromotableToStatic => { + force!(const_is_rvalue_promotable_to_static, def_id!()); + } + DepKind::RvaluePromotableMap => { force!(rvalue_promotable_map, def_id!()); } + DepKind::ImplParent => { force!(impl_parent, def_id!()); } + DepKind::TraitOfItem => { force!(trait_of_item, def_id!()); } + DepKind::IsExportedSymbol => { force!(is_exported_symbol, def_id!()); } + DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); } + DepKind::ItemAttrs => { force!(item_attrs, def_id!()); } + DepKind::FnArgNames => { force!(fn_arg_names, def_id!()); } + DepKind::DylibDepFormats => { force!(dylib_dependency_formats, krate!()); } + DepKind::IsPanicRuntime => { force!(is_panic_runtime, krate!()); } + DepKind::IsCompilerBuiltins => { force!(is_compiler_builtins, krate!()); } + DepKind::HasGlobalAllocator => { force!(has_global_allocator, krate!()); } + DepKind::ExternCrate => { force!(extern_crate, def_id!()); } + DepKind::LintLevels => { force!(lint_levels, LOCAL_CRATE); } + DepKind::InScopeTraits => { force!(in_scope_traits_map, def_id!().index); } + DepKind::ModuleExports => { force!(module_exports, def_id!()); } + DepKind::IsSanitizerRuntime => { force!(is_sanitizer_runtime, krate!()); } + DepKind::IsProfilerRuntime => { force!(is_profiler_runtime, krate!()); } + DepKind::GetPanicStrategy => { force!(panic_strategy, krate!()); } + DepKind::IsNoBuiltins => { force!(is_no_builtins, krate!()); } + DepKind::ImplDefaultness => { force!(impl_defaultness, def_id!()); } + DepKind::ExportedSymbolIds => { force!(exported_symbol_ids, krate!()); } + DepKind::NativeLibraries => { force!(native_libraries, krate!()); } + DepKind::PluginRegistrarFn => { force!(plugin_registrar_fn, krate!()); } + DepKind::DeriveRegistrarFn => { force!(derive_registrar_fn, krate!()); } + DepKind::CrateDisambiguator => { force!(crate_disambiguator, krate!()); } + DepKind::CrateHash => { force!(crate_hash, krate!()); } + DepKind::OriginalCrateName => { force!(original_crate_name, krate!()); } + + DepKind::AllTraitImplementations => { + force!(all_trait_implementations, krate!()); + } + + DepKind::IsDllimportForeignItem => { + force!(is_dllimport_foreign_item, def_id!()); + } + DepKind::IsStaticallyIncludedForeignItem => { + force!(is_statically_included_foreign_item, def_id!()); + } + DepKind::NativeLibraryKind => { force!(native_library_kind, def_id!()); } + DepKind::LinkArgs => { force!(link_args, LOCAL_CRATE); } + + DepKind::NamedRegion => { force!(named_region_map, def_id!().index); } + DepKind::IsLateBound => { force!(is_late_bound_map, def_id!().index); } + DepKind::ObjectLifetimeDefaults => { + force!(object_lifetime_defaults_map, def_id!().index); + } + + DepKind::Visibility => { force!(visibility, def_id!()); } + DepKind::DepKind => { force!(dep_kind, krate!()); } + DepKind::CrateName => { force!(crate_name, krate!()); } + DepKind::ItemChildren => { force!(item_children, def_id!()); } + DepKind::ExternModStmtCnum => { force!(extern_mod_stmt_cnum, def_id!()); } + DepKind::GetLangItems => { force!(get_lang_items, LOCAL_CRATE); } + DepKind::DefinedLangItems => { force!(defined_lang_items, krate!()); } + DepKind::MissingLangItems => { force!(missing_lang_items, krate!()); } + DepKind::ExternConstBody => { force!(extern_const_body, def_id!()); } + DepKind::VisibleParentMap => { force!(visible_parent_map, LOCAL_CRATE); } + DepKind::MissingExternCrateItem => { + force!(missing_extern_crate_item, krate!()); + } + DepKind::UsedCrateSource => { force!(used_crate_source, krate!()); } + DepKind::PostorderCnums => { force!(postorder_cnums, LOCAL_CRATE); } + DepKind::HasCloneClosures => { force!(has_clone_closures, krate!()); } + DepKind::HasCopyClosures => { force!(has_copy_closures, krate!()); } + + DepKind::Freevars => { force!(freevars, def_id!()); } + DepKind::MaybeUnusedTraitImport => { + force!(maybe_unused_trait_import, def_id!()); + } + DepKind::MaybeUnusedExternCrates => { force!(maybe_unused_extern_crates, LOCAL_CRATE); } + DepKind::StabilityIndex => { force!(stability_index, LOCAL_CRATE); } + DepKind::AllCrateNums => { force!(all_crate_nums, LOCAL_CRATE); } + DepKind::ExportedSymbols => { force!(exported_symbols, krate!()); } + DepKind::CollectAndPartitionTranslationItems => { + force!(collect_and_partition_translation_items, LOCAL_CRATE); + } + DepKind::ExportName => { force!(export_name, def_id!()); } + DepKind::ContainsExternIndicator => { + force!(contains_extern_indicator, def_id!()); + } + DepKind::IsTranslatedFunction => { force!(is_translated_function, def_id!()); } + DepKind::OutputFilenames => { force!(output_filenames, LOCAL_CRATE); } + } + + true } + + +// FIXME(#45015): Another piece of boilerplate code that could be generated in +// a combined define_dep_nodes!()/define_maps!() macro. +macro_rules! impl_load_from_cache { + ($($dep_kind:ident => $query_name:ident,)*) => { + impl DepNode { + // Check whether the query invocation corresponding to the given + // DepNode is eligible for on-disk-caching. + pub fn cache_on_disk(&self, tcx: TyCtxt) -> bool { + use ty::maps::queries; + use ty::maps::QueryDescription; + + match self.kind { + $(DepKind::$dep_kind => { + let def_id = self.extract_def_id(tcx).unwrap(); + queries::$query_name::cache_on_disk(def_id) + })* + _ => false + } + } + + // This is method will execute the query corresponding to the given + // DepNode. It is only expected to work for DepNodes where the + // above `cache_on_disk` methods returns true. + // Also, as a sanity check, it expects that the corresponding query + // invocation has been marked as green already. + pub fn load_from_on_disk_cache(&self, tcx: TyCtxt) { + match self.kind { + $(DepKind::$dep_kind => { + debug_assert!(tcx.dep_graph + .node_color(self) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let def_id = self.extract_def_id(tcx).unwrap(); + let _ = tcx.$query_name(def_id); + })* + _ => { + bug!() + } + } + } + } + } +} + +impl_load_from_cache!( + TypeckTables => typeck_tables_of, + MirOptimized => optimized_mir, + UnsafetyCheckResult => unsafety_check_result, + BorrowCheck => borrowck, + MirBorrowCheck => mir_borrowck, + MirConstQualif => mir_const_qualif, +); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 2c1b3e28ffb00..afe999cede70d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -17,7 +17,8 @@ pub use self::fold::TypeFoldable; use hir::{map as hir_map, FreevarMap, TraitMap}; use hir::def::{Def, CtorKind, ExportMap}; -use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; +use hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use hir::map::DefPathData; use ich::StableHashingContext; use middle::const_val::ConstVal; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; @@ -25,6 +26,7 @@ use middle::privacy::AccessLevels; use middle::resolve_lifetime::ObjectLifetimeDefault; use mir::Mir; use mir::GeneratorLayout; +use session::CrateDisambiguator; use traits; use ty; use ty::subst::{Subst, Substs}; @@ -54,7 +56,6 @@ use rustc_const_math::ConstInt; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, HashStable}; -use rustc_data_structures::transitive_relation::TransitiveRelation; use hir; @@ -88,7 +89,10 @@ pub use self::maps::queries; pub mod adjustment; pub mod binding; pub mod cast; +#[macro_use] +pub mod codec; pub mod error; +mod erase_regions; pub mod fast_reject; pub mod fold; pub mod inhabitedness; @@ -141,6 +145,15 @@ pub enum AssociatedItemContainer { } impl AssociatedItemContainer { + /// Asserts that this is the def-id of an associated item declared + /// in a trait, and returns the trait def-id. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self) + } + } + pub fn id(&self) -> DefId { match *self { TraitContainer(id) => id, @@ -311,11 +324,6 @@ pub enum Variance { /// `tcx.variances_of()` to get the variance for a *particular* /// item. pub struct CrateVariancesMap { - /// This relation tracks the dependencies between the variance of - /// various items. In particular, if `a < b`, then the variance of - /// `a` depends on the sources of `b`. - pub dependencies: TransitiveRelation, - /// For each item with generics, maps to a vector of the variance /// of its generics. If an item has no generics, it will have no /// entry. @@ -575,7 +583,7 @@ impl Slice { #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct UpvarId { pub var_id: hir::HirId, - pub closure_expr_id: DefIndex, + pub closure_expr_id: LocalDefId, } #[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, Copy)] @@ -675,6 +683,8 @@ pub struct TypeParameterDef { /// on generic parameter `T`, asserts data behind the parameter /// `T` won't be accessed during the parent type's `Drop` impl. pub pure_wrt_drop: bool, + + pub synthetic: Option, } #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] @@ -711,6 +721,13 @@ impl ty::EarlyBoundRegion { /// Information about the formal type/lifetime parameters associated /// with an item or method. Analogous to hir::Generics. +/// +/// Note that in the presence of a `Self` parameter, the ordering here +/// is different from the ordering in a Substs. Substs are ordered as +/// Self, *Regions, *Other Type Params, (...child generics) +/// while this struct is ordered as +/// regions = Regions +/// types = [Self, *Other Type Params] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct Generics { pub parent: Option, @@ -727,7 +744,7 @@ pub struct Generics { pub has_late_bound_regions: Option, } -impl Generics { +impl<'a, 'gcx, 'tcx> Generics { pub fn parent_count(&self) -> usize { self.parent_regions as usize + self.parent_types as usize } @@ -740,14 +757,52 @@ impl Generics { self.parent_count() + self.own_count() } - pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef { - assert_eq!(self.parent_count(), 0); - &self.regions[param.index as usize - self.has_self as usize] - } - - pub fn type_param(&self, param: &ParamTy) -> &TypeParameterDef { - assert_eq!(self.parent_count(), 0); - &self.types[param.idx as usize - self.has_self as usize - self.regions.len()] + pub fn region_param(&'tcx self, + param: &EarlyBoundRegion, + tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> &'tcx RegionParameterDef + { + if let Some(index) = param.index.checked_sub(self.parent_count() as u32) { + &self.regions[index as usize - self.has_self as usize] + } else { + tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) + .region_param(param, tcx) + } + } + + /// Returns the `TypeParameterDef` associated with this `ParamTy`. + pub fn type_param(&'tcx self, + param: &ParamTy, + tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> &TypeParameterDef { + if let Some(idx) = param.idx.checked_sub(self.parent_count() as u32) { + // non-Self type parameters are always offset by exactly + // `self.regions.len()`. In the absence of a Self, this is obvious, + // but even in the absence of a `Self` we just have to "compensate" + // for the regions: + // + // For example, for `trait Foo<'a, 'b, T1, T2>`, the + // situation is: + // Substs: + // 0 1 2 3 4 + // Self 'a 'b T1 T2 + // generics.types: + // 0 1 2 + // Self T1 T2 + // And it can be seen that to move from a substs offset to a + // generics offset you just have to offset by the number of regions. + let type_param_offset = self.regions.len(); + if let Some(idx) = (idx as usize).checked_sub(type_param_offset) { + assert!(!(self.has_self && idx == 0)); + &self.types[idx] + } else { + assert!(self.has_self && idx == 0); + &self.types[0] + } + } else { + tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) + .type_param(param, tcx) + } } } @@ -841,7 +896,7 @@ pub enum Predicate<'tcx> { /// No direct syntax. May be thought of as `where T : FnFoo<...>` /// for some substitutions `...` and T being a closure type. /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, ClosureKind), + ClosureKind(DefId, ClosureSubsts<'tcx>, ClosureKind), /// `T1 <: T2` Subtype(PolySubtypePredicate<'tcx>), @@ -850,6 +905,12 @@ pub enum Predicate<'tcx> { ConstEvaluatable(DefId, &'tcx Substs<'tcx>), } +impl<'tcx> AsRef> for Predicate<'tcx> { + fn as_ref(&self) -> &Predicate<'tcx> { + self + } +} + impl<'a, 'gcx, 'tcx> Predicate<'tcx> { /// Performs a substitution suitable for going from a /// poly-trait-ref to supertraits that must hold if that @@ -938,8 +999,8 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> { Predicate::WellFormed(data.subst(tcx, substs)), Predicate::ObjectSafe(trait_def_id) => Predicate::ObjectSafe(trait_def_id), - Predicate::ClosureKind(closure_def_id, kind) => - Predicate::ClosureKind(closure_def_id, kind), + Predicate::ClosureKind(closure_def_id, closure_substs, kind) => + Predicate::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind), Predicate::ConstEvaluatable(def_id, const_substs) => Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)), } @@ -1121,8 +1182,8 @@ impl<'tcx> Predicate<'tcx> { ty::Predicate::ObjectSafe(_trait_def_id) => { vec![] } - ty::Predicate::ClosureKind(_closure_def_id, _kind) => { - vec![] + ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => { + closure_substs.substs.types().collect() } ty::Predicate::ConstEvaluatable(_, substs) => { substs.types().collect() @@ -1155,6 +1216,25 @@ impl<'tcx> Predicate<'tcx> { } } } + + pub fn to_opt_type_outlives(&self) -> Option> { + match *self { + Predicate::TypeOutlives(data) => { + Some(data) + } + Predicate::Trait(..) | + Predicate::Projection(..) | + Predicate::Equate(..) | + Predicate::Subtype(..) | + Predicate::RegionOutlives(..) | + Predicate::WellFormed(..) | + Predicate::ObjectSafe(..) | + Predicate::ClosureKind(..) | + Predicate::ConstEvaluatable(..) => { + None + } + } + } } /// Represents the bounds declared on a particular set of type @@ -1251,6 +1331,22 @@ impl<'tcx, T> ParamEnvAnd<'tcx, T> { } } +impl<'gcx, T> HashStable> for ParamEnvAnd<'gcx, T> + where T: HashStable> +{ + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + let ParamEnvAnd { + ref param_env, + ref value + } = *self; + + param_env.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + } +} + #[derive(Copy, Clone, Debug)] pub struct Destructor { /// The def-id of the destructor method @@ -1265,6 +1361,12 @@ bitflags! { const IS_FUNDAMENTAL = 1 << 2; const IS_UNION = 1 << 3; const IS_BOX = 1 << 4; + /// Indicates whether this abstract data type will be expanded on in future (new + /// fields/variants) and as such, whether downstream crates must match exhaustively on the + /// fields/variants of this data type. + /// + /// See RFC 2008 (https://github.com/rust-lang/rfcs/pull/2008). + const IS_NON_EXHAUSTIVE = 1 << 5; } } @@ -1465,6 +1567,9 @@ impl<'a, 'gcx, 'tcx> AdtDef { if Some(did) == tcx.lang_items().owned_box() { flags = flags | AdtFlags::IS_BOX; } + if tcx.has_attr(did, "non_exhaustive") { + flags = flags | AdtFlags::IS_NON_EXHAUSTIVE; + } match kind { AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM, AdtKind::Union => flags = flags | AdtFlags::IS_UNION, @@ -1493,6 +1598,11 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.intersects(AdtFlags::IS_ENUM) } + #[inline] + pub fn is_non_exhaustive(&self) -> bool { + self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE) + } + /// Returns the kind of the ADT - Struct or Enum. #[inline] pub fn adt_kind(&self) -> AdtKind { @@ -1564,11 +1674,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.variants.iter().flat_map(|v| v.fields.iter()) } - #[inline] - pub fn is_univariant(&self) -> bool { - self.variants.len() == 1 - } - pub fn is_payloadfree(&self) -> bool { !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) @@ -1724,7 +1829,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { vec![] } - TyStr | TyDynamic(..) | TySlice(_) | TyError => { + TyStr | TyDynamic(..) | TySlice(_) | TyForeign(..) | TyError => { // these are never sized - return the target type vec![ty] } @@ -1816,6 +1921,12 @@ impl<'a, 'gcx, 'tcx> FieldDef { } } +/// Represents the various closure traits in the Rust language. This +/// will determine the type of the environment (`self`, in the +/// desuaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum ClosureKind { // Warning: Ordering is significant here! The ordering is chosen @@ -1827,6 +1938,9 @@ pub enum ClosureKind { } impl<'a, 'tcx> ClosureKind { + // This is the initial value used when doing upvar inference. + pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; + pub fn trait_did(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> DefId { match *self { ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem), @@ -1852,6 +1966,16 @@ impl<'a, 'tcx> ClosureKind { _ => false, } } + + /// Returns the representative scalar type for this closure kind. + /// See `TyS::to_opt_closure_kind` for more details. + pub fn to_ty(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Ty<'tcx> { + match self { + ty::ClosureKind::Fn => tcx.types.i8, + ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } } impl<'tcx> TyS<'tcx> { @@ -2169,10 +2293,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + /// Given a `VariantDef`, returns the def-id of the `AdtDef` of which it is a part. + pub fn adt_def_id_of_variant(self, variant_def: &'tcx VariantDef) -> DefId { + let def_key = self.def_key(variant_def.did); + match def_key.disambiguated_data.data { + // for enum variants and tuple structs, the def-id of the ADT itself + // is the *parent* of the variant + DefPathData::EnumVariant(..) | DefPathData::StructCtor => + DefId { krate: variant_def.did.krate, index: def_key.parent.unwrap() }, + + // otherwise, for structs and unions, they share a def-id + _ => variant_def.did, + } + } + pub fn item_name(self, id: DefId) -> InternedString { - if let Some(id) = self.hir.as_local_node_id(id) { - self.hir.name(id).as_str() - } else if id.index == CRATE_DEF_INDEX { + if id.index == CRATE_DEF_INDEX { self.original_crate_name(id.krate).as_str() } else { let def_key = self.def_key(id); @@ -2233,8 +2369,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.get_attrs(did).iter().any(|item| item.check_name(attr)) } - pub fn trait_has_default_impl(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).has_default_impl + /// Returns true if this is an `auto trait`. + /// + /// NB. For a limited time, also returns true if `impl Trait for .. { }` is in the code-base. + pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).has_auto_impl } pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { @@ -2282,6 +2421,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + // Hygienically compare a use-site name (`use_name`) for a field or an associated item with its + // supposed definition name (`def_name`). The method also needs `DefId` of the supposed + // definition's parent/scope to perform comparison. + pub fn hygienic_eq(self, use_name: Name, def_name: Name, def_parent_def_id: DefId) -> bool { + self.adjust(use_name, def_parent_def_id, DUMMY_NODE_ID).0 == def_name.to_ident() + } + pub fn adjust(self, name: Name, scope: DefId, block: NodeId) -> (Ident, DefId) { self.adjust_ident(name.to_ident(), scope, block) } @@ -2293,6 +2439,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }; let scope = match ident.ctxt.adjust(expansion) { Some(macro_def) => self.hir.definitions().macro_def_scope(macro_def), + None if block == DUMMY_NODE_ID => DefId::local(CRATE_DEF_INDEX), // Dummy DefId None => self.hir.get_module_parent(block), }; (ident, scope) @@ -2475,7 +2622,7 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn crate_disambiguator<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - crate_num: CrateNum) -> Symbol { + crate_num: CrateNum) -> CrateDisambiguator { assert_eq!(crate_num, LOCAL_CRATE); tcx.sess.local_crate_disambiguator() } @@ -2487,8 +2634,10 @@ fn original_crate_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } pub fn provide(providers: &mut ty::maps::Providers) { - util::provide(providers); context::provide(providers); + erase_regions::provide(providers); + layout::provide(providers); + util::provide(providers); *providers = ty::maps::Providers { associated_item, associated_item_def_ids, @@ -2504,17 +2653,6 @@ pub fn provide(providers: &mut ty::maps::Providers) { }; } -pub fn provide_extern(providers: &mut ty::maps::Providers) { - *providers = ty::maps::Providers { - adt_sized_constraint, - adt_dtorck_constraint, - trait_impls_of: trait_def::trait_impls_of_provider, - param_env, - ..*providers - }; -} - - /// A map for the local crate mapping each type to a vector of its /// inherent impls. This is not meant to be used outside of coherence; /// rather, you should request the vector for a specific type via @@ -2568,7 +2706,7 @@ impl<'tcx> DtorckConstraint<'tcx> { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub struct SymbolName { // FIXME: we don't rely on interning or equality here - better have // this be a `&'tcx str`. diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index 657ed4077911c..707137649d771 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs @@ -73,42 +73,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // projection). match ty.sty { ty::TyClosure(def_id, ref substs) => { - // FIXME(#27086). We do not accumulate from substs, since they - // don't represent reachable data. This means that, in - // practice, some of the lifetime parameters might not - // be in scope when the body runs, so long as there is - // no reachable data with that lifetime. For better or - // worse, this is consistent with fn types, however, - // which can also encapsulate data in this fashion - // (though it's somewhat harder, and typically - // requires virtual dispatch). - // - // Note that changing this (in a naive way, at least) - // causes regressions for what appears to be perfectly - // reasonable code like this: - // - // ``` - // fn foo<'a>(p: &Data<'a>) { - // bar(|q: &mut Parser| q.read_addr()) - // } - // fn bar(p: Box) { - // } - // ``` - // - // Note that `p` (and `'a`) are not used in the - // closure at all, but to meet the requirement that - // the closure type `C: 'static` (so it can be coerced - // to the object type), we get the requirement that - // `'a: 'static` since `'a` appears in the closure - // type `C`. - // - // A smarter fix might "prune" unused `func_substs` -- - // this would avoid breaking simple examples like - // this, but would still break others (which might - // indeed be invalid, depending on your POV). Pruning - // would be a subtle process, since we have to see - // what func/type parameters are used and unused, - // taking into consideration UFCS and so forth. for upvar_ty in substs.upvar_tys(def_id, *self) { self.compute_components(upvar_ty, out); @@ -178,6 +142,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::TyNever | // ... ty::TyAdt(..) | // OutlivesNominalType ty::TyAnon(..) | // OutlivesNominalType (ish) + ty::TyForeign(..) | // OutlivesNominalType ty::TyStr | // OutlivesScalar (ish) ty::TyArray(..) | // ... ty::TySlice(..) | // ... diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 309880ba06333..376cdc462e82f 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -381,6 +381,12 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(tcx.mk_adt(a_def, substs)) } + (&ty::TyForeign(a_id), &ty::TyForeign(b_id)) + if a_id == b_id => + { + Ok(tcx.mk_foreign(a_id)) + } + (&ty::TyDynamic(ref a_obj, ref a_region), &ty::TyDynamic(ref b_obj, ref b_region)) => { let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 54d55748c8e3a..438511281ba47 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! This module contains implements of the `Lift` and `TypeFoldable` +//! traits for various types in the Rust compiler. Most are written by +//! hand, though we've recently added some macros (e.g., +//! `BraceStructLiftImpl!`) to help with the tedium. + use infer::type_variable; use middle::const_val::{self, ConstVal, ConstAggregate, ConstEvalErr}; use ty::{self, Lift, Ty, TyCtxt}; @@ -16,9 +21,158 @@ use rustc_data_structures::accumulate_vec::AccumulateVec; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use std::rc::Rc; -use syntax::abi; -use hir; +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +macro_rules! CopyImpls { + ($($ty:ty,)+) => { + $( + impl<'tcx> Lift<'tcx> for $ty { + type Lifted = Self; + fn lift_to_tcx<'a, 'gcx>(&self, _: TyCtxt<'a, 'gcx, 'tcx>) -> Option { + Some(*self) + } + } + + impl<'tcx> TypeFoldable<'tcx> for $ty { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> $ty { + *self + } + + fn super_visit_with>(&self, _: &mut F) -> bool { + false + } + } + )+ + } +} + +CopyImpls! { + (), + ::hir::Unsafety, + ::syntax::abi::Abi, + ::hir::def_id::DefId, + ::mir::Local, + ::traits::Reveal, + ::syntax_pos::Span, +} + +/////////////////////////////////////////////////////////////////////////// +// Macros +// +// When possible, use one of these (relatively) convenient macros to write +// the impls for you. + +#[macro_export] +macro_rules! BraceStructLiftImpl { + (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { + type Lifted = $lifted:ty; + $($field:ident),* $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::Lift<$tcx> for $s + $(where $($wc)*)* + { + type Lifted = $lifted; + + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { + $(let $field = tcx.lift(&self.$field)?;)* + Some(Self::Lifted { $($field),* }) + } + } + }; +} + +#[macro_export] +macro_rules! EnumLiftImpl { + (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { + type Lifted = $lifted:ty; + $( + ($variant:path) ( $( $variant_arg:ident),* ) + ),* + $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::Lift<$tcx> for $s + $(where $($wc)*)* + { + type Lifted = $lifted; + + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { + match self { + $($variant ( $($variant_arg),* ) => { + Some($variant ( $(tcx.lift($variant_arg)?),* )) + })* + } + } + } + }; +} + +#[macro_export] +macro_rules! BraceStructTypeFoldableImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $($field:ident),* $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + folder: &mut V, + ) -> Self { + let $s { $($field,)* } = self; + $s { $($field: $field.fold_with(folder),)* } + } + + fn super_visit_with>( + &self, + visitor: &mut V, + ) -> bool { + let $s { $($field,)* } = self; + false $(|| $field.visit_with(visitor))* + } + } + }; +} + +#[macro_export] +macro_rules! EnumTypeFoldableImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $( + ($variant:path) ( $( $variant_arg:ident),* ) + ),* + $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + folder: &mut V, + ) -> Self { + match self { + $($variant ( $($variant_arg),* ) => { + $variant ( $($variant_arg.fold_with(folder)),* ) + })* + } + } + + fn super_visit_with>( + &self, + visitor: &mut V, + ) -> bool { + match self { + $($variant ( $($variant_arg),* ) => { + false $(|| $variant_arg.visit_with(visitor))* + })* + } + } + } + }; +} /////////////////////////////////////////////////////////////////////////// // Lift implementations @@ -90,6 +244,15 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { } } +impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { + type Lifted = IndexVec; + fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { + self.iter() + .map(|e| tcx.lift(e)) + .collect() + } +} + impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { type Lifted = ty::TraitRef<'tcx>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { @@ -211,8 +374,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> { ty::Predicate::WellFormed(ty) => { tcx.lift(&ty).map(ty::Predicate::WellFormed) } - ty::Predicate::ClosureKind(closure_def_id, kind) => { - Some(ty::Predicate::ClosureKind(closure_def_id, kind)) + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + tcx.lift(&closure_substs) + .map(|closure_substs| ty::Predicate::ClosureKind(closure_def_id, + closure_substs, + kind)) } ty::Predicate::ObjectSafe(trait_def_id) => { Some(ty::Predicate::ObjectSafe(trait_def_id)) @@ -381,16 +547,10 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { } } -impl<'a, 'tcx> Lift<'tcx> for type_variable::Default<'a> { - type Lifted = type_variable::Default<'tcx>; - fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { - tcx.lift(&self.ty).map(|ty| { - type_variable::Default { - ty, - origin_span: self.origin_span, - def_id: self.def_id - } - }) +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for type_variable::Default<'a> { + type Lifted = type_variable::Default<'tcx>; + ty, origin_span, def_id } } @@ -420,7 +580,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { FloatMismatch(x) => FloatMismatch(x), Traits(x) => Traits(x), VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy => CyclicTy, + CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), ProjectionMismatched(x) => ProjectionMismatched(x), ProjectionBoundsLength(x) => ProjectionBoundsLength(x), @@ -428,7 +588,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { TyParamDefaultMismatch(ref x) => { return tcx.lift(x).map(TyParamDefaultMismatch) } - ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch) + ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), + OldStyleLUB(ref x) => return tcx.lift(x).map(OldStyleLUB), }) } } @@ -473,6 +634,7 @@ impl<'a, 'tcx> Lift<'tcx> for const_val::ErrKind<'a> { } TypeckError => TypeckError, + CheckMatchError => CheckMatchError, }) } } @@ -502,31 +664,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::layout::LayoutError<'a> { // can easily refactor the folding into the TypeFolder trait as // needed. -macro_rules! CopyImpls { - ($($ty:ty),+) => { - $( - impl<'tcx> Lift<'tcx> for $ty { - type Lifted = Self; - fn lift_to_tcx<'a, 'gcx>(&self, _: TyCtxt<'a, 'gcx, 'tcx>) -> Option { - Some(*self) - } - } - - impl<'tcx> TypeFoldable<'tcx> for $ty { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> $ty { - *self - } - - fn super_visit_with>(&self, _: &mut F) -> bool { - false - } - } - )+ - } -} - -CopyImpls! { (), hir::Unsafety, abi::Abi, hir::def_id::DefId, ::mir::Local } - impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> (T, U) { (self.0.fold_with(folder), self.1.fold_with(folder)) @@ -596,18 +733,8 @@ impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { } } -impl<'tcx> TypeFoldable<'tcx> for ty::ParamEnv<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ParamEnv { - reveal: self.reveal, - caller_bounds: self.caller_bounds.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - let &ty::ParamEnv { reveal: _, ref caller_bounds } = self; - caller_bounds.super_visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ParamEnv<'tcx> { reveal, caller_bounds } } impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice> { @@ -676,7 +803,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)), ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) | - ty::TyParam(..) | ty::TyNever => return self + ty::TyParam(..) | ty::TyNever | ty::TyForeign(..) => return self }; if self.sty == sty { @@ -710,7 +837,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyAnon(_, ref substs) => substs.visit_with(visitor), ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) | - ty::TyParam(..) | ty::TyNever => false, + ty::TyParam(..) | ty::TyNever | ty::TyForeign(..) => false, } } @@ -729,17 +856,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::GenSig<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::GenSig { - yield_ty: self.yield_ty.fold_with(folder), - return_ty: self.return_ty.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.yield_ty.visit_with(visitor) || - self.return_ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::GenSig<'tcx> { + yield_ty, return_ty } } @@ -760,46 +879,20 @@ impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::TraitRef { - def_id: self.def_id, - substs: self.substs.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> { def_id, substs } } -impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialTraitRef<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ExistentialTraitRef { - def_id: self.def_id, - substs: self.substs.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialTraitRef<'tcx> { def_id, substs } } -impl<'tcx> TypeFoldable<'tcx> for ty::ImplHeader<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ImplHeader { - impl_def_id: self.impl_def_id, - self_ty: self.self_ty.fold_with(folder), - trait_ref: self.trait_ref.map(|t| t.fold_with(folder)), - predicates: self.predicates.iter().map(|p| p.fold_with(folder)).collect(), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.self_ty.visit_with(visitor) || - self.trait_ref.map(|r| r.visit_with(visitor)).unwrap_or(false) || - self.predicates.iter().any(|p| p.visit_with(visitor)) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ImplHeader<'tcx> { + impl_def_id, + self_ty, + trait_ref, + predicates, } } @@ -843,17 +936,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjustment<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::adjustment::Adjustment { - kind: self.kind.fold_with(folder), - target: self.target.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.kind.visit_with(visitor) || - self.target.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjustment<'tcx> { + kind, + target, } } @@ -924,16 +1010,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoBorrow<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::GenericPredicates { - parent: self.parent, - predicates: self.predicates.fold_with(folder), - } - } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.predicates.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> { + parent, predicates } } @@ -965,8 +1045,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { ty::Predicate::Projection(binder.fold_with(folder)), ty::Predicate::WellFormed(data) => ty::Predicate::WellFormed(data.fold_with(folder)), - ty::Predicate::ClosureKind(closure_def_id, kind) => - ty::Predicate::ClosureKind(closure_def_id, kind), + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => + ty::Predicate::ClosureKind(closure_def_id, closure_substs.fold_with(folder), kind), ty::Predicate::ObjectSafe(trait_def_id) => ty::Predicate::ObjectSafe(trait_def_id), ty::Predicate::ConstEvaluatable(def_id, substs) => @@ -983,62 +1063,35 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor), ty::Predicate::Projection(ref binder) => binder.visit_with(visitor), ty::Predicate::WellFormed(data) => data.visit_with(visitor), - ty::Predicate::ClosureKind(_closure_def_id, _kind) => false, + ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => + closure_substs.visit_with(visitor), ty::Predicate::ObjectSafe(_trait_def_id) => false, ty::Predicate::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor), } } } -impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionPredicate<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ProjectionPredicate { - projection_ty: self.projection_ty.fold_with(folder), - ty: self.ty.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.projection_ty.visit_with(visitor) || self.ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionPredicate<'tcx> { + projection_ty, ty } } -impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialProjection<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ExistentialProjection { - ty: self.ty.fold_with(folder), - substs: self.substs.fold_with(folder), - item_def_id: self.item_def_id, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) || self.ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialProjection<'tcx> { + ty, substs, item_def_id } } -impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionTy<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ProjectionTy { - substs: self.substs.fold_with(folder), - item_def_id: self.item_def_id, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionTy<'tcx> { + substs, item_def_id } } -impl<'tcx> TypeFoldable<'tcx> for ty::InstantiatedPredicates<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::InstantiatedPredicates { - predicates: self.predicates.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.predicates.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::InstantiatedPredicates<'tcx> { + predicates } } @@ -1168,12 +1221,13 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { FloatMismatch(x) => FloatMismatch(x), Traits(x) => Traits(x), VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy => CyclicTy, + CyclicTy(t) => CyclicTy(t.fold_with(folder)), ProjectionMismatched(x) => ProjectionMismatched(x), ProjectionBoundsLength(x) => ProjectionBoundsLength(x), Sorts(x) => Sorts(x.fold_with(folder)), TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(x.fold_with(folder)), ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)), + OldStyleLUB(ref x) => OldStyleLUB(x.fold_with(folder)), } } @@ -1191,8 +1245,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { b.visit_with(visitor) }, Sorts(x) => x.visit_with(visitor), + OldStyleLUB(ref x) => x.visit_with(visitor), TyParamDefaultMismatch(ref x) => x.visit_with(visitor), ExistentialMismatch(x) => x.visit_with(visitor), + CyclicTy(t) => t.visit_with(visitor), Mismatch | Mutability | TupleSize(_) | @@ -1202,7 +1258,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { FloatMismatch(_) | Traits(_) | VariadicMismatch(_) | - CyclicTy | ProjectionMismatched(_) | ProjectionBoundsLength(_) => false, } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a203e290dbaeb..05aab27dc2acc 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -14,6 +14,7 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; use middle::region; +use rustc_data_structures::indexed_vec::Idx; use ty::subst::{Substs, Subst}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{Slice, TyS}; @@ -24,7 +25,6 @@ use std::cmp::Ordering; use syntax::abi; use syntax::ast::{self, Name}; use syntax::symbol::keywords; -use util::nodemap::FxHashMap; use serialize; @@ -105,6 +105,8 @@ pub enum TypeVariants<'tcx> { /// definition and not a concrete use of it. TyAdt(&'tcx AdtDef, &'tcx Substs<'tcx>), + TyForeign(DefId), + /// The pointee of a string slice. Written as `str`. TyStr, @@ -172,16 +174,26 @@ pub enum TypeVariants<'tcx> { /// A closure can be modeled as a struct that looks like: /// -/// struct Closure<'l0...'li, T0...Tj, U0...Uk> { +/// struct Closure<'l0...'li, T0...Tj, CK, CS, U0...Uk> { /// upvar0: U0, /// ... /// upvark: Uk /// } /// -/// where 'l0...'li and T0...Tj are the lifetime and type parameters -/// in scope on the function that defined the closure, and U0...Uk are -/// type parameters representing the types of its upvars (borrowed, if -/// appropriate). +/// where: +/// +/// - 'l0...'li and T0...Tj are the lifetime and type parameters +/// in scope on the function that defined the closure, +/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This +/// is rather hackily encoded via a scalar type. See +/// `TyS::to_opt_closure_kind` for details. +/// - CS represents the *closure signature*, representing as a `fn()` +/// type. For example, `fn(u32, u32) -> u32` would mean that the closure +/// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait +/// specified above. +/// - U0...Uk are type parameters representing the types of its upvars +/// (borrowed, if appropriate; that is, if Ui represents a by-ref upvar, +/// and the up-var has the type `Foo`, then `Ui = &Foo`). /// /// So, for example, given this function: /// @@ -244,6 +256,17 @@ pub enum TypeVariants<'tcx> { /// closure C wind up influencing the decisions we ought to make for /// closure C (which would then require fixed point iteration to /// handle). Plus it fixes an ICE. :P +/// +/// ## Generators +/// +/// Perhaps surprisingly, `ClosureSubsts` are also used for +/// generators. In that case, what is written above is only half-true +/// -- the set of type parameters is similar, but the role of CK and +/// CS are different. CK represents the "yield type" and CS +/// represents the "return type" of the generator. +/// +/// It'd be nice to split this struct into ClosureSubsts and +/// GeneratorSubsts, I believe. -nmatsakis #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, @@ -254,14 +277,97 @@ pub struct ClosureSubsts<'tcx> { pub substs: &'tcx Substs<'tcx>, } -impl<'a, 'gcx, 'acx, 'tcx> ClosureSubsts<'tcx> { +/// Struct returned by `split()`. Note that these are subslices of the +/// parent slice and not canonical substs themselves. +struct SplitClosureSubsts<'tcx> { + closure_kind_ty: Ty<'tcx>, + closure_sig_ty: Ty<'tcx>, + upvar_kinds: &'tcx [Kind<'tcx>], +} + +impl<'tcx> ClosureSubsts<'tcx> { + /// Divides the closure substs into their respective + /// components. Single source of truth with respect to the + /// ordering. + fn split(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> SplitClosureSubsts<'tcx> { + let generics = tcx.generics_of(def_id); + let parent_len = generics.parent_count(); + SplitClosureSubsts { + closure_kind_ty: self.substs[parent_len].as_type().expect("CK should be a type"), + closure_sig_ty: self.substs[parent_len + 1].as_type().expect("CS should be a type"), + upvar_kinds: &self.substs[parent_len + 2..], + } + } + #[inline] - pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'acx>) -> + pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> impl Iterator> + 'tcx { - let generics = tcx.generics_of(def_id); - self.substs[self.substs.len()-generics.own_count()..].iter().map( - |t| t.as_type().expect("unexpected region in upvars")) + let SplitClosureSubsts { upvar_kinds, .. } = self.split(def_id, tcx); + upvar_kinds.iter().map(|t| t.as_type().expect("upvar should be type")) + } + + /// Returns the closure kind for this closure; may return a type + /// variable during inference. To get the closure kind during + /// inference, use `infcx.closure_kind(def_id, substs)`. + pub fn closure_kind_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> { + self.split(def_id, tcx).closure_kind_ty + } + + /// Returns the type representing the closure signature for this + /// closure; may contain type variables during inference. To get + /// the closure signature during inference, use + /// `infcx.fn_sig(def_id)`. + pub fn closure_sig_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> { + self.split(def_id, tcx).closure_sig_ty + } + + /// Returns the type representing the yield type of the generator. + pub fn generator_yield_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> { + self.closure_kind_ty(def_id, tcx) + } + + /// Returns the type representing the return type of the generator. + pub fn generator_return_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> { + self.closure_sig_ty(def_id, tcx) + } + + /// Return the "generator signature", which consists of its yield + /// and return types. + /// + /// NB. Some bits of the code prefers to see this wrapped in a + /// binder, but it never contains bound regions. Probably this + /// function should be removed. + pub fn generator_poly_sig(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> PolyGenSig<'tcx> { + ty::Binder(self.generator_sig(def_id, tcx)) + } + + /// Return the "generator signature", which consists of its yield + /// and return types. + pub fn generator_sig(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> GenSig<'tcx> { + ty::GenSig { + yield_ty: self.generator_yield_ty(def_id, tcx), + return_ty: self.generator_return_ty(def_id, tcx), + } + } +} + +impl<'tcx> ClosureSubsts<'tcx> { + /// Returns the closure kind for this closure; only usable outside + /// of an inference context, because in that context we know that + /// there are no type variables. + pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind { + self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap() + } + + /// Extracts the signature from the closure; only usable outside + /// of an inference context, because in that context we know that + /// there are no type variables. + pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::PolyFnSig<'tcx> { + match self.closure_sig_ty(def_id, tcx).sty { + ty::TyFnPtr(sig) => sig, + ref t => bug!("closure_sig_ty is not a fn-ptr: {:?}", t), + } } } @@ -574,6 +680,26 @@ impl Binder { { ty::Binder(f(self.0)) } + + /// Unwraps and returns the value within, but only if it contains + /// no bound regions at all. (In other words, if this binder -- + /// and indeed any enclosing binder -- doesn't bind anything at + /// all.) Otherwise, returns `None`. + /// + /// (One could imagine having a method that just unwraps a single + /// binder, but permits late-bound regions bound by enclosing + /// binders, but that would require adjusting the debruijn + /// indices, and given the shallow binding structure we often use, + /// would not be that useful.) + pub fn no_late_bound_regions<'tcx>(self) -> Option + where T : TypeFoldable<'tcx> + { + if self.skip_binder().has_escaping_regions() { + None + } else { + Some(self.skip_binder().clone()) + } + } } /// Represents the projection of an associated type. In explicit UFCS @@ -596,9 +722,10 @@ impl<'a, 'tcx> ProjectionTy<'tcx> { pub fn from_ref_and_name( tcx: TyCtxt, trait_ref: ty::TraitRef<'tcx>, item_name: Name ) -> ProjectionTy<'tcx> { - let item_def_id = tcx.associated_items(trait_ref.def_id).find( - |item| item.name == item_name && item.kind == ty::AssociatedKind::Type - ).unwrap().def_id; + let item_def_id = tcx.associated_items(trait_ref.def_id).find(|item| { + item.kind == ty::AssociatedKind::Type && + tcx.hygienic_eq(item_name, item.name, trait_ref.def_id) + }).unwrap().def_id; ProjectionTy { substs: trait_ref.substs, @@ -758,7 +885,7 @@ impl<'a, 'gcx, 'tcx> ParamTy { /// is the outer fn. /// /// [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index -#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, Copy, PartialOrd, Ord)] pub struct DebruijnIndex { /// We maintain the invariant that this is never 0. So 1 indicates /// the innermost binder. To ensure this, create with `DebruijnIndex::new`. @@ -823,7 +950,7 @@ pub type Region<'tcx> = &'tcx RegionKind; /// /// [1] http://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ /// [2] http://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ -#[derive(Clone, PartialEq, Eq, Hash, Copy, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, Eq, Hash, Copy, RustcEncodable, RustcDecodable, PartialOrd, Ord)] pub enum RegionKind { // Region bound in a type or fn declaration which will be // substituted 'early' -- that is, at the same time when type @@ -869,7 +996,7 @@ pub enum RegionKind { impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {} -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, PartialOrd, Ord)] pub struct EarlyBoundRegion { pub def_id: DefId, pub index: u32, @@ -891,12 +1018,13 @@ pub struct FloatVid { pub index: u32, } -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] -pub struct RegionVid { - pub index: u32, -} +newtype_index!(RegionVid + { + pub idx + DEBUG_FORMAT = custom, + }); -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] pub struct SkolemizedRegionVid { pub index: u32, } @@ -1035,17 +1163,39 @@ impl RegionKind { flags } -} -/// Type utilities -impl<'a, 'gcx, 'tcx> TyS<'tcx> { - pub fn as_opt_param_ty(&self) -> Option { - match self.sty { - ty::TyParam(ref d) => Some(d.clone()), - _ => None, + /// Given an early-bound or free region, returns the def-id where it was bound. + /// For example, consider the regions in this snippet of code: + /// + /// ``` + /// impl<'a> Foo { + /// ^^ -- early bound, declared on an impl + /// + /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c + /// ^^ ^^ ^ anonymous, late-bound + /// | early-bound, appears in where-clauses + /// late-bound, appears only in fn args + /// {..} + /// } + /// ``` + /// + /// Here, `free_region_binding_scope('a)` would return the def-id + /// of the impl, and for all the other highlighted regions, it + /// would return the def-id of the function. In other cases (not shown), this + /// function might return the def-id of a closure. + pub fn free_region_binding_scope(&self, tcx: TyCtxt<'_, '_, '_>) -> DefId { + match self { + ty::ReEarlyBound(br) => { + tcx.parent_def_id(br.def_id).unwrap() + } + ty::ReFree(fr) => fr.scope, + _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), } } +} +/// Type utilities +impl<'a, 'gcx, 'tcx> TyS<'tcx> { pub fn is_nil(&self) -> bool { match self.sty { TyTuple(ref tys, _) => tys.is_empty(), @@ -1069,54 +1219,6 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - /// 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 = FxHashMap::default(); - let forest = self.uninhabited_from(&mut visited, tcx); - - // 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 { match self.sty { TyBool | TyChar | TyInt(_) | TyUint(_) | TyFloat(_) => true, @@ -1165,13 +1267,6 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - pub fn is_structural(&self) -> bool { - match self.sty { - TyAdt(..) | TyTuple(..) | TyArray(..) | TyClosure(..) => true, - _ => self.is_slice() | self.is_trait(), - } - } - #[inline] pub fn is_simd(&self) -> bool { match self.sty { @@ -1272,6 +1367,15 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn is_enum(&self) -> bool { + match self.sty { + TyAdt(adt_def, _) => { + adt_def.is_enum() + } + _ => false, + } + } + pub fn is_closure(&self) -> bool { match self.sty { TyClosure(..) => true, @@ -1279,6 +1383,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn is_generator(&self) -> bool { + match self.sty { + TyGenerator(..) => true, + _ => false, + } + } + pub fn is_integral(&self) -> bool { match self.sty { TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true, @@ -1286,6 +1397,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn is_fresh_ty(&self) -> bool { + match self.sty { + TyInfer(FreshTy(_)) => true, + _ => false, + } + } + pub fn is_fresh(&self) -> bool { match self.sty { TyInfer(FreshTy(_)) => true, @@ -1395,6 +1513,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { match self.sty { TyDynamic(ref tt, ..) => tt.principal().map(|p| p.def_id()), TyAdt(def, _) => Some(def.did), + TyForeign(did) => Some(did), TyClosure(id, _) => Some(id), _ => None, } @@ -1444,6 +1563,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { TyRawPtr(_) | TyNever | TyTuple(..) | + TyForeign(..) | TyParam(_) | TyInfer(_) | TyError => { @@ -1451,6 +1571,37 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } } + + /// When we create a closure, we record its kind (i.e., what trait + /// it implements) into its `ClosureSubsts` using a type + /// parameter. This is kind of a phantom type, except that the + /// most convenient thing for us to are the integral types. This + /// function converts such a special type into the closure + /// kind. To go the other way, use + /// `tcx.closure_kind_ty(closure_kind)`. + /// + /// Note that during type checking, we use an inference variable + /// to represent the closure kind, because it has not yet been + /// inferred. Once [upvar inference] is complete, that type varibale + /// will be unified. + /// + /// [upvar inference]: src/librustc_typeck/check/upvar.rs + pub fn to_opt_closure_kind(&self) -> Option { + match self.sty { + TyInt(int_ty) => match int_ty { + ast::IntTy::I8 => Some(ty::ClosureKind::Fn), + ast::IntTy::I16 => Some(ty::ClosureKind::FnMut), + ast::IntTy::I32 => Some(ty::ClosureKind::FnOnce), + _ => bug!("cannot convert type `{:?}` to a closure kind", self), + }, + + TyInfer(_) => None, + + TyError => Some(ty::ClosureKind::Fn), + + _ => bug!("cannot convert type `{:?}` to a closure kind", self), + } + } } /// Typed constant value. diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index e2881ac9b798e..80b113dfdf5a5 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -107,6 +107,19 @@ impl<'tcx> fmt::Debug for Kind<'tcx> { } } +impl<'tcx> fmt::Display for Kind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(ty) = self.as_type() { + write!(f, "{}", ty) + } else if let Some(r) = self.as_region() { + write!(f, "{}", r) + } else { + // FIXME(RFC 2000): extend this if/else chain when we support const generic. + unimplemented!(); + } + } +} + impl<'tcx> TypeFoldable<'tcx> for Kind<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { if let Some(ty) = self.as_type() { @@ -207,11 +220,11 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> { tcx.intern_substs(&result) } - fn fill_item(substs: &mut Vec>, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - defs: &ty::Generics, - mk_region: &mut FR, - mk_type: &mut FT) + pub fn fill_item(substs: &mut Vec>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + defs: &ty::Generics, + mk_region: &mut FR, + mk_type: &mut FT) where FR: FnMut(&ty::RegionParameterDef, &[Kind<'tcx>]) -> ty::Region<'tcx>, FT: FnMut(&ty::TypeParameterDef, &[Kind<'tcx>]) -> Ty<'tcx> { diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index e0b05c2ba39ac..0fbf9f1bd587b 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -34,7 +34,7 @@ pub struct TraitDef { /// be usable with the sugar (or without it). pub paren_sugar: bool, - pub has_default_impl: bool, + pub has_auto_impl: bool, /// The ICH of this trait's DefPath, cached here so it doesn't have to be /// recomputed all the time. @@ -51,14 +51,14 @@ impl<'a, 'gcx, 'tcx> TraitDef { pub fn new(def_id: DefId, unsafety: hir::Unsafety, paren_sugar: bool, - has_default_impl: bool, + has_auto_impl: bool, def_path_hash: DefPathHash) -> TraitDef { TraitDef { def_id, paren_sugar, unsafety, - has_default_impl, + has_auto_impl, def_path_hash, } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 27819f551b9b3..129badc46d8c1 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -10,14 +10,15 @@ //! misc. type-system utilities too small to deserve their own file +use hir::def::Def; use hir::def_id::{DefId, LOCAL_CRATE}; -use hir::map::DefPathData; +use hir::map::{DefPathData, Node}; +use hir; use ich::NodeIdHashingMode; use middle::const_val::ConstVal; use traits::{self, Reveal}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::TypeVisitor; -use ty::layout::{Layout, LayoutError}; use ty::subst::{Subst, Kind}; use ty::TypeVariants::*; use util::common::ErrorReported; @@ -515,11 +516,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let result = item_substs.iter().zip(impl_substs.iter()) .filter(|&(_, &k)| { if let Some(&ty::RegionKind::ReEarlyBound(ref ebr)) = k.as_region() { - !impl_generics.region_param(ebr).pure_wrt_drop + !impl_generics.region_param(ebr, self).pure_wrt_drop } else if let Some(&ty::TyS { sty: ty::TypeVariants::TyParam(ref pt), .. }) = k.as_type() { - !impl_generics.type_param(pt).pure_wrt_drop + !impl_generics.type_param(pt, self).pure_wrt_drop } else { // not a type or region param - this should be reported // as an error. @@ -553,7 +554,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let result = match ty.sty { ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | - ty::TyFloat(_) | ty::TyStr | ty::TyNever | + ty::TyFloat(_) | ty::TyStr | ty::TyNever | ty::TyForeign(..) | ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => { // these types never have a destructor Ok(ty::DtorckConstraint::empty()) @@ -619,9 +620,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { result } + pub fn is_closure(self, def_id: DefId) -> bool { + self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr + } + + /// Given the `DefId` of a fn or closure, returns the `DefId` of + /// the innermost fn item that the closure is contained within. + /// This is a significant def-id because, when we do + /// type-checking, we type-check this fn item and all of its + /// (transitive) closures together. Therefore, when we fetch the + /// `typeck_tables_of` the closure, for example, we really wind up + /// fetching the `typeck_tables_of` the enclosing fn item. pub fn closure_base_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; - while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { + while self.is_closure(def_id) { def_id = self.parent_def_id(def_id).unwrap_or_else(|| { bug!("closure {:?} has no parent", def_id); }); @@ -629,6 +641,33 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { def_id } + /// Given the def-id and substs a closure, creates the type of + /// `self` argument that the closure expects. For example, for a + /// `Fn` closure, this would return a reference type `&T` where + /// `T=closure_ty`. + /// + /// Returns `None` if this closure's kind has not yet been inferred. + /// This should only be possible during type checking. + /// + /// Note that the return value is a late-bound region and hence + /// wrapped in a binder. + pub fn closure_env_ty(self, + closure_def_id: DefId, + closure_substs: ty::ClosureSubsts<'tcx>) + -> Option>> + { + let closure_ty = self.mk_closure(closure_def_id, closure_substs); + let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); + let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self); + let closure_kind = closure_kind_ty.to_opt_closure_kind()?; + let env_ty = match closure_kind { + ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnOnce => closure_ty, + }; + Some(ty::Binder(env_ty)) + } + /// Given the def-id of some item that has no type parameters, make /// a suitable "empty substs" for it. pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> &'tcx ty::Substs<'tcx> { @@ -647,6 +686,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { _ => bug!(), } } + + /// Check if the node pointed to by def_id is a mutable static item + pub fn is_static_mut(&self, def_id: DefId) -> bool { + if let Some(node) = self.hir.get_if_local(def_id) { + match node { + Node::NodeItem(&hir::Item { + node: hir::ItemStatic(_, hir::MutMutable, _), .. + }) => true, + Node::NodeForeignItem(&hir::ForeignItem { + node: hir::ForeignItemStatic(_, mutbl), .. + }) => mutbl, + _ => false + } + } else { + match self.describe_def(def_id) { + Some(Def::Static(_, mutbl)) => mutbl, + _ => false + } + } + } } pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { @@ -714,6 +773,7 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> TyAnon(def_id, _) | TyFnDef(def_id, _) => self.def_id(def_id), TyAdt(d, _) => self.def_id(d.did), + TyForeign(def_id) => self.def_id(def_id), TyFnPtr(f) => { self.hash(f.unsafety()); self.hash(f.abi()); @@ -825,30 +885,6 @@ impl<'a, 'tcx> ty::TyS<'tcx> { tcx.needs_drop_raw(param_env.and(self)) } - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout<'lcx>(&'tcx self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Result<&'tcx Layout, LayoutError<'tcx>> { - let ty = tcx.erase_regions(&self); - let layout = tcx.layout_raw(param_env.reveal_all().and(ty)); - - // NB: This recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout()`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admitedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - if let Ok(l) = layout { - Layout::record_layout_for_printing(tcx, ty, param_env, l); - } - - layout - } - - /// Check whether a type is representable. This means it cannot contain unboxed /// structural recursion. This check is needed for structs and enums. pub fn is_representable(&'tcx self, @@ -1109,6 +1145,9 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyStr => false, + // Foreign types can never have destructors + ty::TyForeign(..) => false, + // Issue #22536: We first query type_moves_by_default. It sees a // normalized version of the type, and therefore will definitely // know whether the type implements Copy (and thus needs no @@ -1154,24 +1193,56 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) - -> Result<&'tcx Layout, LayoutError<'tcx>> -{ - let (param_env, ty) = query.into_parts(); - - let rec_limit = tcx.sess.recursion_limit.get(); - let depth = tcx.layout_depth.get(); - if depth > rec_limit { - tcx.sess.fatal( - &format!("overflow representing the type `{}`", ty)); - } +pub enum ExplicitSelf<'tcx> { + ByValue, + ByReference(ty::Region<'tcx>, hir::Mutability), + ByBox, + Other +} - tcx.layout_depth.set(depth+1); - let layout = Layout::compute_uncached(tcx, param_env, ty); - tcx.layout_depth.set(depth); +impl<'tcx> ExplicitSelf<'tcx> { + /// Categorizes an explicit self declaration like `self: SomeType` + /// into either `self`, `&self`, `&mut self`, `Box`, or + /// `Other`. + /// This is mainly used to require the arbitrary_self_types feature + /// in the case of `Other`, to improve error messages in the common cases, + /// and to make `Other` non-object-safe. + /// + /// Examples: + /// + /// ``` + /// impl<'a> Foo for &'a T { + /// // Legal declarations: + /// fn method1(self: &&'a T); // ExplicitSelf::ByReference + /// fn method2(self: &'a T); // ExplicitSelf::ByValue + /// fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox + /// fn method4(self: Rc<&'a T>); // ExplicitSelf::Other + /// + /// // Invalid cases will be caught by `check_method_receiver`: + /// fn method_err1(self: &'a mut T); // ExplicitSelf::Other + /// fn method_err2(self: &'static T) // ExplicitSelf::ByValue + /// fn method_err3(self: &&T) // ExplicitSelf::ByReference + /// } + /// ``` + /// + pub fn determine

", (*entry).1); return Some(Event::Html(reference.into())); @@ -394,7 +393,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { v.sort_by(|a, b| a.1.cmp(&b.1)); let mut ret = String::from("

    "); for (mut content, id) in v { - write!(ret, "
  1. ", id).unwrap(); + write!(ret, "
  2. ", id).unwrap(); let mut is_paragraph = false; if let Some(&Event::End(Tag::Paragraph)) = content.last() { content.pop(); @@ -402,7 +401,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { } html::push_html(&mut ret, content.into_iter()); write!(ret, - " ", + " ", id).unwrap(); if is_paragraph { ret.push_str("

    "); @@ -639,9 +638,9 @@ pub fn render(w: &mut fmt::Formatter, )) }); let tooltip = if ignore { - Some(("Be careful when using this code, it's not being tested!", "ignore")) + Some(("This example is not tested", "ignore")) } else if compile_fail { - Some(("This code doesn't compile so be extra careful!", "compile_fail")) + Some(("This example deliberately fails to compile", "compile_fail")) } else { None }; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 485e75443fe08..9dc01bb0916f5 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -34,10 +34,10 @@ //! both occur before the crate is rendered. pub use self::ExternalLocation::*; -use std::ascii::AsciiExt; +use std::borrow::Cow; use std::cell::RefCell; use std::cmp::Ordering; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::default::Default; use std::error; use std::fmt::{self, Display, Formatter, Write as FmtWrite}; @@ -125,6 +125,38 @@ pub struct SharedContext { /// Warnings for the user if rendering would differ using different markdown /// parsers. pub markdown_warnings: RefCell)>>, + /// The directories that have already been created in this doc run. Used to reduce the number + /// of spurious `create_dir_all` calls. + pub created_dirs: RefCell>, +} + +impl SharedContext { + fn ensure_dir(&self, dst: &Path) -> io::Result<()> { + let mut dirs = self.created_dirs.borrow_mut(); + if !dirs.contains(dst) { + fs::create_dir_all(dst)?; + dirs.insert(dst.to_path_buf()); + } + + Ok(()) + } +} + +impl SharedContext { + /// Returns whether the `collapse-docs` pass was run on this crate. + pub fn was_collapsed(&self) -> bool { + self.passes.contains("collapse-docs") + } + + /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the + /// `collapsed_doc_value` of the given item. + pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option> { + if self.was_collapsed() { + item.collapsed_doc_value().map(|s| s.into()) + } else { + item.doc_value().map(|s| s.into()) + } + } } /// Indicates where an external crate can be found. @@ -256,6 +288,9 @@ pub struct Cache { // the access levels from crateanalysis. pub access_levels: Arc>, + /// The version of the crate being documented, if given fron the `--crate-version` flag. + pub crate_version: Option, + // Private fields only used when initially crawling a crate to build a cache stack: Vec, @@ -342,6 +377,7 @@ impl ToJson for IndexItem { /// A type used for the search index. struct Type { name: Option, + generics: Option>, } impl ToJson for Type { @@ -350,6 +386,9 @@ impl ToJson for Type { Some(ref name) => { let mut data = BTreeMap::new(); data.insert("name".to_owned(), name.to_json()); + if let Some(ref generics) = self.generics { + data.insert("generics".to_owned(), generics.to_json()); + } Json::Object(data) }, None => Json::Null @@ -401,7 +440,7 @@ fn init_ids() -> FxHashMap { "methods", "deref-methods", "implementations", - ].into_iter().map(|id| (String::from(*id), 1)).collect() + ].into_iter().map(|id| (String::from(*id), 1)).collect() } /// This method resets the local table of used ID attributes. This is typically @@ -460,6 +499,7 @@ pub fn run(mut krate: clean::Crate, }, css_file_extension: css_file_extension.clone(), markdown_warnings: RefCell::new(vec![]), + created_dirs: RefCell::new(FxHashSet()), }; // If user passed in `--playground-url` arg, we fill in crate name here @@ -534,6 +574,7 @@ pub fn run(mut krate: clean::Crate, primitive_locations: FxHashMap(), stripped_mod: false, access_levels: krate.access_levels.clone(), + crate_version: krate.version.take(), orphan_impl_items: Vec::new(), traits: mem::replace(&mut krate.external_traits, FxHashMap()), deref_trait_did, @@ -646,7 +687,6 @@ fn concise_compared_strs(s1: &str, s2: &str) -> (String, String) { (format!("...{}", concise_str(s1)), format!("...{}", concise_str(s2))) } - fn print_message(msg: &str, intro_msg: &mut bool, span: &Span, text: &str) { if !*intro_msg { println!("WARNING: documentation for this crate may be rendered \ @@ -790,7 +830,6 @@ fn write_shared(cx: &Context, // Write out the shared files. Note that these are shared among all rustdoc // docs placed in the output directory, so this needs to be a synchronized // operation with respect to all other rustdocs running around. - try_err!(fs::create_dir_all(&cx.dst), &cx.dst); let _lock = flock::Lock::panicking_new(&cx.dst.join(".lock"), true, true, true); // Add all the static files. These may already exist, but we just @@ -1234,7 +1273,7 @@ impl DocFolder for Cache { clean::FunctionItem(..) | clean::ModuleItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | clean::ConstantItem(..) | clean::StaticItem(..) | - clean::UnionItem(..) + clean::UnionItem(..) | clean::ForeignTypeItem if !self.stripped_mod => { // Reexported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, @@ -1269,7 +1308,7 @@ impl DocFolder for Cache { // Maintain the parent stack let orig_parent_is_trait_impl = self.parent_is_trait_impl; let parent_pushed = match item.inner { - clean::TraitItem(..) | clean::EnumItem(..) | + clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem | clean::StructItem(..) | clean::UnionItem(..) => { self.parent_stack.push(item.def_id); self.parent_is_trait_impl = false; @@ -1306,7 +1345,8 @@ impl DocFolder for Cache { // Figure out the id of this impl. This may map to a // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. - let did = if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { + let mut dids = FxHashSet(); + if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { let masked_trait = i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)); if !masked_trait { @@ -1315,23 +1355,33 @@ impl DocFolder for Cache { clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { - Some(did) + dids.insert(did); } ref t => { - t.primitive_type().and_then(|t| { + let did = t.primitive_type().and_then(|t| { self.primitive_locations.get(&t).cloned() - }) + }); + + if let Some(did) = did { + dids.insert(did); + } + } + } + } + + if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { + for bound in generics { + if let Some(did) = bound.def_id() { + dids.insert(did); } } - } else { - None } } else { unreachable!() }; - if let Some(did) = did { + for did in dids { self.impls.entry(did).or_insert(vec![]).push(Impl { - impl_item: item, + impl_item: item.clone(), }); } None @@ -1503,8 +1553,8 @@ impl Context { this.render_item(&mut buf, &item, false).unwrap(); // buf will be empty if the module is stripped and there is no redirect for it if !buf.is_empty() { + try_err!(this.shared.ensure_dir(&this.dst), &this.dst); let joint_dst = this.dst.join("index.html"); - try_err!(fs::create_dir_all(&this.dst), &this.dst); let mut dst = try_err!(File::create(&joint_dst), &joint_dst); try_err!(dst.write_all(&buf), &joint_dst); } @@ -1538,8 +1588,8 @@ impl Context { let name = item.name.as_ref().unwrap(); let item_type = item.type_(); let file_name = &item_path(item_type, name); + try_err!(self.shared.ensure_dir(&self.dst), &self.dst); let joint_dst = self.dst.join(file_name); - try_err!(fs::create_dir_all(&self.dst), &self.dst); let mut dst = try_err!(File::create(&joint_dst), &joint_dst); try_err!(dst.write_all(&buf), &joint_dst); @@ -1547,9 +1597,10 @@ impl Context { // URL for the page. let redir_name = format!("{}.{}.html", name, item_type.name_space()); let redir_dst = self.dst.join(redir_name); - if let Ok(mut redirect_out) = OpenOptions::new().create_new(true) + if let Ok(redirect_out) = OpenOptions::new().create_new(true) .write(true) .open(&redir_dst) { + let mut redirect_out = BufWriter::new(redirect_out); try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst); } @@ -1559,7 +1610,8 @@ impl Context { if item_type == ItemType::Macro { let redir_name = format!("{}.{}!.html", item_type, name); let redir_dst = self.dst.join(redir_name); - let mut redirect_out = try_err!(File::create(&redir_dst), &redir_dst); + let redirect_out = try_err!(File::create(&redir_dst), &redir_dst); + let mut redirect_out = BufWriter::new(redirect_out); try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst); } } @@ -1647,7 +1699,7 @@ impl<'a> Item<'a> { format!("{}-{}", self.item.source.loline, self.item.source.hiline) }; Some(format!("{root}src/{krate}/{path}#{lines}", - root = root, + root = Escape(&root), krate = krate, path = path, lines = lines)) @@ -1675,6 +1727,7 @@ impl<'a> fmt::Display for Item<'a> { clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?, clean::StaticItem(..) | clean::ForeignStaticItem(..) => write!(fmt, "Static ")?, clean::ConstantItem(..) => write!(fmt, "Constant ")?, + clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?, _ => { // We don't generate pages for any other type. unreachable!(); @@ -1739,6 +1792,7 @@ impl<'a> fmt::Display for Item<'a> { clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(fmt, self.cx, self.item, i), clean::ConstantItem(ref c) => item_constant(fmt, self.cx, self.item, c), + clean::ForeignTypeItem => item_foreign_type(fmt, self.cx, self.item), _ => { // We don't generate pages for any other type. unreachable!(); @@ -1763,7 +1817,9 @@ fn full_path(cx: &Context, item: &clean::Item) -> String { fn shorter<'a>(s: Option<&'a str>) -> String { match s { - Some(s) => s.lines().take_while(|line|{ + Some(s) => s.lines() + .skip_while(|s| s.chars().all(|c| c.is_whitespace())) + .take_while(|line|{ (*line).chars().any(|chr|{ !chr.is_whitespace() }) @@ -1779,6 +1835,9 @@ fn plain_summary_line(s: Option<&str>) -> String { } fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result { + if let Some(ref name) = item.name { + info!("Documenting {}", name); + } document_stability(w, cx, item)?; let prefix = render_assoc_const_value(item); document_full(w, item, cx, &prefix)?; @@ -1794,37 +1853,32 @@ fn render_markdown(w: &mut fmt::Formatter, prefix: &str, scx: &SharedContext) -> fmt::Result { - // We only emit warnings if the user has opted-in to Pulldown rendering. - let output = if render_type == RenderType::Pulldown { - // Save the state of USED_ID_MAP so it only gets updated once even - // though we're rendering twice. - let orig_used_id_map = USED_ID_MAP.with(|map| map.borrow().clone()); - let hoedown_output = format!("{}", Markdown(md_text, RenderType::Hoedown)); - USED_ID_MAP.with(|map| *map.borrow_mut() = orig_used_id_map); - let pulldown_output = format!("{}", Markdown(md_text, RenderType::Pulldown)); - let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output); - differences.retain(|s| { - match *s { - html_diff::Difference::NodeText { ref elem_text, - ref opposite_elem_text, - .. } - if elem_text.split_whitespace().eq(opposite_elem_text.split_whitespace()) => { - false - } - _ => true, + // Save the state of USED_ID_MAP so it only gets updated once even + // though we're rendering twice. + let orig_used_id_map = USED_ID_MAP.with(|map| map.borrow().clone()); + let hoedown_output = format!("{}", Markdown(md_text, RenderType::Hoedown)); + USED_ID_MAP.with(|map| *map.borrow_mut() = orig_used_id_map); + let pulldown_output = format!("{}", Markdown(md_text, RenderType::Pulldown)); + let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output); + differences.retain(|s| { + match *s { + html_diff::Difference::NodeText { ref elem_text, + ref opposite_elem_text, + .. } + if elem_text.split_whitespace().eq(opposite_elem_text.split_whitespace()) => { + false } - }); - - if !differences.is_empty() { - scx.markdown_warnings.borrow_mut().push((span, md_text.to_owned(), differences)); + _ => true, } + }); - pulldown_output - } else { - format!("{}", Markdown(md_text, RenderType::Hoedown)) - }; + if !differences.is_empty() { + scx.markdown_warnings.borrow_mut().push((span, md_text.to_owned(), differences)); + } - write!(w, "
    {}{}
    ", prefix, output) + write!(w, "
    {}{}
    ", + prefix, + if render_type == RenderType::Pulldown { pulldown_output } else { hoedown_output }) } fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLink, @@ -1860,8 +1914,9 @@ fn render_assoc_const_value(item: &clean::Item) -> String { fn document_full(w: &mut fmt::Formatter, item: &clean::Item, cx: &Context, prefix: &str) -> fmt::Result { - if let Some(s) = item.doc_value() { - render_markdown(w, s, item.source.clone(), cx.render_type, prefix, &cx.shared)?; + if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { + debug!("Doc block: =====\n{}\n=====", s); + render_markdown(w, &*s, item.source.clone(), cx.render_type, prefix, &cx.shared)?; } else if !prefix.is_empty() { write!(w, "
    {}
    ", prefix)?; } @@ -1902,7 +1957,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, document(w, cx, item)?; let mut indices = (0..items.len()).filter(|i| { - if let clean::DefaultImplItem(..) = items[*i].inner { + if let clean::AutoImplItem(..) = items[*i].inner { return false; } !items[*i].is_stripped() @@ -2017,6 +2072,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::Primitive => ("primitives", "Primitive Types"), ItemType::AssociatedType => ("associated-types", "Associated Types"), ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), + ItemType::ForeignType => ("foreign-types", "Foreign Types"), }; write!(w, "

    \ {name}

    \n", @@ -2234,10 +2290,10 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, AbiSpace(f.abi), it.name.as_ref().unwrap(), f.generics).len(); - write!(w, "
    ")?;
    +    write!(w, "{}
    ", render_spotlight_traits(it)?)?;
         render_attributes(w, it)?;
    -    write!(w, "{vis}{constness}{unsafety}{abi}fn \
    -               {name}{generics}{decl}{where_clause}
    ", + write!(w, + "{vis}{constness}{unsafety}{abi}fn {name}{generics}{decl}{where_clause}
    ", vis = VisSpace(&it.visibility), constness = ConstnessSpace(f.constness), unsafety = UnsafetySpace(f.unsafety), @@ -2246,13 +2302,25 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, generics = f.generics, where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, decl = Method { - decl: &f.decl, - name_len, - indent: 0, + decl: &f.decl, + name_len, + indent: 0, })?; document(w, cx, it) } +fn implementor2item<'a>(cache: &'a Cache, imp : &Implementor) -> Option<&'a clean::Item> { + if let Some(t_did) = imp.impl_.for_.def_id() { + if let Some(impl_item) = cache.impls.get(&t_did).and_then(|i| i.iter() + .find(|i| i.impl_item.def_id == imp.def_id)) + { + let i = &impl_item.impl_item; + return Some(i); + } + } + None +} + fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t: &clean::Trait) -> fmt::Result { let mut bounds = String::new(); @@ -2356,8 +2424,9 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, let item_type = m.type_(); let id = derive_id(format!("{}.{}", item_type, name)); let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); - write!(w, "

    \ + write!(w, "{extra}

    \

    '; + function addTab(array, query, display) { + var extraStyle = ''; + if (display === false) { + extraStyle = ' style="display: none;"'; + } - if (results.length > 0) { - shown = []; + var output = ''; + if (array.length > 0) { + output = '
    '; + var shown = []; - results.forEach(function(item) { + array.forEach(function(item) { var name, type, href, displayPath; if (shown.indexOf(item) !== -1) { @@ -752,13 +1171,41 @@ '' + escape(item.desc) + ' '; }); + output += '
    '; } else { - output += 'No results :( No results :(
    ' + + 'Try on
    Try on DuckDuckGo?'; + '">DuckDuckGo?
'; + } + return output; + } + + function makeTabHeader(tabNb, text, nbElems) { + if (currentTab === tabNb) { + return '
' + text + + '
(' + nbElems + ')
'; } + return '
' + text + '
(' + nbElems + ')
'; + } + + function showResults(results) { + var output, query = getQuery(); + + currentResults = query.id; + output = '

Results for ' + escape(query.query) + + (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '

' + + '
' + + makeTabHeader(0, "Types/modules", results['others'].length) + + makeTabHeader(1, "As parameters", results['in_args'].length) + + makeTabHeader(2, "As return value", results['returned'].length) + + '
'; + + output += addTab(results['others'], query); + output += addTab(results['in_args'], query, false); + output += addTab(results['returned'], query, false); + output += '
'; - output += "

"; addClass(document.getElementById('main'), 'hidden'); var search = document.getElementById('search'); removeClass(search, 'hidden'); @@ -773,14 +1220,17 @@ e.style.width = width + 'px'; }); initSearchNav(); + var elems = document.getElementById('titles').childNodes; + elems[0].onclick = function() { printTab(0); }; + elems[1].onclick = function() { printTab(1); }; + elems[2].onclick = function() { printTab(2); }; + printTab(currentTab); } function search(e) { var query, - filterdata = [], obj, i, len, - results = [], - maxResults = 200, + results = {"in_args": [], "returned": [], "others": []}, resultIndex; var params = getQueryStringParams(); @@ -806,25 +1256,15 @@ } } - resultIndex = execQuery(query, 20000, index); - len = resultIndex.length; - for (i = 0; i < len; ++i) { - if (resultIndex[i].id > -1) { - obj = searchIndex[resultIndex[i].id]; - filterdata.push([obj.name, obj.ty, obj.path, obj.desc]); - results.push(obj); - } - if (results.length >= maxResults) { - break; - } - } - + results = execQuery(query, 20000, index); showResults(results); } function itemTypeFromName(typename) { for (var i = 0; i < itemTypes.length; ++i) { - if (itemTypes[i] === typename) { return i; } + if (itemTypes[i] === typename) { + return i; + } } return -1; } @@ -915,7 +1355,7 @@ var search_input = document.getElementsByClassName("search-input")[0]; search_input.onkeyup = callback; search_input.oninput = callback; - document.getElementsByClassName("search-form")[0].onsubmit = function(e){ + document.getElementsByClassName("search-form")[0].onsubmit = function(e) { e.preventDefault(); clearTimeout(searchTimeout); search(); @@ -991,7 +1431,9 @@ var crates = []; for (var crate in rawSearchIndex) { - if (!rawSearchIndex.hasOwnProperty(crate)) { continue; } + if (!rawSearchIndex.hasOwnProperty(crate)) { + continue; + } crates.push(crate); } crates.sort(); @@ -1071,6 +1513,7 @@ block("trait", "Traits"); block("fn", "Functions"); block("type", "Type Definitions"); + block("foreigntype", "Foreign Types"); } window.initSidebarItems = initSidebarItems; @@ -1290,6 +1733,30 @@ return wrapper; } + // In the search display, allows to switch between tabs. + function printTab(nb) { + if (nb === 0 || nb === 1 || nb === 2) { + currentTab = nb; + } + var nb_copy = nb; + onEach(document.getElementById('titles').childNodes, function(elem) { + if (nb_copy === 0) { + addClass(elem, 'selected'); + } else { + removeClass(elem, 'selected'); + } + nb_copy -= 1; + }); + onEach(document.getElementById('results').childNodes, function(elem) { + if (nb === 0) { + elem.style.display = ''; + } else { + elem.style.display = 'none'; + } + nb -= 1; + }); + } + onEach(document.getElementById('main').getElementsByTagName('pre'), function(e) { onEach(e.getElementsByClassName('attributes'), function(i_e) { i_e.parentNode.insertBefore(createToggleWrapper(), i_e); @@ -1314,6 +1781,47 @@ }); } }); + + function showModal(content) { + var modal = document.createElement('div'); + modal.id = "important"; + addClass(modal, 'modal'); + modal.innerHTML = ''; + document.getElementsByTagName('body')[0].appendChild(modal); + document.getElementById('modal-close').onclick = hideModal; + modal.onclick = hideModal; + } + + function hideModal() { + var modal = document.getElementById("important"); + if (modal) { + modal.parentNode.removeChild(modal); + } + } + + onEach(document.getElementsByClassName('important-traits'), function(e) { + e.onclick = function() { + showModal(e.lastElementChild.innerHTML); + }; + }); + + var search_input = document.getElementsByClassName("search-input")[0]; + + if (search_input) { + search_input.onfocus = function() { + if (search_input.value !== "") { + addClass(document.getElementById("main"), "hidden"); + removeClass(document.getElementById("search"), "hidden"); + if (browserSupportsHistoryApi()) { + history.replaceState(search_input.value, + "", + "?search=" + encodeURIComponent(search_input.value)); + } + } + }; + } }()); // Sets the focus on the search bar at the top of the page diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 1b7232bf1bca8..679f5f6e3fde9 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -89,7 +89,7 @@ h2 { h3 { font-size: 1.3em; } -h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) { +h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.important), h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) { font-weight: 500; margin: 20px 0 15px 0; padding-bottom: 6px; @@ -111,7 +111,10 @@ h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant { h3.impl, h3.method, h3.type { margin-top: 15px; } -h1, h2, h3, h4, .sidebar, a.source, .search-input, .content table :not(code)>a, .collapse-toggle { + +h1, h2, h3, h4, +.sidebar, a.source, .search-input, .content table :not(code)>a, +.collapse-toggle, ul.item-list > li > .out-of-band { font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; } @@ -138,9 +141,12 @@ code, pre { border-radius: 3px; padding: 0 0.2em; } -.docblock pre code, .docblock-short pre code { +.docblock pre code, .docblock-short pre code, .docblock code.spotlight { padding: 0; } +.docblock code.spotlight :last-child { + padding-bottom: 0.6em; +} pre { padding: 14px; } @@ -168,10 +174,12 @@ nav.sub { .sidebar { width: 200px; - position: absolute; + position: fixed; left: 0; top: 0; - min-height: 100%; + height: 100vh; + overflow: auto; + z-index: 1; } .sidebar .current { @@ -184,22 +192,29 @@ nav.sub { .js-only, .hidden { display: none !important; } -.sidebar { - padding: 10px; -} .sidebar img { margin: 20px auto; display: block; + margin-top: 10px; } .sidebar .location { border: 1px solid; font-size: 17px; - margin: 30px 0 20px 0; + margin: 30px 10px 20px 10px; text-align: center; word-wrap: break-word; } +.sidebar .version { + font-size: 15px; + text-align: center; + border-bottom: #DDDDDD 1px solid; + overflow-wrap: break-word; + word-wrap: break-word; /* deprecated */ + word-break: break-word; /* Chrome, non-standard */ +} + .location:empty { border: none; } @@ -207,7 +222,7 @@ nav.sub { .location a:first-child { font-weight: 500; } .block { - padding: 0 10px; + padding: 0; margin-bottom: 14px; } .block h2, .block h3 { @@ -216,7 +231,7 @@ nav.sub { text-align: center; } .block ul, .block li { - margin: 0; + margin: 0 10px; padding: 0; list-style: none; } @@ -232,6 +247,23 @@ nav.sub { transition: border 500ms ease-out; } +.sidebar-title { + border-top: 1px solid #777; + border-bottom: 1px solid #777; + text-align: center; + font-size: 17px; + margin-bottom: 5px; +} + +.sidebar-links { + margin-bottom: 15px; +} + +.sidebar-links > a { + padding-left: 10px; + width: 100%; +} + .content { padding: 15px 0; } @@ -310,6 +342,10 @@ h4.method > .out-of-band { font-size: 19px; } +ul.item-list > li > .out-of-band { + font-size: 19px; +} + h4 > code, h3 > code, .invisible > code { position: inherit; } @@ -378,7 +414,7 @@ h4 > code, h3 > code, .invisible > code { padding: 0; } -.content .item-list li { margin-bottom: 3px; } +.content .item-list li { margin-bottom: 1em; } .content .multi-column { -moz-column-count: 5; @@ -403,10 +439,11 @@ h4 > code, h3 > code, .invisible > code { font-size: 0.8em; } -.content .methods > div { margin-left: 40px; } +.content .methods > div:not(.important-traits) { margin-left: 40px; } .content .impl-items .docblock, .content .impl-items .stability { margin-left: 40px; + margin-bottom: .6em; } .content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant { margin-left: 20px; @@ -513,7 +550,8 @@ a { .content .search-results td:first-child { padding-right: 0; } .content .search-results td:first-child a { padding-right: 10px; } -tr.result span.primitive::after { content: ' (primitive type)'; font-style: italic; color: black; +tr.result span.primitive::after { + content: ' (primitive type)'; font-style: italic; color: black; } body.blur > :not(#help) { @@ -536,7 +574,7 @@ body.blur > :not(#help) { flex: 0 0 auto; box-shadow: 0 0 6px rgba(0,0,0,.2); width: 550px; - height: 330px; + height: 354px; border: 1px solid; } #help dt { @@ -549,13 +587,14 @@ body.blur > :not(#help) { display: block; margin-top: -1px; } -#help dd { margin: 5px 33px; } +#help dd { margin: 5px 35px; } #help .infos { padding-left: 0; } #help h1, #help h2 { margin-top: 0; } #help > div div { width: 50%; float: left; padding: 20px; + padding-left: 17px; } .stab { @@ -729,6 +768,15 @@ span.since { margin-top: 5px; } +.docblock > .section-header:first-child { + margin-left: 15px; + margin-top: 0; +} + +.docblock > .section-header:first-child:hover > a:before { + left: -10px; +} + .enum > .collapsed, .struct > .collapsed { margin-bottom: 25px; } @@ -757,17 +805,19 @@ span.since { } .sidebar { - height: 40px; + height: 45px; min-height: 40px; - width: 100%; - margin: 0px; - padding: 0px; + width: calc(100% + 30px); + margin: 0; + margin-left: -15px; + padding: 0 15px; position: static; } .sidebar .location { float: right; margin: 0px; + margin-top: 2px; padding: 3px 10px 1px 10px; min-height: 39px; background: inherit; @@ -782,7 +832,7 @@ span.since { .sidebar img { width: 35px; margin-top: 5px; - margin-bottom: 0px; + margin-bottom: 5px; float: left; } @@ -802,8 +852,8 @@ span.since { width: 100%; } - .content .out-of-band { - display: none; + .content h4 > .out-of-band { + position: inherit; } .toggle-wrapper > .collapse-toggle { @@ -813,6 +863,14 @@ span.since { .toggle-wrapper { height: 1.5em; } + + #search { + margin-left: 0; + } + + .content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant { + display: flex; + } } @media print { @@ -863,6 +921,143 @@ span.since { border-color: transparent black transparent transparent; } +.important-traits .tooltip .tooltiptext { + background-color: white; + color: black; + border: 1px solid #000; +} + pre.rust { position: relative; } + +.search-failed { + text-align: center; + margin-top: 20px; +} + +#titles { + height: 35px; +} + +#titles > div { + float: left; + width: 33.3%; + text-align: center; + border-bottom: 1px solid #ccc; + font-size: 18px; + cursor: pointer; +} + +#titles > div.selected { + border-bottom: 3px solid #0078ee; +} + +#titles > div:hover { + border-bottom: 3px solid #0089ff; +} + +#titles > div > div.count { + display: inline-block; + color: #888; + font-size: 16px; +} + +.important-traits { + cursor: pointer; + z-index: 2; +} + +h4 > .important-traits { + position: absolute; + left: -44px; + top: 2px; +} + +.modal { + position: fixed; + width: 100vw; + height: 100vh; + background-color: rgba(0,0,0,0.3); + z-index: 10000; + top: 0; + left: 0; +} + +.modal-content { + display: block; + max-width: 60%; + min-width: 200px; + background-color: #eee; + padding: 8px; + top: 40%; + position: absolute; + left: 50%; + transform: translate(-50%, -40%); + border: 1px solid #999; + border-radius: 4px; + border-top-right-radius: 0; +} + +.modal-content > .docblock { + margin: 0; +} + +h3.important { + margin: 0; + margin-bottom: 13px; + font-size: 19px; +} + +.modal-content > .docblock > code.content { + margin: 0; + padding: 0; + font-size: 20px; +} + +.modal-content > .close { + position: absolute; + font-weight: 900; + right: -25px; + top: -1px; + font-size: 18px; + background-color: #eee; + width: 25px; + padding-right: 2px; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + text-align: center; + border: 1px solid #999; + border-right: 0; + cursor: pointer; +} + +.modal-content > .close:hover { + background-color: #ff1f1f; + color: white; +} + +.modal-content > .whiter { + height: 25px; + position: absolute; + width: 3px; + background-color: #eee; + right: -2px; + top: 0px; +} + +.modal-content > .close:hover + .whiter { + background-color: #ff1f1f; +} + +#main > div.important-traits { + position: absolute; + left: -24px; + margin-top: 16px; +} + +.content > .methods > div.important-traits { + position: absolute; + left: -42px; + margin-top: 2px; +} \ No newline at end of file diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css index 42d0ec704f45f..cb19034bf0612 100644 --- a/src/librustdoc/html/static/styles/main.css +++ b/src/librustdoc/html/static/styles/main.css @@ -104,6 +104,7 @@ pre { .content .highlighted.method, .content .highlighted.tymethod { background-color: #c6afb3; } .content .highlighted.type { background-color: #ffc891; } +.content .highlighted.foreigntype { background-color: #f5c4ff; } .content .highlighted.macro { background-color: #8ce488; } .content .highlighted.constant, .content .highlighted.static { background-color: #c3e0ff; } @@ -112,6 +113,7 @@ pre { .content span.enum, .content a.enum, .block a.current.enum { color: #508157; } .content span.struct, .content a.struct, .block a.current.struct { color: #df3600; } .content span.type, .content a.type, .block a.current.type { color: #ba5d00; } +.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #cd00e2; } .content span.macro, .content a.macro, .block a.current.macro { color: #068000; } .content span.union, .content a.union, .block a.current.union { color: #767b27; } .content span.constant, .content a.constant, .block a.current.constant, @@ -235,3 +237,7 @@ pre.ignore:hover, .information:hover + pre.ignore { .information > .ignore:hover { color: rgba(255,142,0,1); } + +.search-failed > a { + color: #0089ff; +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9563ccfcc65fd..f0bb87015f805 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -14,6 +14,7 @@ html_playground_url = "https://play.rust-lang.org/")] #![deny(warnings)] +#![feature(ascii_ctype)] #![feature(rustc_private)] #![feature(box_patterns)] #![feature(box_syntax)] @@ -23,7 +24,6 @@ #![feature(test)] #![feature(unicode)] #![feature(vec_remove_item)] -#![feature(ascii_ctype)] extern crate arena; extern crate getopts; @@ -48,6 +48,7 @@ extern crate std_unicode; #[macro_use] extern crate log; extern crate rustc_errors as errors; extern crate pulldown_cmark; +extern crate tempdir; extern crate serialize as rustc_serialize; // used by deriving @@ -170,6 +171,9 @@ pub fn opts() -> Vec { stable("no-default", |o| { o.optflag("", "no-defaults", "don't run the default passes") }), + stable("document-private-items", |o| { + o.optflag("", "document-private-items", "document private items") + }), stable("test", |o| o.optflag("", "test", "run code examples as tests")), stable("test-args", |o| { o.optmulti("", "test-args", "arguments to pass to the test runner", @@ -243,6 +247,12 @@ pub fn opts() -> Vec { unstable("display-warnings", |o| { o.optflag("", "display-warnings", "to print code warnings when testing doc") }), + unstable("crate-version", |o| { + o.optopt("", "crate-version", "crate version to print into documentation", "VERSION") + }), + unstable("linker", |o| { + o.optopt("", "linker", "linker used for building executable test code", "PATH") + }), ] } @@ -269,6 +279,9 @@ pub fn main_args(args: &[String]) -> isize { // Check for unstable options. nightly_options::check_nightly_options(&matches, &opts()); + // check for deprecated options + check_deprecated_options(&matches); + if matches.opt_present("h") || matches.opt_present("help") { usage("rustdoc"); return 0; @@ -354,15 +367,16 @@ pub fn main_args(args: &[String]) -> isize { let playground_url = matches.opt_str("playground-url"); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); let display_warnings = matches.opt_present("display-warnings"); + let linker = matches.opt_str("linker"); match (should_test, markdown_input) { (true, true) => { return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, render_type, - display_warnings) + display_warnings, linker) } (true, false) => { return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot, - render_type, display_warnings) + render_type, display_warnings, linker) } (false, true) => return markdown::render(input, output.unwrap_or(PathBuf::from("doc")), @@ -451,6 +465,17 @@ where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { let mut passes = matches.opt_strs("passes"); let mut plugins = matches.opt_strs("plugins"); + // We hardcode in the passes here, as this is a new flag and we + // are generally deprecating passes. + if matches.opt_present("document-private-items") { + default_passes = false; + + passes = vec![ + String::from("collapse-docs"), + String::from("unindent-comments"), + ]; + } + // First, parse the crate and extract all relevant information. let mut paths = SearchPaths::new(); for s in &matches.opt_strs("L") { @@ -460,6 +485,7 @@ where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { let triple = matches.opt_str("target"); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); let crate_name = matches.opt_str("crate-name"); + let crate_version = matches.opt_str("crate-version"); let plugin_path = matches.opt_str("plugin-path"); let cr = PathBuf::from(cratefile); @@ -484,6 +510,8 @@ where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { krate.name = name } + krate.version = crate_version; + // Process all of the crate attributes, extracting plugin metadata along // with the passes which we are supposed to run. for attr in krate.module.as_ref().unwrap().attrs.lists("doc") { @@ -540,3 +568,26 @@ where R: 'static + Send, F: 'static + Send + FnOnce(Output) -> R { }); rx.recv().unwrap() } + +/// Prints deprecation warnings for deprecated options +fn check_deprecated_options(matches: &getopts::Matches) { + let deprecated_flags = [ + "input-format", + "output-format", + "plugin-path", + "plugins", + "no-defaults", + "passes", + ]; + + for flag in deprecated_flags.into_iter() { + if matches.opt_present(flag) { + eprintln!("WARNING: the '{}' flag is considered deprecated", flag); + eprintln!("WARNING: please see https://github.com/rust-lang/rust/issues/44136"); + } + } + + if matches.opt_present("no-defaults") { + eprintln!("WARNING: (you may want to use --document-private-items)"); + } +} diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 57e8e88cd13dd..fe6bd985bb618 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -11,7 +11,6 @@ use std::default::Default; use std::fs::File; use std::io::prelude::*; -use std::io; use std::path::{PathBuf, Path}; use getopts; @@ -75,9 +74,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, let mut out = match File::create(&output) { Err(e) => { - let _ = writeln!(&mut io::stderr(), - "rustdoc: {}: {}", - output.display(), e); + eprintln!("rustdoc: {}: {}", output.display(), e); return 4; } Ok(f) => f @@ -85,10 +82,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { - let _ = writeln!( - &mut io::stderr(), - "rustdoc: invalid markdown file: no initial lines starting with `# ` or `%`" - ); + eprintln!("rustdoc: invalid markdown file: no initial lines starting with `# ` or `%`"); return 5; } let title = metadata[0]; @@ -138,9 +132,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, match err { Err(e) => { - let _ = writeln!(&mut io::stderr(), - "rustdoc: cannot write to `{}`: {}", - output.display(), e); + eprintln!("rustdoc: cannot write to `{}`: {}", output.display(), e); 6 } Ok(_) => 0 @@ -150,7 +142,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, /// Run any tests/code examples in the markdown file `input`. pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, mut test_args: Vec, maybe_sysroot: Option, - render_type: RenderType, display_warnings: bool) -> isize { + render_type: RenderType, display_warnings: bool, linker: Option) -> isize { let input_str = match load_string(input) { Ok(s) => s, Err(LoadStringError::ReadFail) => return 1, @@ -162,7 +154,7 @@ pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, let mut collector = Collector::new(input.to_string(), cfgs, libs, externs, true, opts, maybe_sysroot, None, Some(input.to_owned()), - render_type); + render_type, linker); if render_type == RenderType::Pulldown { old_find_testable_code(&input_str, &mut collector, DUMMY_SP); find_testable_code(&input_str, &mut collector, DUMMY_SP); diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs index 3c63302127c5e..a2d651d29de93 100644 --- a/src/librustdoc/passes/collapse_docs.rs +++ b/src/librustdoc/passes/collapse_docs.rs @@ -8,10 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use clean::{self, Item}; +use clean::{self, DocFragment, Item}; use plugins; use fold; use fold::DocFolder; +use std::mem::replace; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum DocFragmentKind { + Sugared, + Raw, + Include, +} + +impl DocFragment { + fn kind(&self) -> DocFragmentKind { + match *self { + DocFragment::SugaredDoc(..) => DocFragmentKind::Sugared, + DocFragment::RawDoc(..) => DocFragmentKind::Raw, + DocFragment::Include(..) => DocFragmentKind::Include, + } + } +} pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult { Collapser.fold_crate(krate) @@ -26,15 +44,51 @@ impl fold::DocFolder for Collapser { } } -impl clean::Attributes { - pub fn collapse_doc_comments(&mut self) { - let mut doc_string = self.doc_strings.join("\n"); - if doc_string.is_empty() { - self.doc_strings = vec![]; +fn collapse(doc_strings: &mut Vec) { + let mut docs = vec![]; + let mut last_frag: Option = None; + + for frag in replace(doc_strings, vec![]) { + if let Some(mut curr_frag) = last_frag.take() { + let curr_kind = curr_frag.kind(); + let new_kind = frag.kind(); + + if curr_kind == DocFragmentKind::Include || curr_kind != new_kind { + match curr_frag { + DocFragment::SugaredDoc(_, _, ref mut doc_string) + | DocFragment::RawDoc(_, _, ref mut doc_string) => { + // add a newline for extra padding between segments + doc_string.push('\n'); + } + _ => {} + } + docs.push(curr_frag); + last_frag = Some(frag); + } else { + match curr_frag { + DocFragment::SugaredDoc(_, ref mut span, ref mut doc_string) + | DocFragment::RawDoc(_, ref mut span, ref mut doc_string) => { + doc_string.push('\n'); + doc_string.push_str(frag.as_str()); + *span = span.to(frag.span()); + } + _ => unreachable!(), + } + last_frag = Some(curr_frag); + } } else { - // FIXME(eddyb) Is this still needed? - doc_string.push('\n'); - self.doc_strings = vec![doc_string]; + last_frag = Some(frag); } } + + if let Some(frag) = last_frag.take() { + docs.push(frag); + } + *doc_strings = docs; +} + +impl clean::Attributes { + pub fn collapse_doc_comments(&mut self) { + collapse(&mut self.doc_strings); + } } diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 146629486fabd..3e15d3d3007ac 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -90,7 +90,7 @@ impl<'a> fold::DocFolder for Stripper<'a> { clean::VariantItem(..) | clean::MethodItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | clean::ConstantItem(..) | clean::UnionItem(..) | - clean::AssociatedConstItem(..) => { + clean::AssociatedConstItem(..) | clean::ForeignTypeItem => { if i.def_id.is_local() { if !self.access_levels.is_exported(i.def_id) { return None; @@ -116,7 +116,7 @@ impl<'a> fold::DocFolder for Stripper<'a> { // handled in the `strip-priv-imports` pass clean::ExternCrateItem(..) | clean::ImportItem(..) => {} - clean::DefaultImplItem(..) | clean::ImplItem(..) => {} + clean::AutoImplItem(..) | clean::ImplItem(..) => {} // tymethods/macros have no control over privacy clean::MacroItem(..) | clean::TyMethodItem(..) => {} @@ -184,6 +184,15 @@ impl<'a> fold::DocFolder for ImplStripper<'a> { return None; } } + if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { + for typaram in generics { + if let Some(did) = typaram.def_id() { + if did.is_local() && !self.retained.contains(&did) { + return None; + } + } + } + } } self.fold_item_recur(i) } diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index 59fef8d20271b..912c7646a06e5 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -12,7 +12,7 @@ use std::cmp; use std::string::String; use std::usize; -use clean::{self, Item}; +use clean::{self, DocFragment, Item}; use plugins; use fold::{self, DocFolder}; @@ -31,8 +31,17 @@ impl fold::DocFolder for CommentCleaner { impl clean::Attributes { pub fn unindent_doc_comments(&mut self) { - for doc_string in &mut self.doc_strings { - *doc_string = unindent(doc_string); + unindent_fragments(&mut self.doc_strings); + } +} + +fn unindent_fragments(docs: &mut Vec) { + for fragment in docs { + match *fragment { + DocFragment::SugaredDoc(_, _, ref mut doc_string) | + DocFragment::RawDoc(_, _, ref mut doc_string) | + DocFragment::Include(_, _, _, ref mut doc_string) => + *doc_string = unindent(doc_string), } } } diff --git a/src/librustdoc/plugins.rs b/src/librustdoc/plugins.rs index 4fc5159588d86..1a1e60a6945ed 100644 --- a/src/librustdoc/plugins.rs +++ b/src/librustdoc/plugins.rs @@ -16,7 +16,7 @@ use std::mem; use std::string::String; use std::path::PathBuf; -use rustc_back::dynamic_lib as dl; +use rustc_metadata::dynamic_lib as dl; pub type PluginResult = clean::Crate; pub type PluginCallback = fn (clean::Crate) -> PluginResult; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 7fa1b38bdadfa..74a16cb867d74 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -27,11 +27,10 @@ use rustc::hir::intravisit; use rustc::session::{self, CompileIncomplete, config}; use rustc::session::config::{OutputType, OutputTypes, Externs}; use rustc::session::search_paths::{SearchPaths, PathKind}; -use rustc_back::dynamic_lib::DynamicLibrary; -use rustc_back::tempdir::TempDir; +use rustc_metadata::dynamic_lib::DynamicLibrary; +use tempdir::TempDir; use rustc_driver::{self, driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; -use rustc_driver::pretty::ReplaceBodyWithLoop; use rustc_metadata::cstore::CStore; use rustc_resolve::MakeGlobMap; use rustc_trans; @@ -39,7 +38,6 @@ use rustc_trans::back::link; use syntax::ast; use syntax::codemap::CodeMap; use syntax::feature_gate::UnstableFeatures; -use syntax::fold::Folder; use syntax_pos::{BytePos, DUMMY_SP, Pos, Span}; use errors; use errors::emitter::ColorConfig; @@ -61,7 +59,8 @@ pub fn run(input: &str, crate_name: Option, maybe_sysroot: Option, render_type: RenderType, - display_warnings: bool) + display_warnings: bool, + linker: Option) -> isize { let input_path = PathBuf::from(input); let input = config::Input::File(input_path.clone()); @@ -80,7 +79,9 @@ pub fn run(input: &str, let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping())); let handler = - errors::Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); + errors::Handler::with_tty_emitter(ColorConfig::Auto, + true, false, + Some(codemap.clone())); let cstore = Rc::new(CStore::new(box rustc_trans::LlvmMetadataLoader)); let mut sess = session::build_session_( @@ -94,7 +95,6 @@ pub fn run(input: &str, let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input)); - let krate = ReplaceBodyWithLoop::new().fold_crate(krate); let driver::ExpansionResult { defs, mut hir_forest, .. } = { phase_2_configure_and_expand( &sess, @@ -121,7 +121,8 @@ pub fn run(input: &str, maybe_sysroot, Some(codemap), None, - render_type); + render_type, + linker); { let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs); @@ -180,10 +181,13 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec, libs externs: Externs, should_panic: bool, no_run: bool, as_test_harness: bool, compile_fail: bool, mut error_codes: Vec, opts: &TestOptions, - maybe_sysroot: Option) { + maybe_sysroot: Option, + linker: Option) { // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` let test = make_test(test, Some(cratename), as_test_harness, opts); + // FIXME(#44940): if doctests ever support path remapping, then this filename + // needs to be the result of CodeMap::span_to_unmapped_path let input = config::Input::Str { name: filename.to_owned(), input: test.to_owned(), @@ -199,6 +203,7 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec, libs externs, cg: config::CodegenOptions { prefer_dynamic: true, + linker, .. config::basic_codegen_options() }, test: as_test_harness, @@ -232,7 +237,8 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec, libs let data = Arc::new(Mutex::new(Vec::new())); let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping())); let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()), - Some(codemap.clone())); + Some(codemap.clone()), + false); let old = io::set_panic(Some(box Sink(data.clone()))); let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout())); @@ -330,15 +336,23 @@ pub fn make_test(s: &str, let mut prog = String::new(); - // First push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + } - // Next, any attributes for other aspects such as lints. + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. for attr in &opts.attrs { prog.push_str(&format!("#![{}]\n", attr)); } + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&crate_attrs); + // Don't inject `extern crate std` because it's already injected by the // compiler. if !s.contains("extern crate") && !opts.no_crate_inject && cratename != Some("std") { @@ -405,13 +419,33 @@ pub struct Collector { pub tests: Vec, // to be removed when hoedown will be definitely gone pub old_tests: HashMap>, + + // The name of the test displayed to the user, separated by `::`. + // + // In tests from Rust source, this is the path to the item + // e.g. `["std", "vec", "Vec", "push"]`. + // + // In tests from a markdown file, this is the titles of all headers (h1~h6) + // of the sections that contain the code block, e.g. if the markdown file is + // written as: + // + // ``````markdown + // # Title + // + // ## Subtitle + // + // ```rust + // assert!(true); + // ``` + // `````` + // + // the `names` vector of that test will be `["Title", "Subtitle"]`. names: Vec, + cfgs: Vec, libs: SearchPaths, externs: Externs, - cnt: usize, use_headers: bool, - current_header: Option, cratename: String, opts: TestOptions, maybe_sysroot: Option, @@ -420,13 +454,14 @@ pub struct Collector { filename: Option, // to be removed when hoedown will be removed as well pub render_type: RenderType, + linker: Option, } impl Collector { pub fn new(cratename: String, cfgs: Vec, libs: SearchPaths, externs: Externs, use_headers: bool, opts: TestOptions, maybe_sysroot: Option, codemap: Option>, filename: Option, - render_type: RenderType) -> Collector { + render_type: RenderType, linker: Option) -> Collector { Collector { tests: Vec::new(), old_tests: HashMap::new(), @@ -434,9 +469,7 @@ impl Collector { cfgs, libs, externs, - cnt: 0, use_headers, - current_header: None, cratename, opts, maybe_sysroot, @@ -444,32 +477,17 @@ impl Collector { codemap, filename, render_type, + linker, } } fn generate_name(&self, line: usize, filename: &str) -> String { - if self.use_headers { - if let Some(ref header) = self.current_header { - format!("{} - {} (line {})", filename, header, line) - } else { - format!("{} - (line {})", filename, line) - } - } else { - format!("{} - {} (line {})", filename, self.names.join("::"), line) - } + format!("{} - {} (line {})", filename, self.names.join("::"), line) } // to be removed once hoedown is gone fn generate_name_beginning(&self, filename: &str) -> String { - if self.use_headers { - if let Some(ref header) = self.current_header { - format!("{} - {} (line", filename, header) - } else { - format!("{} - (line", filename) - } - } else { - format!("{} - {} (line", filename, self.names.join("::")) - } + format!("{} - {} (line", filename, self.names.join("::")) } pub fn add_old_test(&mut self, test: String, filename: String) { @@ -493,11 +511,10 @@ impl Collector { found = entry.remove_item(&test).is_some(); } if !found { - let _ = writeln!(&mut io::stderr(), - "WARNING: {} Code block is not currently run as a test, but will \ - in future versions of rustdoc. Please ensure this code block is \ - a runnable test, or use the `ignore` directive.", - name); + eprintln!("WARNING: {} Code block is not currently run as a test, but will \ + in future versions of rustdoc. Please ensure this code block is \ + a runnable test, or use the `ignore` directive.", + name); return } } @@ -507,6 +524,7 @@ impl Collector { let cratename = self.cratename.to_string(); let opts = self.opts.clone(); let maybe_sysroot = self.maybe_sysroot.clone(); + let linker = self.linker.clone(); debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -535,7 +553,8 @@ impl Collector { compile_fail, error_codes, &opts, - maybe_sysroot) + maybe_sysroot, + linker) }) } { Ok(()) => (), @@ -578,7 +597,7 @@ impl Collector { } pub fn register_header(&mut self, name: &str, level: u32) { - if self.use_headers && level == 1 { + if self.use_headers { // we use these headings as test names, so it's good if // they're valid identifiers. let name = name.chars().enumerate().map(|(i, c)| { @@ -590,9 +609,28 @@ impl Collector { } }).collect::(); - // new header => reset count. - self.cnt = 0; - self.current_header = Some(name); + // Here we try to efficiently assemble the header titles into the + // test name in the form of `h1::h2::h3::h4::h5::h6`. + // + // Suppose originally `self.names` contains `[h1, h2, h3]`... + let level = level as usize; + if level <= self.names.len() { + // ... Consider `level == 2`. All headers in the lower levels + // are irrelevant in this new level. So we should reset + // `self.names` to contain headers until

, and replace that + // slot with the new name: `[h1, name]`. + self.names.truncate(level); + self.names[level - 1] = name; + } else { + // ... On the other hand, consider `level == 5`. This means we + // need to extend `self.names` to contain five headers. We fill + // in the missing level (

) with `_`. Thus `self.names` will + // become `[h1, h2, h3, "_", name]`. + if level - 1 > self.names.len() { + self.names.resize(level - 1, "_".to_owned()); + } + self.names.push(name); + } } } } @@ -622,15 +660,16 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { attrs.collapse_doc_comments(); attrs.unindent_doc_comments(); - if let Some(doc) = attrs.doc_value() { - self.collector.cnt = 0; + // the collapse-docs pass won't combine sugared/raw doc attributes, or included files with + // anything else, this will combine them for us + if let Some(doc) = attrs.collapsed_doc_value() { if self.collector.render_type == RenderType::Pulldown { - markdown::old_find_testable_code(doc, self.collector, + markdown::old_find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); - markdown::find_testable_code(doc, self.collector, + markdown::find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); } else { - markdown::old_find_testable_code(doc, self.collector, + markdown::old_find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 327a330c2a2e5..fe1dac36be1f9 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -306,6 +306,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { Def::Struct(did) | Def::Union(did) | Def::Enum(did) | + Def::TyForeign(did) | Def::TyAlias(did) if !self_is_hidden => { self.cx.access_levels.borrow_mut().map.insert(did, AccessLevel::Public); }, @@ -348,6 +349,17 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.inlining = prev; true } + hir_map::NodeForeignItem(it) if !glob => { + // generate a fresh `extern {}` block if we want to inline a foreign item. + om.foreigns.push(hir::ForeignMod { + abi: tcx.hir.get_foreign_abi(it.id), + items: vec![hir::ForeignItem { + name: renamed.unwrap_or(it.name), + .. it.clone() + }].into(), + }); + true + } _ => false, }; self.view_item_stack.remove(&def_node_id); @@ -481,7 +493,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; om.constants.push(s); }, - hir::ItemTrait(unsafety, ref gen, ref b, ref item_ids) => { + hir::ItemTrait(_, unsafety, ref gen, ref b, ref item_ids) => { let items = item_ids.iter() .map(|ti| self.cx.tcx.hir.trait_item(ti.id).clone()) .collect(); @@ -532,10 +544,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { om.impls.push(i); } }, - hir::ItemDefaultImpl(unsafety, ref trait_ref) => { + hir::ItemAutoImpl(unsafety, ref trait_ref) => { // See comment above about ItemImpl. if !self.inlining { - let i = DefaultImpl { + let i = AutoImpl { unsafety, trait_: trait_ref.clone(), id: item.id, diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs index 1a995276931d8..d8ae9729224d7 100644 --- a/src/libserialize/collection_impls.rs +++ b/src/libserialize/collection_impls.rs @@ -14,6 +14,7 @@ use std::hash::{Hash, BuildHasher}; use {Decodable, Encodable, Decoder, Encoder}; use std::collections::{LinkedList, VecDeque, BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; impl< T: Encodable @@ -194,3 +195,26 @@ impl Decodable for HashSet }) } } + +impl Encodable for Rc<[T]> { + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_seq(self.len(), |s| { + for (index, e) in self.iter().enumerate() { + s.emit_seq_elt(index, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl Decodable for Rc<[T]> { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut vec = Vec::with_capacity(len); + for index in 0..len { + vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); + } + Ok(vec.into()) + }) + } +} diff --git a/src/libserialize/opaque.rs b/src/libserialize/opaque.rs index f3475bd18ce69..99557659b297b 100644 --- a/src/libserialize/opaque.rs +++ b/src/libserialize/opaque.rs @@ -162,6 +162,10 @@ impl<'a> Decoder<'a> { self.position } + pub fn set_position(&mut self, pos: usize) { + self.position = pos + } + pub fn advance(&mut self, bytes: usize) { self.position += bytes; } diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index fb276448ffac4..3430ecabcbeae 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -3,6 +3,9 @@ authors = ["The Rust Project Developers"] name = "std" version = "0.0.0" build = "build.rs" +license = "MIT/Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library" [lib] name = "std" @@ -15,15 +18,16 @@ alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } alloc_system = { path = "../liballoc_system" } panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } -collections = { path = "../libcollections" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } -rand = { path = "../librand" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } std_unicode = { path = "../libstd_unicode" } unwind = { path = "../libunwind" } +[dev-dependencies] +rand = "0.3" + [target.x86_64-apple-darwin.dependencies] rustc_asan = { path = "../librustc_asan" } rustc_tsan = { path = "../librustc_tsan" } @@ -36,7 +40,6 @@ rustc_tsan = { path = "../librustc_tsan" } [build-dependencies] build_helper = { path = "../build_helper" } -cc = "1.0" [features] backtrace = [] diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 4e3781ecafab5..92507a73bae5f 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -38,8 +38,8 @@ use iter::FusedIterator; /// ``` /// use std::ascii::AsciiExt; /// -/// assert_eq!("café".to_ascii_uppercase(), "CAFÉ"); -/// assert_eq!("café".to_ascii_uppercase(), "CAFé"); +/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFÉ"); +/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFé"); /// ``` /// /// In the first example, the lowercased string is represented `"cafe\u{301}"` @@ -60,19 +60,10 @@ pub trait AsciiExt { /// Checks if the value is within the ASCII range. /// - /// # Examples + /// # Note /// - /// ``` - /// use std::ascii::AsciiExt; - /// - /// let ascii = 'a'; - /// let non_ascii = '❤'; - /// let int_ascii = 97; - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// assert!(int_ascii.is_ascii()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[stable(feature = "rust1", since = "1.0.0")] fn is_ascii(&self) -> bool; @@ -86,19 +77,10 @@ pub trait AsciiExt { /// To uppercase ASCII characters in addition to non-ASCII characters, use /// [`str::to_uppercase`]. /// - /// # Examples - /// - /// ``` - /// use std::ascii::AsciiExt; - /// - /// let ascii = 'a'; - /// let non_ascii = '❤'; - /// let int_ascii = 97; + /// # Note /// - /// assert_eq!('A', ascii.to_ascii_uppercase()); - /// assert_eq!('❤', non_ascii.to_ascii_uppercase()); - /// assert_eq!(65, int_ascii.to_ascii_uppercase()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. /// /// [`make_ascii_uppercase`]: #tymethod.make_ascii_uppercase /// [`str::to_uppercase`]: ../primitive.str.html#method.to_uppercase @@ -115,19 +97,10 @@ pub trait AsciiExt { /// To lowercase ASCII characters in addition to non-ASCII characters, use /// [`str::to_lowercase`]. /// - /// # Examples + /// # Note /// - /// ``` - /// use std::ascii::AsciiExt; - /// - /// let ascii = 'A'; - /// let non_ascii = '❤'; - /// let int_ascii = 65; - /// - /// assert_eq!('a', ascii.to_ascii_lowercase()); - /// assert_eq!('❤', non_ascii.to_ascii_lowercase()); - /// assert_eq!(97, int_ascii.to_ascii_lowercase()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. /// /// [`make_ascii_lowercase`]: #tymethod.make_ascii_lowercase /// [`str::to_lowercase`]: ../primitive.str.html#method.to_lowercase @@ -139,20 +112,10 @@ pub trait AsciiExt { /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, /// but without allocating and copying temporaries. /// - /// # Examples - /// - /// ``` - /// use std::ascii::AsciiExt; + /// # Note /// - /// let ascii1 = 'A'; - /// let ascii2 = 'a'; - /// let ascii3 = 'A'; - /// let ascii4 = 'z'; - /// - /// assert!(ascii1.eq_ignore_ascii_case(&ascii2)); - /// assert!(ascii1.eq_ignore_ascii_case(&ascii3)); - /// assert!(!ascii1.eq_ignore_ascii_case(&ascii4)); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[stable(feature = "rust1", since = "1.0.0")] fn eq_ignore_ascii_case(&self, other: &Self) -> bool; @@ -164,17 +127,10 @@ pub trait AsciiExt { /// To return a new uppercased value without modifying the existing one, use /// [`to_ascii_uppercase`]. /// - /// # Examples - /// - /// ``` - /// use std::ascii::AsciiExt; - /// - /// let mut ascii = 'a'; + /// # Note /// - /// ascii.make_ascii_uppercase(); - /// - /// assert_eq!('A', ascii); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. /// /// [`to_ascii_uppercase`]: #tymethod.to_ascii_uppercase #[stable(feature = "ascii", since = "1.9.0")] @@ -188,17 +144,10 @@ pub trait AsciiExt { /// To return a new lowercased value without modifying the existing one, use /// [`to_ascii_lowercase`]. /// - /// # Examples - /// - /// ``` - /// use std::ascii::AsciiExt; - /// - /// let mut ascii = 'A'; + /// # Note /// - /// ascii.make_ascii_lowercase(); - /// - /// assert_eq!('a', ascii); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. /// /// [`to_ascii_lowercase`]: #tymethod.to_ascii_lowercase #[stable(feature = "ascii", since = "1.9.0")] @@ -209,32 +158,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII alphabetic. /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(A.is_ascii_alphabetic()); - /// assert!(G.is_ascii_alphabetic()); - /// assert!(a.is_ascii_alphabetic()); - /// assert!(g.is_ascii_alphabetic()); - /// assert!(!zero.is_ascii_alphabetic()); - /// assert!(!percent.is_ascii_alphabetic()); - /// assert!(!space.is_ascii_alphabetic()); - /// assert!(!lf.is_ascii_alphabetic()); - /// assert!(!esc.is_ascii_alphabetic()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_alphabetic(&self) -> bool { unimplemented!(); } @@ -243,32 +170,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII uppercase. /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(A.is_ascii_uppercase()); - /// assert!(G.is_ascii_uppercase()); - /// assert!(!a.is_ascii_uppercase()); - /// assert!(!g.is_ascii_uppercase()); - /// assert!(!zero.is_ascii_uppercase()); - /// assert!(!percent.is_ascii_uppercase()); - /// assert!(!space.is_ascii_uppercase()); - /// assert!(!lf.is_ascii_uppercase()); - /// assert!(!esc.is_ascii_uppercase()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_uppercase(&self) -> bool { unimplemented!(); } @@ -277,32 +182,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII lowercase. /// - /// # Examples + /// # Note /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; - /// - /// assert!(!A.is_ascii_lowercase()); - /// assert!(!G.is_ascii_lowercase()); - /// assert!(a.is_ascii_lowercase()); - /// assert!(g.is_ascii_lowercase()); - /// assert!(!zero.is_ascii_lowercase()); - /// assert!(!percent.is_ascii_lowercase()); - /// assert!(!space.is_ascii_lowercase()); - /// assert!(!lf.is_ascii_lowercase()); - /// assert!(!esc.is_ascii_lowercase()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_lowercase(&self) -> bool { unimplemented!(); } @@ -312,32 +195,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII alphanumeric. /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(A.is_ascii_alphanumeric()); - /// assert!(G.is_ascii_alphanumeric()); - /// assert!(a.is_ascii_alphanumeric()); - /// assert!(g.is_ascii_alphanumeric()); - /// assert!(zero.is_ascii_alphanumeric()); - /// assert!(!percent.is_ascii_alphanumeric()); - /// assert!(!space.is_ascii_alphanumeric()); - /// assert!(!lf.is_ascii_alphanumeric()); - /// assert!(!esc.is_ascii_alphanumeric()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_alphanumeric(&self) -> bool { unimplemented!(); } @@ -346,32 +207,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII digits. /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(!A.is_ascii_digit()); - /// assert!(!G.is_ascii_digit()); - /// assert!(!a.is_ascii_digit()); - /// assert!(!g.is_ascii_digit()); - /// assert!(zero.is_ascii_digit()); - /// assert!(!percent.is_ascii_digit()); - /// assert!(!space.is_ascii_digit()); - /// assert!(!lf.is_ascii_digit()); - /// assert!(!esc.is_ascii_digit()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_digit(&self) -> bool { unimplemented!(); } @@ -381,69 +220,27 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII hex digits. /// - /// # Examples + /// # Note /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; - /// - /// assert!(A.is_ascii_hexdigit()); - /// assert!(!G.is_ascii_hexdigit()); - /// assert!(a.is_ascii_hexdigit()); - /// assert!(!g.is_ascii_hexdigit()); - /// assert!(zero.is_ascii_hexdigit()); - /// assert!(!percent.is_ascii_hexdigit()); - /// assert!(!space.is_ascii_hexdigit()); - /// assert!(!lf.is_ascii_hexdigit()); - /// assert!(!esc.is_ascii_hexdigit()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_hexdigit(&self) -> bool { unimplemented!(); } /// Checks if the value is an ASCII punctuation character: + /// /// U+0021 ... U+002F `! " # $ % & ' ( ) * + , - . /` /// U+003A ... U+0040 `: ; < = > ? @` - /// U+005B ... U+0060 `[ \\ ] ^ _ \`` + /// U+005B ... U+0060 ``[ \\ ] ^ _ ` `` /// U+007B ... U+007E `{ | } ~` + /// /// For strings, true if all characters in the string are /// ASCII punctuation. /// - /// # Examples + /// # Note /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; - /// - /// assert!(!A.is_ascii_punctuation()); - /// assert!(!G.is_ascii_punctuation()); - /// assert!(!a.is_ascii_punctuation()); - /// assert!(!g.is_ascii_punctuation()); - /// assert!(!zero.is_ascii_punctuation()); - /// assert!(percent.is_ascii_punctuation()); - /// assert!(!space.is_ascii_punctuation()); - /// assert!(!lf.is_ascii_punctuation()); - /// assert!(!esc.is_ascii_punctuation()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_punctuation(&self) -> bool { unimplemented!(); } @@ -452,32 +249,10 @@ pub trait AsciiExt { /// For strings, true if all characters in the string are /// ASCII punctuation. /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(A.is_ascii_graphic()); - /// assert!(G.is_ascii_graphic()); - /// assert!(a.is_ascii_graphic()); - /// assert!(g.is_ascii_graphic()); - /// assert!(zero.is_ascii_graphic()); - /// assert!(percent.is_ascii_graphic()); - /// assert!(!space.is_ascii_graphic()); - /// assert!(!lf.is_ascii_graphic()); - /// assert!(!esc.is_ascii_graphic()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_graphic(&self) -> bool { unimplemented!(); } @@ -503,32 +278,10 @@ pub trait AsciiExt { /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 /// - /// # Examples - /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; + /// # Note /// - /// assert!(!A.is_ascii_whitespace()); - /// assert!(!G.is_ascii_whitespace()); - /// assert!(!a.is_ascii_whitespace()); - /// assert!(!g.is_ascii_whitespace()); - /// assert!(!zero.is_ascii_whitespace()); - /// assert!(!percent.is_ascii_whitespace()); - /// assert!(space.is_ascii_whitespace()); - /// assert!(lf.is_ascii_whitespace()); - /// assert!(!esc.is_ascii_whitespace()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_whitespace(&self) -> bool { unimplemented!(); } @@ -537,168 +290,91 @@ pub trait AsciiExt { /// Note that most ASCII whitespace characters are control /// characters, but SPACE is not. /// - /// # Examples + /// # Note /// - /// ``` - /// #![feature(ascii_ctype)] - /// # #![allow(non_snake_case)] - /// use std::ascii::AsciiExt; - /// let A = 'A'; - /// let G = 'G'; - /// let a = 'a'; - /// let g = 'g'; - /// let zero = '0'; - /// let percent = '%'; - /// let space = ' '; - /// let lf = '\n'; - /// let esc = '\u{001b}'; - /// - /// assert!(!A.is_ascii_control()); - /// assert!(!G.is_ascii_control()); - /// assert!(!a.is_ascii_control()); - /// assert!(!g.is_ascii_control()); - /// assert!(!zero.is_ascii_control()); - /// assert!(!percent.is_ascii_control()); - /// assert!(!space.is_ascii_control()); - /// assert!(lf.is_ascii_control()); - /// assert!(esc.is_ascii_control()); - /// ``` + /// This method will be deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. #[unstable(feature = "ascii_ctype", issue = "39658")] fn is_ascii_control(&self) -> bool { unimplemented!(); } } -#[stable(feature = "rust1", since = "1.0.0")] -impl AsciiExt for str { - type Owned = String; - - #[inline] - fn is_ascii(&self) -> bool { - self.bytes().all(|b| b.is_ascii()) - } +macro_rules! delegating_ascii_methods { + () => { + #[inline] + fn is_ascii(&self) -> bool { self.is_ascii() } - #[inline] - fn to_ascii_uppercase(&self) -> String { - let mut bytes = self.as_bytes().to_vec(); - bytes.make_ascii_uppercase(); - // make_ascii_uppercase() preserves the UTF-8 invariant. - unsafe { String::from_utf8_unchecked(bytes) } - } + #[inline] + fn to_ascii_uppercase(&self) -> Self::Owned { self.to_ascii_uppercase() } - #[inline] - fn to_ascii_lowercase(&self) -> String { - let mut bytes = self.as_bytes().to_vec(); - bytes.make_ascii_lowercase(); - // make_ascii_uppercase() preserves the UTF-8 invariant. - unsafe { String::from_utf8_unchecked(bytes) } - } + #[inline] + fn to_ascii_lowercase(&self) -> Self::Owned { self.to_ascii_lowercase() } - #[inline] - fn eq_ignore_ascii_case(&self, other: &str) -> bool { - self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) - } + #[inline] + fn eq_ignore_ascii_case(&self, o: &Self) -> bool { self.eq_ignore_ascii_case(o) } - fn make_ascii_uppercase(&mut self) { - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_uppercase() - } + #[inline] + fn make_ascii_uppercase(&mut self) { self.make_ascii_uppercase(); } - fn make_ascii_lowercase(&mut self) { - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_lowercase() + #[inline] + fn make_ascii_lowercase(&mut self) { self.make_ascii_lowercase(); } } +} - #[inline] - fn is_ascii_alphabetic(&self) -> bool { - self.bytes().all(|b| b.is_ascii_alphabetic()) - } +macro_rules! delegating_ascii_ctype_methods { + () => { + #[inline] + fn is_ascii_alphabetic(&self) -> bool { self.is_ascii_alphabetic() } - #[inline] - fn is_ascii_uppercase(&self) -> bool { - self.bytes().all(|b| b.is_ascii_uppercase()) - } + #[inline] + fn is_ascii_uppercase(&self) -> bool { self.is_ascii_uppercase() } - #[inline] - fn is_ascii_lowercase(&self) -> bool { - self.bytes().all(|b| b.is_ascii_lowercase()) - } + #[inline] + fn is_ascii_lowercase(&self) -> bool { self.is_ascii_lowercase() } - #[inline] - fn is_ascii_alphanumeric(&self) -> bool { - self.bytes().all(|b| b.is_ascii_alphanumeric()) - } + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { self.is_ascii_alphanumeric() } - #[inline] - fn is_ascii_digit(&self) -> bool { - self.bytes().all(|b| b.is_ascii_digit()) - } + #[inline] + fn is_ascii_digit(&self) -> bool { self.is_ascii_digit() } - #[inline] - fn is_ascii_hexdigit(&self) -> bool { - self.bytes().all(|b| b.is_ascii_hexdigit()) - } + #[inline] + fn is_ascii_hexdigit(&self) -> bool { self.is_ascii_hexdigit() } - #[inline] - fn is_ascii_punctuation(&self) -> bool { - self.bytes().all(|b| b.is_ascii_punctuation()) - } + #[inline] + fn is_ascii_punctuation(&self) -> bool { self.is_ascii_punctuation() } - #[inline] - fn is_ascii_graphic(&self) -> bool { - self.bytes().all(|b| b.is_ascii_graphic()) - } + #[inline] + fn is_ascii_graphic(&self) -> bool { self.is_ascii_graphic() } - #[inline] - fn is_ascii_whitespace(&self) -> bool { - self.bytes().all(|b| b.is_ascii_whitespace()) - } + #[inline] + fn is_ascii_whitespace(&self) -> bool { self.is_ascii_whitespace() } - #[inline] - fn is_ascii_control(&self) -> bool { - self.bytes().all(|b| b.is_ascii_control()) + #[inline] + fn is_ascii_control(&self) -> bool { self.is_ascii_control() } } } #[stable(feature = "rust1", since = "1.0.0")] -impl AsciiExt for [u8] { - type Owned = Vec; - #[inline] - fn is_ascii(&self) -> bool { - self.iter().all(|b| b.is_ascii()) - } +impl AsciiExt for u8 { + type Owned = u8; - #[inline] - fn to_ascii_uppercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_uppercase(); - return me - } + delegating_ascii_methods!(); + delegating_ascii_ctype_methods!(); +} - #[inline] - fn to_ascii_lowercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_lowercase(); - return me - } +#[stable(feature = "rust1", since = "1.0.0")] +impl AsciiExt for char { + type Owned = char; - #[inline] - fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { - self.len() == other.len() && - self.iter().zip(other).all(|(a, b)| { - a.eq_ignore_ascii_case(b) - }) - } + delegating_ascii_methods!(); + delegating_ascii_ctype_methods!(); +} - fn make_ascii_uppercase(&mut self) { - for byte in self { - byte.make_ascii_uppercase(); - } - } +#[stable(feature = "rust1", since = "1.0.0")] +impl AsciiExt for [u8] { + type Owned = Vec; - fn make_ascii_lowercase(&mut self) { - for byte in self { - byte.make_ascii_lowercase(); - } - } + delegating_ascii_methods!(); #[inline] fn is_ascii_alphabetic(&self) -> bool { @@ -752,198 +428,59 @@ impl AsciiExt for [u8] { } #[stable(feature = "rust1", since = "1.0.0")] -impl AsciiExt for u8 { - type Owned = u8; - #[inline] - fn is_ascii(&self) -> bool { *self & 128 == 0 } - #[inline] - fn to_ascii_uppercase(&self) -> u8 { ASCII_UPPERCASE_MAP[*self as usize] } - #[inline] - fn to_ascii_lowercase(&self) -> u8 { ASCII_LOWERCASE_MAP[*self as usize] } - #[inline] - fn eq_ignore_ascii_case(&self, other: &u8) -> bool { - self.to_ascii_lowercase() == other.to_ascii_lowercase() - } - #[inline] - fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } - #[inline] - fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } - - #[inline] - fn is_ascii_alphabetic(&self) -> bool { - if *self >= 0x80 { return false; } - match ASCII_CHARACTER_CLASS[*self as usize] { - L|Lx|U|Ux => true, - _ => false - } - } - - #[inline] - fn is_ascii_uppercase(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - U|Ux => true, - _ => false - } - } - - #[inline] - fn is_ascii_lowercase(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - L|Lx => true, - _ => false - } - } - - #[inline] - fn is_ascii_alphanumeric(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - D|L|Lx|U|Ux => true, - _ => false - } - } - - #[inline] - fn is_ascii_digit(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - D => true, - _ => false - } - } - - #[inline] - fn is_ascii_hexdigit(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - D|Lx|Ux => true, - _ => false - } - } - - #[inline] - fn is_ascii_punctuation(&self) -> bool { - if *self >= 0x80 { return false } - match ASCII_CHARACTER_CLASS[*self as usize] { - P => true, - _ => false - } - } - - #[inline] - fn is_ascii_graphic(&self) -> bool { - if *self >= 0x80 { return false; } - match ASCII_CHARACTER_CLASS[*self as usize] { - Ux|U|Lx|L|D|P => true, - _ => false - } - } - - #[inline] - fn is_ascii_whitespace(&self) -> bool { - if *self >= 0x80 { return false; } - match ASCII_CHARACTER_CLASS[*self as usize] { - Cw|W => true, - _ => false - } - } - - #[inline] - fn is_ascii_control(&self) -> bool { - if *self >= 0x80 { return false; } - match ASCII_CHARACTER_CLASS[*self as usize] { - C|Cw => true, - _ => false - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsciiExt for char { - type Owned = char; - #[inline] - fn is_ascii(&self) -> bool { - *self as u32 <= 0x7F - } - - #[inline] - fn to_ascii_uppercase(&self) -> char { - if self.is_ascii() { - (*self as u8).to_ascii_uppercase() as char - } else { - *self - } - } - - #[inline] - fn to_ascii_lowercase(&self) -> char { - if self.is_ascii() { - (*self as u8).to_ascii_lowercase() as char - } else { - *self - } - } - - #[inline] - fn eq_ignore_ascii_case(&self, other: &char) -> bool { - self.to_ascii_lowercase() == other.to_ascii_lowercase() - } +impl AsciiExt for str { + type Owned = String; - #[inline] - fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } - #[inline] - fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } + delegating_ascii_methods!(); #[inline] fn is_ascii_alphabetic(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphabetic() + self.bytes().all(|b| b.is_ascii_alphabetic()) } #[inline] fn is_ascii_uppercase(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_uppercase() + self.bytes().all(|b| b.is_ascii_uppercase()) } #[inline] fn is_ascii_lowercase(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_lowercase() + self.bytes().all(|b| b.is_ascii_lowercase()) } #[inline] fn is_ascii_alphanumeric(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphanumeric() + self.bytes().all(|b| b.is_ascii_alphanumeric()) } #[inline] fn is_ascii_digit(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_digit() + self.bytes().all(|b| b.is_ascii_digit()) } #[inline] fn is_ascii_hexdigit(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_hexdigit() + self.bytes().all(|b| b.is_ascii_hexdigit()) } #[inline] fn is_ascii_punctuation(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_punctuation() + self.bytes().all(|b| b.is_ascii_punctuation()) } #[inline] fn is_ascii_graphic(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_graphic() + self.bytes().all(|b| b.is_ascii_graphic()) } #[inline] fn is_ascii_whitespace(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_whitespace() + self.bytes().all(|b| b.is_ascii_whitespace()) } #[inline] fn is_ascii_control(&self) -> bool { - (*self as u32 <= 0x7f) && (*self as u8).is_ascii_control() + self.bytes().all(|b| b.is_ascii_control()) } } @@ -1064,112 +601,12 @@ impl fmt::Debug for EscapeDefault { } -static ASCII_LOWERCASE_MAP: [u8; 256] = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'', - b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', - b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', - b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', - b'@', - - b'a', b'b', b'c', b'd', b'e', b'f', b'g', - b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', - b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', - b'x', b'y', b'z', - - b'[', b'\\', b']', b'^', b'_', - b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', - b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', - b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', - b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -static ASCII_UPPERCASE_MAP: [u8; 256] = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'', - b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', - b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', - b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', - b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', - b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', - b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', - b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_', - b'`', - - b'A', b'B', b'C', b'D', b'E', b'F', b'G', - b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', - b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', - b'X', b'Y', b'Z', - - b'{', b'|', b'}', b'~', 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, -]; - -enum AsciiCharacterClass { - C, // control - Cw, // control whitespace - W, // whitespace - D, // digit - L, // lowercase - Lx, // lowercase hex digit - U, // uppercase - Ux, // uppercase hex digit - P, // punctuation -} -use self::AsciiCharacterClass::*; - -static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 128] = [ -// _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _a _b _c _d _e _f - C, C, C, C, C, C, C, C, C, Cw,Cw,C, Cw,Cw,C, C, // 0_ - C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // 1_ - W, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, // 2_ - D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, // 3_ - P, Ux,Ux,Ux,Ux,Ux,Ux,U, U, U, U, U, U, U, U, U, // 4_ - U, U, U, U, U, U, U, U, U, U, U, P, P, P, P, P, // 5_ - P, Lx,Lx,Lx,Lx,Lx,Lx,L, L, L, L, L, L, L, L, L, // 6_ - L, L, L, L, L, L, L, L, L, L, L, P, P, P, P, C, // 7_ -]; - #[cfg(test)] mod tests { - use super::*; + //! Note that most of these tests are not testing `AsciiExt` methods, but + //! test inherent ascii methods of char, u8, str and [u8]. `AsciiExt` is + //! just using those methods, though. + use super::AsciiExt; use char::from_u32; #[test] diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 7ca762c801a81..06f11c8deb458 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -11,7 +11,6 @@ #![deny(warnings)] extern crate build_helper; -extern crate cc; use std::env; use std::process::Command; @@ -20,8 +19,12 @@ use build_helper::{run, native_lib_boilerplate, BuildExpectation}; fn main() { let target = env::var("TARGET").expect("TARGET was not set"); let host = env::var("HOST").expect("HOST was not set"); - if cfg!(feature = "backtrace") && !target.contains("msvc") && - !target.contains("emscripten") && !target.contains("fuchsia") { + if cfg!(feature = "backtrace") && + !target.contains("msvc") && + !target.contains("emscripten") && + !target.contains("fuchsia") && + !target.contains("wasm32") + { let _ = build_libbacktrace(&host, &target); } @@ -77,12 +80,6 @@ fn main() { fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> { let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?; - let compiler = cc::Build::new().get_compiler(); - // only msvc returns None for ar so unwrap is okay - let ar = build_helper::cc2ar(compiler.path(), target).unwrap(); - let mut cflags = compiler.args().iter().map(|s| s.to_str().unwrap()) - .collect::>().join(" "); - cflags.push_str(" -fvisibility=hidden"); run(Command::new("sh") .current_dir(&native.out_dir) .arg(native.src_dir.join("configure").to_str().unwrap() @@ -94,10 +91,7 @@ fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> { .arg("--disable-host-shared") .arg(format!("--host={}", build_helper::gnu_target(target))) .arg(format!("--build={}", build_helper::gnu_target(host))) - .env("CC", compiler.path()) - .env("AR", &ar) - .env("RANLIB", format!("{} s", ar.display())) - .env("CFLAGS", cflags), + .env("CFLAGS", env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden"), BuildExpectation::None); run(Command::new(build_helper::make(host)) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 026b863b96371..7a79a472d58d9 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -20,8 +20,8 @@ use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; use ops::{Deref, Index, InPlace, Place, Placer}; -use rand::{self, Rng}; use ptr; +use sys; use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; use super::table::BucketState::{Empty, Full}; @@ -691,6 +691,17 @@ impl HashMap /// Returns a reference to the map's [`BuildHasher`]. /// /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::RandomState; + /// + /// let hasher = RandomState::new(); + /// let map: HashMap = HashMap::with_hasher(hasher); + /// let hasher: &RandomState = map.hasher(); + /// ``` #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { &self.hash_builder @@ -1102,6 +1113,7 @@ impl HashMap /// assert_eq!(map.get(&2), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn get(&self, k: &Q) -> Option<&V> where K: Borrow, Q: Hash + Eq @@ -1350,7 +1362,7 @@ pub struct Iter<'a, K: 'a, V: 'a> { inner: table::Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { @@ -1403,7 +1415,7 @@ pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Keys<'a, K, V> { fn clone(&self) -> Keys<'a, K, V> { @@ -1432,7 +1444,7 @@ pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Values<'a, K, V> { fn clone(&self) -> Values<'a, K, V> { @@ -2002,6 +2014,41 @@ impl<'a, K, V> Entry<'a, K, V> { Vacant(ref entry) => entry.key(), } } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(entry_and_modify)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[unstable(feature = "entry_and_modify", issue = "44733")] + pub fn and_modify(self, mut f: F) -> Self + where F: FnMut(&mut V) + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + }, + Vacant(entry) => Vacant(entry), + } + } + } impl<'a, K, V: Default> Entry<'a, K, V> { @@ -2192,28 +2239,29 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { self.key.take() } - /// Replaces the entry, returning the old key and value. + /// Replaces the entry, returning the old key and value. The new key in the hash map will be + /// the key used to create this entry. /// /// # Examples /// /// ``` /// #![feature(map_entry_replace)] - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// map.insert(Rc::new("Stringthing".to_string()), 15); /// - /// let mut map: HashMap = HashMap::new(); - /// map.insert("poneyland".to_string(), 15); + /// let my_key = Rc::new("Stringthing".to_string()); /// - /// if let Entry::Occupied(entry) = map.entry("poneyland".to_string()) { - /// let (old_key, old_value): (String, u32) = entry.replace(16); - /// assert_eq!(old_key, "poneyland"); - /// assert_eq!(old_value, 15); + /// if let Entry::Occupied(entry) = map.entry(my_key) { + /// // Also replace the key with a handle to our other key. + /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); /// } /// - /// assert_eq!(map.get("poneyland"), Some(&16)); /// ``` #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace(mut self, value: V) -> (K, V) { + pub fn replace_entry(mut self, value: V) -> (K, V) { let (old_key, old_value) = self.elem.read_mut(); let old_key = mem::replace(old_key, self.key.unwrap()); @@ -2221,6 +2269,37 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { (old_key, old_value) } + + /// Replaces the key in the hash map with the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_replace)] + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// let mut known_strings: Vec> = Vec::new(); + /// + /// // Initialise known strings, run program, etc. + /// + /// reclaim_memory(&mut map, &known_strings); + /// + /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { + /// for s in known_strings { + /// if let Entry::Occupied(entry) = map.entry(s.clone()) { + /// // Replaces the entry's key with our version of it in `known_strings`. + /// entry.replace_key(); + /// } + /// } + /// } + /// ``` + #[unstable(feature = "map_entry_replace", issue = "44286")] + pub fn replace_key(mut self) -> K { + let (old_key, _) = self.elem.read_mut(); + mem::replace(old_key, self.key.unwrap()) + } } impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { @@ -2414,9 +2493,7 @@ impl RandomState { // increment one of the seeds on every RandomState creation, giving // every corresponding HashMap a different iteration order. thread_local!(static KEYS: Cell<(u64, u64)> = { - let r = rand::OsRng::new(); - let mut r = r.expect("failed to create an OS RNG"); - Cell::new((r.gen(), r.gen())) + Cell::new(sys::hashmap_random_keys()) }); KEYS.with(|keys| { @@ -2508,6 +2585,7 @@ impl super::Recover for HashMap { type Key = K; + #[inline] fn get(&self, key: &Q) -> Option<&K> { self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0) } @@ -2520,6 +2598,7 @@ impl super::Recover for HashMap self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0) } + #[inline] fn replace(&mut self, key: K) -> Option { self.reserve(1); diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index f1e8ff66af178..7e623a0af17c3 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -717,26 +717,25 @@ fn calculate_offsets(hashes_size: usize, (pairs_offset, end_of_pairs, oflo) } -// Returns a tuple of (minimum required malloc alignment, hash_offset, +// Returns a tuple of (minimum required malloc alignment, // array_size), from the start of a mallocated array. fn calculate_allocation(hash_size: usize, hash_align: usize, pairs_size: usize, pairs_align: usize) - -> (usize, usize, usize, bool) { - let hash_offset = 0; + -> (usize, usize, bool) { let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align); let align = cmp::max(hash_align, pairs_align); - (align, hash_offset, end_of_pairs, oflo) + (align, end_of_pairs, oflo) } #[test] fn test_offset_calculation() { - assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 0, 144, false)); - assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 0, 5, false)); - assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 0, 20, false)); + assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 144, false)); + assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 5, false)); + assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 20, false)); assert_eq!(calculate_offsets(128, 15, 4), (128, 143, false)); assert_eq!(calculate_offsets(3, 2, 4), (4, 6, false)); assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false)); @@ -768,10 +767,10 @@ impl RawTable { // This is great in theory, but in practice getting the alignment // right is a little subtle. Therefore, calculating offsets has been // factored out into a different function. - let (alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size, - align_of::(), - pairs_size, - align_of::<(K, V)>()); + let (alignment, size, oflo) = calculate_allocation(hashes_size, + align_of::(), + pairs_size, + align_of::<(K, V)>()); assert!(!oflo, "capacity overflow"); // One check for overflow that covers calculation and rounding of size. @@ -784,7 +783,7 @@ impl RawTable { let buffer = Heap.alloc(Layout::from_size_align(size, alignment).unwrap()) .unwrap_or_else(|e| Heap.oom(e)); - let hashes = buffer.offset(hash_offset as isize) as *mut HashUint; + let hashes = buffer as *mut HashUint; RawTable { capacity_mask: capacity.wrapping_sub(1), @@ -925,7 +924,7 @@ struct RawBuckets<'a, K, V> { marker: marker::PhantomData<&'a ()>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` impl<'a, K, V> Clone for RawBuckets<'a, K, V> { fn clone(&self) -> RawBuckets<'a, K, V> { RawBuckets { @@ -976,7 +975,7 @@ pub struct Iter<'a, K: 'a, V: 'a> { unsafe impl<'a, K: Sync, V: Sync> Sync for Iter<'a, K, V> {} unsafe impl<'a, K: Sync, V: Sync> Send for Iter<'a, K, V> {} -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { Iter { @@ -1157,6 +1156,7 @@ impl Clone for RawTable { } new_ht.size = self.size(); + new_ht.set_tag(self.tag()); new_ht } @@ -1183,10 +1183,10 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { let hashes_size = self.capacity() * size_of::(); let pairs_size = self.capacity() * size_of::<(K, V)>(); - let (align, _, size, oflo) = calculate_allocation(hashes_size, - align_of::(), - pairs_size, - align_of::<(K, V)>()); + let (align, size, oflo) = calculate_allocation(hashes_size, + align_of::(), + pairs_size, + align_of::<(K, V)>()); debug_assert!(!oflo, "should be impossible"); diff --git a/src/libstd/env.rs b/src/libstd/env.rs index f81adad3ebebf..457c6e1409d3c 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -671,6 +671,10 @@ pub struct ArgsOs { inner: sys::args::Args } /// set to arbitrary text, and may not even exist. This means this property should /// not be relied upon for security purposes. /// +/// On Unix systems shell usually expands unquoted arguments with glob patterns +/// (such as `*` and `?`). On Windows this is not done, and such arguments are +/// passed as-is. +/// /// # Panics /// /// The returned iterator will panic during iteration if any argument to the diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 6d64ea0d50332..231b0be927612 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -56,6 +56,8 @@ use any::TypeId; use borrow::Cow; use cell; use char; +use convert; +use core::array; use fmt::{self, Debug, Display}; use mem::transmute; use num; @@ -281,6 +283,13 @@ impl Error for num::TryFromIntError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl Error for array::TryFromSliceError { + fn description(&self) -> &str { + self.__description() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Error for num::ParseFloatError { fn description(&self) -> &str { @@ -362,6 +371,13 @@ impl Error for char::ParseCharError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl Error for convert::Infallible { + fn description(&self) -> &str { + match *self { + } + } +} // copied from any.rs impl Error + 'static { diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 0135cd0a588cf..e5b1394f0709a 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -9,8 +9,9 @@ // except according to those terms. //! This module provides constants which are specific to the implementation -//! of the `f32` floating point data type. Mathematically significant -//! numbers are provided in the `consts` sub-module. +//! of the `f32` floating point data type. +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. //! //! *[See also the `f32` primitive type](../primitive.f32.html).* @@ -23,7 +24,8 @@ use core::num; use intrinsics; #[cfg(not(test))] use num::FpCategory; - +#[cfg(not(test))] +use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f32::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON}; @@ -36,92 +38,6 @@ pub use core::f32::{MIN, MIN_POSITIVE, MAX}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f32::consts; -#[allow(dead_code)] -mod cmath { - use libc::{c_float, c_int}; - - extern { - pub fn cbrtf(n: c_float) -> c_float; - pub fn erff(n: c_float) -> c_float; - pub fn erfcf(n: c_float) -> c_float; - pub fn expm1f(n: c_float) -> c_float; - pub fn fdimf(a: c_float, b: c_float) -> c_float; - pub fn fmodf(a: c_float, b: c_float) -> c_float; - pub fn ilogbf(n: c_float) -> c_int; - pub fn logbf(n: c_float) -> c_float; - pub fn log1pf(n: c_float) -> c_float; - pub fn modff(n: c_float, iptr: &mut c_float) -> c_float; - pub fn nextafterf(x: c_float, y: c_float) -> c_float; - pub fn tgammaf(n: c_float) -> c_float; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] - pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] - pub fn hypotf(x: c_float, y: c_float) -> c_float; - } - - // See the comments in the `floor` function for why MSVC is special - // here. - #[cfg(not(target_env = "msvc"))] - extern { - pub fn acosf(n: c_float) -> c_float; - pub fn asinf(n: c_float) -> c_float; - pub fn atan2f(a: c_float, b: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn coshf(n: c_float) -> c_float; - pub fn sinhf(n: c_float) -> c_float; - pub fn tanf(n: c_float) -> c_float; - pub fn tanhf(n: c_float) -> c_float; - } - - #[cfg(target_env = "msvc")] - pub use self::shims::*; - #[cfg(target_env = "msvc")] - mod shims { - use libc::c_float; - - #[inline] - pub unsafe fn acosf(n: c_float) -> c_float { - f64::acos(n as f64) as c_float - } - - #[inline] - pub unsafe fn asinf(n: c_float) -> c_float { - f64::asin(n as f64) as c_float - } - - #[inline] - pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { - f64::atan2(n as f64, b as f64) as c_float - } - - #[inline] - pub unsafe fn atanf(n: c_float) -> c_float { - f64::atan(n as f64) as c_float - } - - #[inline] - pub unsafe fn coshf(n: c_float) -> c_float { - f64::cosh(n as f64) as c_float - } - - #[inline] - pub unsafe fn sinhf(n: c_float) -> c_float { - f64::sinh(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanf(n: c_float) -> c_float { - f64::tan(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanhf(n: c_float) -> c_float { - f64::tanh(n as f64) as c_float - } - } -} - #[cfg(not(test))] #[lang = "f32"] impl f32 { @@ -1082,10 +998,13 @@ impl f32 { /// Raw transmutation to `u32`. /// - /// Converts the `f32` into its raw memory representation, - /// similar to the `transmute` function. + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). /// - /// Note that this function is distinct from casting. + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. /// /// # Examples /// @@ -1102,17 +1021,33 @@ impl f32 { /// Raw transmutation from `u32`. /// - /// Converts the given `u32` containing the float's raw memory - /// representation into the `f32` type, similar to the - /// `transmute` function. + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianess on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. /// - /// There is only one difference to a bare `transmute`: - /// Due to the implications onto Rust's safety promises being - /// uncertain, if the representation of a signaling NaN "sNaN" float - /// is passed to the function, the implementation is allowed to - /// return a quiet NaN instead. + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favours preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. /// - /// Note that this function is distinct from casting. + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. /// /// # Examples /// @@ -1121,25 +1056,11 @@ impl f32 { /// let v = f32::from_bits(0x41480000); /// let difference = (v - 12.5).abs(); /// assert!(difference <= 1e-5); - /// // Example for a signaling NaN value: - /// let snan = 0x7F800001; - /// assert_ne!(f32::from_bits(snan).to_bits(), snan); /// ``` #[stable(feature = "float_bits_conv", since = "1.20.0")] #[inline] - pub fn from_bits(mut v: u32) -> Self { - const EXP_MASK: u32 = 0x7F800000; - const FRACT_MASK: u32 = 0x007FFFFF; - if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { - // While IEEE 754-2008 specifies encodings for quiet NaNs - // and signaling ones, certain MIPS and PA-RISC - // CPUs treat signaling NaNs differently. - // Therefore to be safe, we pass a known quiet NaN - // if v is any kind of NaN. - // The check above only assumes IEEE 754-1985 to be - // valid. - v = unsafe { ::mem::transmute(NAN) }; - } + pub fn from_bits(v: u32) -> Self { + // It turns out the safety issues with sNaN were overblown! Hooray! unsafe { ::mem::transmute(v) } } } @@ -1730,25 +1651,15 @@ mod tests { assert_approx_eq!(f32::from_bits(0x41480000), 12.5); assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); - } - #[test] - fn test_snan_masking() { - // NOTE: this test assumes that our current platform - // implements IEEE 754-2008 that specifies the difference - // in encoding of quiet and signaling NaNs. - // If you are porting Rust to a platform that does not - // implement IEEE 754-2008 (but e.g. IEEE 754-1985, which - // only says that "Signaling NaNs shall be reserved operands" - // but doesn't specify the actual setup), feel free to - // cfg out this test. - let snan: u32 = 0x7F801337; - const QNAN_MASK: u32 = 0x00400000; - let nan_masked_fl = f32::from_bits(snan); - let nan_masked = nan_masked_fl.to_bits(); - // Ensure that signaling NaNs don't stay the same - assert_ne!(nan_masked, snan); - // Ensure that we have a quiet NaN - assert_ne!(nan_masked & QNAN_MASK, 0); - assert!(nan_masked_fl.is_nan()); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA; + let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + assert!(f32::from_bits(masked_nan1).is_nan()); + assert!(f32::from_bits(masked_nan2).is_nan()); + + assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index d73d7cd2c7bd1..f4d804fd50823 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -9,8 +9,9 @@ // except according to those terms. //! This module provides constants which are specific to the implementation -//! of the `f64` floating point data type. Mathematically significant -//! numbers are provided in the `consts` sub-module. +//! of the `f64` floating point data type. +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. //! //! *[See also the `f64` primitive type](../primitive.f64.html).* @@ -23,6 +24,8 @@ use core::num; use intrinsics; #[cfg(not(test))] use num::FpCategory; +#[cfg(not(test))] +use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f64::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON}; @@ -35,53 +38,6 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f64::consts; -#[allow(dead_code)] -mod cmath { - use libc::{c_double, c_int}; - - #[link_name = "m"] - extern { - pub fn acos(n: c_double) -> c_double; - pub fn asin(n: c_double) -> c_double; - pub fn atan(n: c_double) -> c_double; - pub fn atan2(a: c_double, b: c_double) -> c_double; - pub fn cbrt(n: c_double) -> c_double; - pub fn cosh(n: c_double) -> c_double; - pub fn erf(n: c_double) -> c_double; - pub fn erfc(n: c_double) -> c_double; - pub fn expm1(n: c_double) -> c_double; - pub fn fdim(a: c_double, b: c_double) -> c_double; - pub fn fmod(a: c_double, b: c_double) -> c_double; - pub fn frexp(n: c_double, value: &mut c_int) -> c_double; - pub fn ilogb(n: c_double) -> c_int; - pub fn ldexp(x: c_double, n: c_int) -> c_double; - pub fn logb(n: c_double) -> c_double; - pub fn log1p(n: c_double) -> c_double; - pub fn nextafter(x: c_double, y: c_double) -> c_double; - pub fn modf(n: c_double, iptr: &mut c_double) -> c_double; - pub fn sinh(n: c_double) -> c_double; - pub fn tan(n: c_double) -> c_double; - pub fn tanh(n: c_double) -> c_double; - pub fn tgamma(n: c_double) -> c_double; - - // These are commonly only available for doubles - - pub fn j0(n: c_double) -> c_double; - pub fn j1(n: c_double) -> c_double; - pub fn jn(i: c_int, n: c_double) -> c_double; - - pub fn y0(n: c_double) -> c_double; - pub fn y1(n: c_double) -> c_double; - pub fn yn(i: c_int, n: c_double) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")] - pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] - pub fn hypot(x: c_double, y: c_double) -> c_double; - } -} - #[cfg(not(test))] #[lang = "f64"] impl f64 { @@ -997,10 +953,13 @@ impl f64 { /// Raw transmutation to `u64`. /// - /// Converts the `f64` into its raw memory representation, - /// similar to the `transmute` function. + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). /// - /// Note that this function is distinct from casting. + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. /// /// # Examples /// @@ -1017,17 +976,33 @@ impl f64 { /// Raw transmutation from `u64`. /// - /// Converts the given `u64` containing the float's raw memory - /// representation into the `f64` type, similar to the - /// `transmute` function. + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianess on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. /// - /// There is only one difference to a bare `transmute`: - /// Due to the implications onto Rust's safety promises being - /// uncertain, if the representation of a signaling NaN "sNaN" float - /// is passed to the function, the implementation is allowed to - /// return a quiet NaN instead. + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. /// - /// Note that this function is distinct from casting. + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favours preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. /// /// # Examples /// @@ -1036,25 +1011,11 @@ impl f64 { /// let v = f64::from_bits(0x4029000000000000); /// let difference = (v - 12.5).abs(); /// assert!(difference <= 1e-5); - /// // Example for a signaling NaN value: - /// let snan = 0x7FF0000000000001; - /// assert_ne!(f64::from_bits(snan).to_bits(), snan); /// ``` #[stable(feature = "float_bits_conv", since = "1.20.0")] #[inline] - pub fn from_bits(mut v: u64) -> Self { - const EXP_MASK: u64 = 0x7FF0000000000000; - const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; - if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { - // While IEEE 754-2008 specifies encodings for quiet NaNs - // and signaling ones, certain MIPS and PA-RISC - // CPUs treat signaling NaNs differently. - // Therefore to be safe, we pass a known quiet NaN - // if v is any kind of NaN. - // The check above only assumes IEEE 754-1985 to be - // valid. - v = unsafe { ::mem::transmute(NAN) }; - } + pub fn from_bits(v: u64) -> Self { + // It turns out the safety issues with sNaN were overblown! Hooray! unsafe { ::mem::transmute(v) } } } @@ -1641,5 +1602,15 @@ mod tests { assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; + let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + assert!(f64::from_bits(masked_nan1).is_nan()); + assert!(f64::from_bits(masked_nan2).is_nan()); + + assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); } } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 7992aefcb4203..a2022a2eeb23c 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -14,28 +14,80 @@ use cmp::Ordering; use error::Error; use fmt::{self, Write}; use io; -use libc; use mem; use memchr; use ops; use os::raw::c_char; use ptr; +use rc::Rc; use slice; use str::{self, Utf8Error}; +use sync::Arc; +use sys; -/// A type representing an owned C-compatible string. +/// A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the +/// middle. /// -/// This type serves the primary purpose of being able to safely generate a +/// This type serves the purpose of being able to safely generate a /// C-compatible string from a Rust byte slice or vector. An instance of this /// type is a static guarantee that the underlying bytes contain no interior 0 -/// bytes and the final byte is 0. +/// bytes ("nul characters") and that the final byte is 0 ("nul terminator"). /// -/// A `CString` is created from either a byte slice or a byte vector. A [`u8`] -/// slice can be obtained with the `as_bytes` method. Slices produced from a -/// `CString` do *not* contain the trailing nul terminator unless otherwise -/// specified. +/// `CString` is to [`CStr`] as [`String`] is to [`&str`]: the former +/// in each pair are owned strings; the latter are borrowed +/// references. /// +/// # Creating a `CString` +/// +/// A `CString` is created from either a byte slice or a byte vector, +/// or anything that implements [`Into`]`<`[`Vec`]`<`[`u8`]`>>` (for +/// example, you can build a `CString` straight out of a [`String`] or +/// a [`&str`], since both implement that trait). +/// +/// The [`new`] method will actually check that the provided `&[u8]` +/// does not have 0 bytes in the middle, and return an error if it +/// finds one. +/// +/// # Extracting a raw pointer to the whole C string +/// +/// `CString` implements a [`as_ptr`] method through the [`Deref`] +/// trait. This method will give you a `*const c_char` which you can +/// feed directly to extern functions that expect a nul-terminated +/// string, like C's `strdup()`. +/// +/// # Extracting a slice of the whole C string +/// +/// Alternatively, you can obtain a `&[`[`u8`]`]` slice from a +/// `CString` with the [`as_bytes`] method. Slices produced in this +/// way do *not* contain the trailing nul terminator. This is useful +/// when you will be calling an extern function that takes a `*const +/// u8` argument which is not necessarily nul-terminated, plus another +/// argument with the length of the string — like C's `strndup()`. +/// You can of course get the slice's length with its +/// [`len`][slice.len] method. +/// +/// If you need a `&[`[`u8`]`]` slice *with* the nul terminator, you +/// can use [`as_bytes_with_nul`] instead. +/// +/// Once you have the kind of slice you need (with or without a nul +/// terminator), you can call the slice's own +/// [`as_ptr`][slice.as_ptr] method to get a raw pointer to pass to +/// extern functions. See the documentation for that function for a +/// discussion on ensuring the lifetime of the raw pointer. +/// +/// [`Into`]: ../convert/trait.Into.html +/// [`Vec`]: ../vec/struct.Vec.html +/// [`String`]: ../string/struct.String.html +/// [`&str`]: ../primitive.str.html /// [`u8`]: ../primitive.u8.html +/// [`new`]: #method.new +/// [`as_bytes`]: #method.as_bytes +/// [`as_bytes_with_nul`]: #method.as_bytes_with_nul +/// [`as_ptr`]: #method.as_ptr +/// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr +/// [slice.len]: ../primitive.slice.html#method.len +/// [`Deref`]: ../ops/trait.Deref.html +/// [`CStr`]: struct.CStr.html /// /// # Examples /// @@ -48,6 +100,8 @@ use str::{self, Utf8Error}; /// fn my_printer(s: *const c_char); /// } /// +/// // We are certain that our string doesn't have 0 bytes in the middle, +/// // so we can .unwrap() /// let c_to_print = CString::new("Hello, world!").unwrap(); /// unsafe { /// my_printer(c_to_print.as_ptr()); @@ -58,7 +112,7 @@ use str::{self, Utf8Error}; /// # Safety /// /// `CString` is intended for working with traditional C-style strings -/// (a sequence of non-null bytes terminated by a single null byte); the +/// (a sequence of non-nul bytes terminated by a single nul byte); the /// primary use case for these kinds of strings is interoperating with C-like /// code. Often you will need to transfer ownership to/from that external /// code. It is strongly recommended that you thoroughly read through the @@ -77,17 +131,21 @@ pub struct CString { /// Representation of a borrowed C string. /// -/// This dynamically sized type is only safely constructed via a borrowed -/// version of an instance of `CString`. This type can be constructed from a raw -/// C string as well and represents a C string borrowed from another location. +/// This type represents a borrowed reference to a nul-terminated +/// array of bytes. It can be constructed safely from a `&[`[`u8`]`]` +/// slice, or unsafely from a raw `*const c_char`. It can then be +/// converted to a Rust [`&str`] by performing UTF-8 validation, or +/// into an owned [`CString`]. +/// +/// `CStr` is to [`CString`] as [`&str`] is to [`String`]: the former +/// in each pair are borrowed references; the latter are owned +/// strings. /// /// Note that this structure is **not** `repr(C)` and is not recommended to be -/// placed in the signatures of FFI functions. Instead safe wrappers of FFI +/// placed in the signatures of FFI functions. Instead, safe wrappers of FFI /// functions may leverage the unsafe [`from_ptr`] constructor to provide a safe /// interface to other consumers. /// -/// [`from_ptr`]: #method.from_ptr -/// /// # Examples /// /// Inspecting a foreign C string: @@ -100,7 +158,7 @@ pub struct CString { /// /// unsafe { /// let slice = CStr::from_ptr(my_string()); -/// println!("string length: {}", slice.to_bytes().len()); +/// println!("string buffer size without nul terminator: {}", slice.to_bytes().len()); /// } /// ``` /// @@ -122,8 +180,6 @@ pub struct CString { /// /// Converting a foreign C string into a Rust [`String`]: /// -/// [`String`]: ../string/struct.String.html -/// /// ```no_run /// use std::ffi::CStr; /// use std::os::raw::c_char; @@ -138,6 +194,12 @@ pub struct CString { /// /// println!("string: {}", my_string_safe()); /// ``` +/// +/// [`u8`]: ../primitive.u8.html +/// [`&str`]: ../primitive.str.html +/// [`String`]: ../string/struct.String.html +/// [`CString`]: struct.CString.html +/// [`from_ptr`]: #method.from_ptr #[derive(Hash)] #[stable(feature = "rust1", since = "1.0.0")] pub struct CStr { @@ -148,9 +210,15 @@ pub struct CStr { inner: [c_char] } -/// An error returned from [`CString::new`] to indicate that a nul byte was found -/// in the vector provided. +/// An error indicating that an interior nul byte was found. +/// +/// While Rust strings may contain nul bytes in the middle, C strings +/// can't, as that byte would effectively truncate the string. /// +/// This error is created by the [`new`][`CString::new`] method on +/// [`CString`]. See its documentation for more. +/// +/// [`CString`]: struct.CString.html /// [`CString::new`]: struct.CString.html#method.new /// /// # Examples @@ -164,9 +232,16 @@ pub struct CStr { #[stable(feature = "rust1", since = "1.0.0")] pub struct NulError(usize, Vec); -/// An error returned from [`CStr::from_bytes_with_nul`] to indicate that a nul -/// byte was found too early in the slice provided or one wasn't found at all. +/// An error indicating that a nul byte was not in the expected position. +/// +/// The slice used to create a [`CStr`] must have one and only one nul +/// byte at the end of the slice. /// +/// This error is created by the +/// [`from_bytes_with_nul`][`CStr::from_bytes_with_nul`] method on +/// [`CStr`]. See its documentation for more. +/// +/// [`CStr`]: struct.CStr.html /// [`CStr::from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul /// /// # Examples @@ -201,9 +276,18 @@ impl FromBytesWithNulError { } } -/// An error returned from [`CString::into_string`] to indicate that a UTF-8 error -/// was encountered during the conversion. +/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`]. +/// +/// `CString` is just a wrapper over a buffer of bytes with a nul +/// terminator; [`into_string`][`CString::into_string`] performs UTF-8 +/// validation on those bytes and may return this error. /// +/// This `struct` is created by the +/// [`into_string`][`CString::into_string`] method on [`CString`]. See +/// its documentation for more. +/// +/// [`String`]: ../string/struct.String.html +/// [`CString`]: struct.CString.html /// [`CString::into_string`]: struct.CString.html#method.into_string #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstring_into", since = "1.7.0")] @@ -215,8 +299,11 @@ pub struct IntoStringError { impl CString { /// Creates a new C-compatible string from a container of bytes. /// - /// This method will consume the provided data and use the underlying bytes - /// to construct a new string, ensuring that there is a trailing 0 byte. + /// This function will consume the provided data and use the + /// underlying bytes to construct a new string, ensuring that + /// there is a trailing 0 byte. This trailing 0 byte will be + /// appended by this function; the provided data should *not* + /// contain any 0 bytes in it. /// /// # Examples /// @@ -234,9 +321,11 @@ impl CString { /// /// # Errors /// - /// This function will return an error if the bytes yielded contain an - /// internal 0 byte. The error returned will contain the bytes as well as + /// This function will return an error if the supplied bytes contain an + /// internal 0 byte. The [`NulError`] returned will contain the bytes as well as /// the position of the nul byte. + /// + /// [`NulError`]: struct.NulError.html #[stable(feature = "rust1", since = "1.0.0")] pub fn new>>(t: T) -> Result { Self::_new(t.into()) @@ -249,8 +338,8 @@ impl CString { } } - /// Creates a C-compatible string from a byte vector without checking for - /// interior 0 bytes. + /// Creates a C-compatible string by consuming a byte vector, + /// without checking for interior 0 bytes. /// /// This method is equivalent to [`new`] except that no runtime assertion /// is made that `v` contains no 0 bytes, and it requires an actual @@ -275,7 +364,7 @@ impl CString { CString { inner: v.into_boxed_slice() } } - /// Retakes ownership of a `CString` that was transferred to C. + /// Retakes ownership of a `CString` that was transferred to C via [`into_raw`]. /// /// Additionally, the length of the string will be recalculated from the pointer. /// @@ -286,7 +375,14 @@ impl CString { /// ownership of a string that was allocated by foreign code) is likely to lead /// to undefined behavior or allocator corruption. /// + /// > **Note:** If you need to borrow a string that was allocated by + /// > foreign code, use [`CStr`]. If you need to take ownership of + /// > a string that was allocated by foreign code, you will need to + /// > make your own provisions for freeing it appropriately, likely + /// > with the foreign code's API to do that. + /// /// [`into_raw`]: #method.into_raw + /// [`CStr`]: struct.CStr.html /// /// # Examples /// @@ -310,16 +406,16 @@ impl CString { /// ``` #[stable(feature = "cstr_memory", since = "1.4.0")] pub unsafe fn from_raw(ptr: *mut c_char) -> CString { - let len = libc::strlen(ptr) + 1; // Including the NUL byte - let slice = slice::from_raw_parts(ptr, len as usize); - CString { inner: mem::transmute(slice) } + let len = sys::strlen(ptr) + 1; // Including the NUL byte + let slice = slice::from_raw_parts_mut(ptr, len as usize); + CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) } } - /// Transfers ownership of the string to a C caller. + /// Consumes the `CString` and transfers ownership of the string to a C caller. /// - /// The pointer must be returned to Rust and reconstituted using + /// The pointer which this function returns must be returned to Rust and reconstituted using /// [`from_raw`] to be properly deallocated. Specifically, one - /// should *not* use the standard C `free` function to deallocate + /// should *not* use the standard C `free()` function to deallocate /// this string. /// /// Failure to call [`from_raw`] will lead to a memory leak. @@ -351,11 +447,27 @@ impl CString { Box::into_raw(self.into_inner()) as *mut c_char } - /// Converts the `CString` into a [`String`] if it contains valid Unicode data. + /// Converts the `CString` into a [`String`] if it contains valid UTF-8 data. /// /// On failure, ownership of the original `CString` is returned. /// /// [`String`]: ../string/struct.String.html + /// + /// # Examples + /// + /// ``` + /// use std::ffi::CString; + /// + /// let valid_utf8 = vec![b'f', b'o', b'o']; + /// let cstring = CString::new(valid_utf8).unwrap(); + /// assert_eq!(cstring.into_string().unwrap(), "foo"); + /// + /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; + /// let cstring = CString::new(invalid_utf8).unwrap(); + /// let err = cstring.into_string().err().unwrap(); + /// assert_eq!(err.utf8_error().valid_up_to(), 1); + /// ``` + #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_string(self) -> Result { String::from_utf8(self.into_bytes()) @@ -365,10 +477,11 @@ impl CString { }) } - /// Returns the underlying byte buffer. + /// Consumes the `CString` and returns the underlying byte buffer. /// - /// The returned buffer does **not** contain the trailing nul separator and - /// it is guaranteed to not have any interior nul bytes. + /// The returned buffer does **not** contain the trailing nul + /// terminator, and it is guaranteed to not have any interior nul + /// bytes. /// /// # Examples /// @@ -388,7 +501,7 @@ impl CString { } /// Equivalent to the [`into_bytes`] function except that the returned vector - /// includes the trailing nul byte. + /// includes the trailing nul terminator. /// /// [`into_bytes`]: #method.into_bytes /// @@ -408,8 +521,12 @@ impl CString { /// Returns the contents of this `CString` as a slice of bytes. /// - /// The returned slice does **not** contain the trailing nul separator and - /// it is guaranteed to not have any interior nul bytes. + /// The returned slice does **not** contain the trailing nul + /// terminator, and it is guaranteed to not have any interior nul + /// bytes. If you need the nul terminator, use + /// [`as_bytes_with_nul`] instead. + /// + /// [`as_bytes_with_nul`]: #method.as_bytes_with_nul /// /// # Examples /// @@ -427,7 +544,7 @@ impl CString { } /// Equivalent to the [`as_bytes`] function except that the returned slice - /// includes the trailing nul byte. + /// includes the trailing nul terminator. /// /// [`as_bytes`]: #method.as_bytes /// @@ -480,7 +597,7 @@ impl CString { /// ``` #[stable(feature = "into_boxed_c_str", since = "1.20.0")] pub fn into_boxed_c_str(self) -> Box { - unsafe { mem::transmute(self.into_inner()) } + unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) } } // Bypass "move out of struct which implements [`Drop`] trait" restriction. @@ -569,7 +686,7 @@ impl Borrow for CString { impl<'a> From<&'a CStr> for Box { fn from(s: &'a CStr) -> Box { let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul()); - unsafe { mem::transmute(boxed) } + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } } } @@ -589,17 +706,53 @@ impl From for Box { } } +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Arc { + #[inline] + fn from(s: CString) -> Arc { + let arc: Arc<[u8]> = Arc::from(s.into_inner()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a CStr> for Arc { + #[inline] + fn from(s: &CStr) -> Arc { + let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Rc { + #[inline] + fn from(s: CString) -> Rc { + let rc: Rc<[u8]> = Rc::from(s.into_inner()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a CStr> for Rc { + #[inline] + fn from(s: &CStr) -> Rc { + let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } + } +} + #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { let boxed: Box<[u8]> = Box::from([0]); - unsafe { mem::transmute(boxed) } + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } } } impl NulError { - /// Returns the position of the nul byte in the slice that was provided to - /// [`CString::new`]. + /// Returns the position of the nul byte in the slice that caused + /// [`CString::new`] to fail. /// /// [`CString::new`]: struct.CString.html#method.new /// @@ -711,9 +864,9 @@ impl fmt::Display for IntoStringError { } impl CStr { - /// Casts a raw C string to a safe C string wrapper. + /// Wraps a raw C string with a safe C string wrapper. /// - /// This function will cast the provided `ptr` to the `CStr` wrapper which + /// This function will wrap the provided `ptr` with a `CStr` wrapper, which /// allows inspection and interoperation of non-owned C strings. This method /// is unsafe for a number of reasons: /// @@ -746,16 +899,16 @@ impl CStr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { - let len = libc::strlen(ptr); + let len = sys::strlen(ptr); let ptr = ptr as *const u8; CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) } /// Creates a C string wrapper from a byte slice. /// - /// This function will cast the provided `bytes` to a `CStr` wrapper after - /// ensuring that it is null terminated and does not contain any interior - /// nul bytes. + /// This function will cast the provided `bytes` to a `CStr` + /// wrapper after ensuring that the byte slice is nul-terminated + /// and does not contain any interior nul bytes. /// /// # Examples /// @@ -766,7 +919,7 @@ impl CStr { /// assert!(cstr.is_ok()); /// ``` /// - /// Creating a `CStr` without a trailing nul byte is an error: + /// Creating a `CStr` without a trailing nul terminator is an error: /// /// ``` /// use std::ffi::CStr; @@ -800,7 +953,7 @@ impl CStr { /// Unsafely creates a C string wrapper from a byte slice. /// /// This function will cast the provided `bytes` to a `CStr` wrapper without - /// performing any sanity checks. The provided slice must be null terminated + /// performing any sanity checks. The provided slice **must** be nul-terminated /// and not contain any interior nul bytes. /// /// # Examples @@ -817,12 +970,12 @@ impl CStr { #[inline] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - mem::transmute(bytes) + &*(bytes as *const [u8] as *const CStr) } /// Returns the inner pointer to this C string. /// - /// The returned pointer will be valid for as long as `self` is and points + /// The returned pointer will be valid for as long as `self` is, and points /// to a contiguous region of memory terminated with a 0 byte to represent /// the end of the string. /// @@ -843,9 +996,9 @@ impl CStr { /// ``` /// /// This happens because the pointer returned by `as_ptr` does not carry any - /// lifetime information and the string is deallocated immediately after + /// lifetime information and the [`CString`] is deallocated immediately after /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated. - /// To fix the problem, bind the string to a local variable: + /// To fix the problem, bind the `CString` to a local variable: /// /// ```no_run /// use std::ffi::{CString}; @@ -857,6 +1010,11 @@ impl CStr { /// *ptr; /// } /// ``` + /// + /// This way, the lifetime of the `CString` in `hello` encompasses + /// the lifetime of `ptr` and the `unsafe` block. + /// + /// [`CString`]: struct.CString.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn as_ptr(&self) -> *const c_char { @@ -865,11 +1023,7 @@ impl CStr { /// Converts this C string to a byte slice. /// - /// This function will calculate the length of this string (which normally - /// requires a linear amount of work to be done) and then return the - /// resulting slice of `u8` elements. - /// - /// The returned slice will **not** contain the trailing nul that this C + /// The returned slice will **not** contain the trailing nul terminator that this C /// string has. /// /// > **Note**: This method is currently implemented as a 0-cost cast, but @@ -894,7 +1048,7 @@ impl CStr { /// Converts this C string to a byte slice containing the trailing 0 byte. /// /// This function is the equivalent of [`to_bytes`] except that it will retain - /// the trailing nul instead of chopping it off. + /// the trailing nul terminator instead of chopping it off. /// /// > **Note**: This method is currently implemented as a 0-cost cast, but /// > it is planned to alter its definition in the future to perform the @@ -913,13 +1067,14 @@ impl CStr { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn to_bytes_with_nul(&self) -> &[u8] { - unsafe { mem::transmute(&self.inner) } + unsafe { &*(&self.inner as *const [c_char] as *const [u8]) } } /// Yields a [`&str`] slice if the `CStr` contains valid UTF-8. /// - /// This function will calculate the length of this string and check for - /// UTF-8 validity, and then return the [`&str`] if it's valid. + /// If the contents of the `CStr` are valid UTF-8 data, this + /// function will return the corresponding [`&str`] slice. Otherwise, + /// it will return an error with details of where UTF-8 validation failed. /// /// > **Note**: This method is currently implemented to check for validity /// > after a 0-cost cast, but it is planned to alter its definition in the @@ -947,10 +1102,12 @@ impl CStr { /// Converts a `CStr` into a [`Cow`]`<`[`str`]`>`. /// - /// This function will calculate the length of this string (which normally - /// requires a linear amount of work to be done) and then return the - /// resulting slice as a [`Cow`]`<`[`str`]`>`, replacing any invalid UTF-8 sequences - /// with `U+FFFD REPLACEMENT CHARACTER`. + /// If the contents of the `CStr` are valid UTF-8 data, this + /// function will return a [`Cow`]`::`[`Borrowed`]`(`[`&str`]`)` + /// with the the corresponding [`&str`] slice. Otherwise, it will + /// replace any invalid UTF-8 sequences with `U+FFFD REPLACEMENT + /// CHARACTER` and return a [`Cow`]`::`[`Owned`]`(`[`String`]`)` + /// with the result. /// /// > **Note**: This method is currently implemented to check for validity /// > after a 0-cost cast, but it is planned to alter its definition in the @@ -958,7 +1115,9 @@ impl CStr { /// > check whenever this method is called. /// /// [`Cow`]: ../borrow/enum.Cow.html + /// [`Borrowed`]: ../borrow/enum.Cow.html#variant.Borrowed /// [`str`]: ../primitive.str.html + /// [`String`]: ../string/struct.String.html /// /// # Examples /// @@ -1005,7 +1164,8 @@ impl CStr { /// ``` #[stable(feature = "into_boxed_c_str", since = "1.20.0")] pub fn into_c_string(self: Box) -> CString { - unsafe { mem::transmute(self) } + let raw = Box::into_raw(self) as *mut [u8]; + CString { inner: unsafe { Box::from_raw(raw) } } } } @@ -1079,6 +1239,8 @@ mod tests { use borrow::Cow::{Borrowed, Owned}; use hash::{Hash, Hasher}; use collections::hash_map::DefaultHasher; + use rc::Rc; + use sync::Arc; #[test] fn c_to_rust() { @@ -1215,4 +1377,21 @@ mod tests { let boxed = >::default(); assert_eq!(boxed.to_bytes_with_nul(), &[0]); } + + #[test] + fn into_rc() { + let orig: &[u8] = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(orig).unwrap(); + let rc: Rc = Rc::from(cstr); + let arc: Arc = Arc::from(cstr); + + assert_eq!(&*rc, cstr); + assert_eq!(&*arc, cstr); + + let rc2: Rc = Rc::from(cstr.to_owned()); + let arc2: Arc = Arc::from(cstr.to_owned()); + + assert_eq!(&*rc2, cstr); + assert_eq!(&*arc2, cstr); + } } diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index ca1ff18f1cad8..a75596351e4cf 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -9,6 +9,157 @@ // except according to those terms. //! Utilities related to FFI bindings. +//! +//! This module provides utilities to handle data across non-Rust +//! interfaces, like other programming languages and the underlying +//! operating system. It is mainly of use for FFI (Foreign Function +//! Interface) bindings and code that needs to exchange C-like strings +//! with other languages. +//! +//! # Overview +//! +//! Rust represents owned strings with the [`String`] type, and +//! borrowed slices of strings with the [`str`] primitive. Both are +//! always in UTF-8 encoding, and may contain nul bytes in the middle, +//! i.e. if you look at the bytes that make up the string, there may +//! be a `\0` among them. Both `String` and `str` store their length +//! explicitly; there are no nul terminators at the end of strings +//! like in C. +//! +//! C strings are different from Rust strings: +//! +//! * **Encodings** - Rust strings are UTF-8, but C strings may use +//! other encodings. If you are using a string from C, you should +//! check its encoding explicitly, rather than just assuming that it +//! is UTF-8 like you can do in Rust. +//! +//! * **Character size** - C strings may use `char` or `wchar_t`-sized +//! characters; please **note** that C's `char` is different from Rust's. +//! The C standard leaves the actual sizes of those types open to +//! interpretation, but defines different APIs for strings made up of +//! each character type. Rust strings are always UTF-8, so different +//! Unicode characters will be encoded in a variable number of bytes +//! each. The Rust type [`char`] represents a '[Unicode scalar +//! value]', which is similar to, but not the same as, a '[Unicode +//! code point]'. +//! +//! * **Nul terminators and implicit string lengths** - Often, C +//! strings are nul-terminated, i.e. they have a `\0` character at the +//! end. The length of a string buffer is not stored, but has to be +//! calculated; to compute the length of a string, C code must +//! manually call a function like `strlen()` for `char`-based strings, +//! or `wcslen()` for `wchar_t`-based ones. Those functions return +//! the number of characters in the string excluding the nul +//! terminator, so the buffer length is really `len+1` characters. +//! Rust strings don't have a nul terminator; their length is always +//! stored and does not need to be calculated. While in Rust +//! accessing a string's length is a O(1) operation (becasue the +//! length is stored); in C it is an O(length) operation because the +//! length needs to be computed by scanning the string for the nul +//! terminator. +//! +//! * **Internal nul characters** - When C strings have a nul +//! terminator character, this usually means that they cannot have nul +//! characters in the middle — a nul character would essentially +//! truncate the string. Rust strings *can* have nul characters in +//! the middle, because nul does not have to mark the end of the +//! string in Rust. +//! +//! # Representations of non-Rust strings +//! +//! [`CString`] and [`CStr`] are useful when you need to transfer +//! UTF-8 strings to and from languages with a C ABI, like Python. +//! +//! * **From Rust to C:** [`CString`] represents an owned, C-friendly +//! string: it is nul-terminated, and has no internal nul characters. +//! Rust code can create a `CString` out of a normal string (provided +//! that the string doesn't have nul characters in the middle), and +//! then use a variety of methods to obtain a raw `*mut u8` that can +//! then be passed as an argument to functions which use the C +//! conventions for strings. +//! +//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it +//! is what you would use to wrap a raw `*const u8` that you got from +//! a C function. A `CStr` is guaranteed to be a nul-terminated array +//! of bytes. Once you have a `CStr`, you can convert it to a Rust +//! `&str` if it's valid UTF-8, or lossily convert it by adding +//! replacement characters. +//! +//! [`OsString`] and [`OsStr`] are useful when you need to transfer +//! strings to and from the operating system itself, or when capturing +//! the output of external commands. Conversions between `OsString`, +//! `OsStr` and Rust strings work similarly to those for [`CString`] +//! and [`CStr`]. +//! +//! * [`OsString`] represents an owned string in whatever +//! representation the operating system prefers. In the Rust standard +//! library, various APIs that transfer strings to/from the operating +//! system use `OsString` instead of plain strings. For example, +//! [`env::var_os()`] is used to query environment variables; it +//! returns an `Option`. If the environment variable exists +//! you will get a `Some(os_string)`, which you can *then* try to +//! convert to a Rust string. This yields a [`Result<>`], so that +//! your code can detect errors in case the environment variable did +//! not in fact contain valid Unicode data. +//! +//! * [`OsStr`] represents a borrowed reference to a string in a +//! format that can be passed to the operating system. It can be +//! converted into an UTF-8 Rust string slice in a similar way to +//! `OsString`. +//! +//! # Conversions +//! +//! ## On Unix +//! +//! On Unix, [`OsStr`] implements the +//! `std::os::unix:ffi::`[`OsStrExt`][unix.OsStrExt] trait, which +//! augments it with two methods, [`from_bytes`] and [`as_bytes`]. +//! These do inexpensive conversions from and to UTF-8 byte slices. +//! +//! Additionally, on Unix [`OsString`] implements the +//! `std::os::unix:ffi::`[`OsStringExt`][unix.OsStringExt] trait, +//! which provides [`from_vec`] and [`into_vec`] methods that consume +//! their arguments, and take or produce vectors of [`u8`]. +//! +//! ## On Windows +//! +//! On Windows, [`OsStr`] implements the +//! `std::os::windows::ffi::`[`OsStrExt`][windows.OsStrExt] trait, +//! which provides an [`encode_wide`] method. This provides an +//! iterator that can be [`collect`]ed into a vector of [`u16`]. +//! +//! Additionally, on Windows [`OsString`] implements the +//! `std::os::windows:ffi::`[`OsStringExt`][windows.OsStringExt] +//! trait, which provides a [`from_wide`] method. The result of this +//! method is an `OsString` which can be round-tripped to a Windows +//! string losslessly. +//! +//! [`String`]: ../string/struct.String.html +//! [`str`]: ../primitive.str.html +//! [`char`]: ../primitive.char.html +//! [`u8`]: ../primitive.u8.html +//! [`u16`]: ../primitive.u16.html +//! [Unicode scalar value]: http://www.unicode.org/glossary/#unicode_scalar_value +//! [Unicode code point]: http://www.unicode.org/glossary/#code_point +//! [`CString`]: struct.CString.html +//! [`CStr`]: struct.CStr.html +//! [`OsString`]: struct.OsString.html +//! [`OsStr`]: struct.OsStr.html +//! [`env::set_var()`]: ../env/fn.set_var.html +//! [`env::var_os()`]: ../env/fn.var_os.html +//! [`Result<>`]: ../result/enum.Result.html +//! [unix.OsStringExt]: ../os/unix/ffi/trait.OsStringExt.html +//! [`from_vec`]: ../os/unix/ffi/trait.OsStringExt.html#tymethod.from_vec +//! [`into_vec`]: ../os/unix/ffi/trait.OsStringExt.html#tymethod.into_vec +//! [unix.OsStrExt]: ../os/unix/ffi/trait.OsStrExt.html +//! [`from_bytes`]: ../os/unix/ffi/trait.OsStrExt.html#tymethod.from_bytes +//! [`as_bytes`]: ../os/unix/ffi/trait.OsStrExt.html#tymethod.as_bytes +//! [`OsStrExt`]: ../os/unix/ffi/trait.OsStrExt.html +//! [windows.OsStrExt]: ../os/windows/ffi/trait.OsStrExt.html +//! [`encode_wide`]: ../os/windows/ffi/trait.OsStrExt.html#tymethod.encode_wide +//! [`collect`]: ../iter/trait.Iterator.html#method.collect +//! [windows.OsStringExt]: ../os/windows/ffi/trait.OsStringExt.html +//! [`from_wide`]: ../os/windows/ffi/trait.OsStringExt.html#tymethod.from_wide #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index a40a9329ed9bf..cb902461f39fd 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -10,10 +10,11 @@ use borrow::{Borrow, Cow}; use fmt; -use mem; use ops; use cmp; use hash::{Hash, Hasher}; +use rc::Rc; +use sync::Arc; use sys::os_str::{Buf, Slice}; use sys_common::{AsInner, IntoInner, FromInner}; @@ -33,18 +34,64 @@ use sys_common::{AsInner, IntoInner, FromInner}; /// /// `OsString` and [`OsStr`] bridge this gap by simultaneously representing Rust /// and platform-native string values, and in particular allowing a Rust string -/// to be converted into an "OS" string with no cost. +/// to be converted into an "OS" string with no cost if possible. +/// +/// `OsString` is to [`OsStr`] as [`String`] is to [`&str`]: the former +/// in each pair are owned strings; the latter are borrowed +/// references. +/// +/// # Creating an `OsString` +/// +/// **From a Rust string**: `OsString` implements +/// [`From`]`<`[`String`]`>`, so you can use `my_string.from` to +/// create an `OsString` from a normal Rust string. +/// +/// **From slices:** Just like you can start with an empty Rust +/// [`String`] and then [`push_str`][String.push_str] `&str` +/// sub-string slices into it, you can create an empty `OsString` with +/// the [`new`] method and then push string slices into it with the +/// [`push`] method. +/// +/// # Extracting a borrowed reference to the whole OS string +/// +/// You can use the [`as_os_str`] method to get an `&`[`OsStr`] from +/// an `OsString`; this is effectively a borrowed reference to the +/// whole string. +/// +/// # Conversions +/// +/// See the [module's toplevel documentation about conversions][conversions] for a discussion on +/// the traits which `OsString` implements for conversions from/to native representations. /// /// [`OsStr`]: struct.OsStr.html +/// [`From`]: ../convert/trait.From.html +/// [`String`]: ../string/struct.String.html +/// [`&str`]: ../primitive.str.html +/// [`u8`]: ../primitive.u8.html +/// [`u16`]: ../primitive.u16.html +/// [String.push_str]: ../string/struct.String.html#method.push_str +/// [`new`]: #method.new +/// [`push`]: #method.push +/// [`as_os_str`]: #method.as_os_str #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct OsString { inner: Buf } -/// Slices into OS strings (see [`OsString`]). +/// Borrowed reference to an OS string (see [`OsString`]). +/// +/// This type represents a borrowed reference to a string in the operating system's preferred +/// representation. +/// +/// `OsStr` is to [`OsString`] as [`String`] is to [`&str`]: the former in each pair are borrowed +/// references; the latter are owned strings. +/// +/// See the [module's toplevel documentation about conversions][conversions] for a discussion on +/// the traits which `OsStr` implements for conversions from/to native representations. /// /// [`OsString`]: struct.OsString.html +/// [conversions]: index.html#conversions #[stable(feature = "rust1", since = "1.0.0")] pub struct OsStr { inner: Slice @@ -260,7 +307,8 @@ impl OsString { /// ``` #[stable(feature = "into_boxed_os_str", since = "1.20.0")] pub fn into_boxed_os_str(self) -> Box { - unsafe { mem::transmute(self.inner.into_box()) } + let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } } } @@ -394,7 +442,7 @@ impl OsStr { } fn from_inner(inner: &Slice) -> &OsStr { - unsafe { mem::transmute(inner) } + unsafe { &*(inner as *const Slice as *const OsStr) } } /// Yields a [`&str`] slice if the `OsStr` is valid Unicode. @@ -511,8 +559,8 @@ impl OsStr { /// [`OsString`]: struct.OsString.html #[stable(feature = "into_boxed_os_str", since = "1.20.0")] pub fn into_os_string(self: Box) -> OsString { - let inner: Box = unsafe { mem::transmute(self) }; - OsString { inner: Buf::from_box(inner) } + let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; + OsString { inner: Buf::from_box(boxed) } } /// Gets the underlying byte representation. @@ -520,14 +568,15 @@ impl OsStr { /// Note: it is *crucial* that this API is private, to avoid /// revealing the internal, platform-specific encodings. fn bytes(&self) -> &[u8] { - unsafe { mem::transmute(&self.inner) } + unsafe { &*(&self.inner as *const _ as *const [u8]) } } } #[stable(feature = "box_from_os_str", since = "1.17.0")] impl<'a> From<&'a OsStr> for Box { fn from(s: &'a OsStr) -> Box { - unsafe { mem::transmute(s.inner.into_box()) } + let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } } } @@ -545,10 +594,47 @@ impl From for Box { } } +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Arc { + #[inline] + fn from(s: OsString) -> Arc { + let arc = s.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a OsStr> for Arc { + #[inline] + fn from(s: &OsStr) -> Arc { + let arc = s.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Rc { + #[inline] + fn from(s: OsString) -> Rc { + let rc = s.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a OsStr> for Rc { + #[inline] + fn from(s: &OsStr) -> Rc { + let rc = s.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } + } +} + #[stable(feature = "box_default_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { - unsafe { mem::transmute(Slice::empty_box()) } + let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } } } @@ -745,6 +831,9 @@ mod tests { use super::*; use sys_common::{AsInner, IntoInner}; + use rc::Rc; + use sync::Arc; + #[test] fn test_os_string_with_capacity() { let os_string = OsString::with_capacity(0); @@ -887,4 +976,21 @@ mod tests { assert_eq!(os_str, os_string); assert!(os_string.capacity() >= 123); } + + #[test] + fn into_rc() { + let orig = "Hello, world!"; + let os_str = OsStr::new(orig); + let rc: Rc = Rc::from(os_str); + let arc: Arc = Arc::from(os_str); + + assert_eq!(&*rc, os_str); + assert_eq!(&*arc, os_str); + + let rc2: Rc = Rc::from(os_str.to_owned()); + let arc2: Arc = Arc::from(os_str.to_owned()); + + assert_eq!(&*rc2, os_str); + assert_eq!(&*arc2, os_str); + } } diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 91600b01298a4..b07733d3c803c 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -1374,14 +1374,17 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// Note that if `from` and `to` both point to the same file, then the file /// will likely get truncated by this operation. /// -/// On success, the total number of bytes copied is returned. +/// On success, the total number of bytes copied is returned and it is equal to +/// the length of the `to` file as reported by `metadata`. /// /// # Platform-specific behavior /// /// This function currently corresponds to the `open` function in Unix /// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. /// `O_CLOEXEC` is set for returned file descriptors. -/// On Windows, this function currently corresponds to `CopyFileEx`. +/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate +/// NTFS streams are copied but only the size of the main stream is returned by +/// this function. /// Note that, this [may change in the future][changes]. /// /// [changes]: ../io/index.html#platform-specific-behavior @@ -2589,13 +2592,25 @@ mod tests { fn copy_file_preserves_streams() { let tmp = tmpdir(); check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); - assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6); + assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); let mut v = Vec::new(); check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); assert_eq!(v, b"carrot".to_vec()); } + #[test] + fn copy_file_returns_metadata_len() { + let tmp = tmpdir(); + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + check!(check!(File::create(&in_path)).write(b"lettuce")); + #[cfg(windows)] + check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); + let copied_len = check!(fs::copy(&in_path, &out_path)); + assert_eq!(check!(out_path.metadata()).len(), copied_len); + } + #[test] fn symlinks_work() { let tmpdir = tmpdir(); diff --git a/src/libstd/heap.rs b/src/libstd/heap.rs index d76ab31862bfc..4d5e4df6f95b8 100644 --- a/src/libstd/heap.rs +++ b/src/libstd/heap.rs @@ -17,6 +17,7 @@ pub use alloc_system::System; #[cfg(not(test))] #[doc(hidden)] +#[allow(unused_attributes)] pub mod __default_lib_allocator { use super::{System, Layout, Alloc, AllocErr}; use ptr; @@ -28,6 +29,7 @@ pub mod __default_lib_allocator { // ABI #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8 { @@ -42,11 +44,13 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_oom(err: *const u8) -> ! { System.oom((*(err as *const AllocErr)).clone()) } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { @@ -54,6 +58,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_usable_size(layout: *const u8, min: *mut usize, max: *mut usize) { @@ -63,6 +68,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_realloc(ptr: *mut u8, old_size: usize, old_align: usize, @@ -81,6 +87,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8 { @@ -95,6 +102,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_excess(size: usize, align: usize, excess: *mut usize, @@ -113,6 +121,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8, old_size: usize, old_align: usize, @@ -135,6 +144,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8, old_size: usize, old_align: usize, @@ -149,6 +159,7 @@ pub mod __default_lib_allocator { } #[no_mangle] + #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8, old_size: usize, old_align: usize, diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 4ebd3554fd142..6d3fbc9d26822 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -147,6 +147,31 @@ impl BufReader { #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut R { &mut self.inner } + /// Returns `true` if there are no bytes in the internal buffer. + /// + /// # Examples + /// ``` + /// # #![feature(bufreader_is_empty)] + /// use std::io::BufReader; + /// use std::io::BufRead; + /// use std::fs::File; + /// + /// # fn foo() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let mut reader = BufReader::new(f1); + /// assert!(reader.is_empty()); + /// + /// if reader.fill_buf()?.len() > 0 { + /// assert!(!reader.is_empty()); + /// } + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "bufreader_is_empty", issue = "45323", reason = "recently added")] + pub fn is_empty(&self) -> bool { + self.pos == self.cap + } + /// Unwraps this `BufReader`, returning the underlying reader. /// /// Note that any leftover data in the internal buffer is lost. diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 32a92145aafed..b5ea5531b65a7 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -230,6 +230,13 @@ impl Read for Cursor where T: AsRef<[u8]> { Ok(n) } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let n = buf.len(); + Read::read_exact(&mut self.fill_buf()?, buf)?; + self.pos += n as u64; + Ok(()) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -475,6 +482,24 @@ mod tests { assert_eq!(reader.read(&mut buf).unwrap(), 0); } + #[test] + fn test_read_exact() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert!(reader.read_exact(&mut buf).is_ok()); + let mut buf = [8]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf[0], 0); + assert_eq!(reader.len(), 7); + let mut buf = [0, 0, 0, 0, 0, 0, 0]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); + assert_eq!(reader.len(), 0); + let mut buf = [0]; + assert!(reader.read_exact(&mut buf).is_err()); + } + #[test] fn test_buf_reader() { let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index d6b41ceda4392..fe1179a3b4a18 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -206,6 +206,14 @@ impl<'a> Read for &'a [u8] { *self = b; Ok(()) } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + buf.extend_from_slice(*self); + let len = self.len(); + *self = &self[len..]; + Ok(len) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 074ab3ebd8fdc..b7a3695b47096 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -366,16 +366,13 @@ fn append_to_string(buf: &mut String, f: F) -> Result fn read_to_end(r: &mut R, buf: &mut Vec) -> Result { let start_len = buf.len(); let mut g = Guard { len: buf.len(), buf: buf }; - let mut new_write_size = 16; let ret; loop { if g.len == g.buf.len() { - if new_write_size < DEFAULT_BUF_SIZE { - new_write_size *= 2; - } unsafe { - g.buf.reserve(new_write_size); - g.buf.set_len(g.len + new_write_size); + g.buf.reserve(32); + let capacity = g.buf.capacity(); + g.buf.set_len(capacity); r.initializer().initialize(&mut g.buf[g.len..]); } } @@ -419,14 +416,8 @@ fn read_to_end(r: &mut R, buf: &mut Vec) -> Result /// /// [`File`]s implement `Read`: /// -/// [`read()`]: trait.Read.html#tymethod.read -/// [`std::io`]: ../../std/io/index.html -/// [`File`]: ../fs/struct.File.html -/// [`BufRead`]: trait.BufRead.html -/// [`BufReader`]: struct.BufReader.html -/// /// ``` -/// use std::io; +/// # use std::io; /// use std::io::prelude::*; /// use std::fs::File; /// @@ -449,7 +440,33 @@ fn read_to_end(r: &mut R, buf: &mut Vec) -> Result /// # Ok(()) /// # } /// ``` +/// +/// Read from `&str` because [`&[u8]`] implements `Read`: +/// +/// ``` +/// # use std::io; +/// use std::io::prelude::*; +/// +/// # fn foo() -> io::Result<()> { +/// let mut b = "This string will be read".as_bytes(); +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// b.read(&mut buffer)?; +/// +/// // etc... it works exactly as a File does! +/// # Ok(()) +/// # } +/// ``` +/// +/// [`read()`]: trait.Read.html#tymethod.read +/// [`std::io`]: ../../std/io/index.html +/// [`File`]: ../fs/struct.File.html +/// [`BufRead`]: trait.BufRead.html +/// [`BufReader`]: struct.BufReader.html +/// [`&[u8]`]: primitive.slice.html #[stable(feature = "rust1", since = "1.0.0")] +#[doc(spotlight)] pub trait Read { /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. @@ -736,10 +753,10 @@ pub trait Read { /// Transforms this `Read` instance to an [`Iterator`] over its bytes. /// - /// The returned type implements [`Iterator`] where the `Item` is [`Result`]`<`[`u8`]`, - /// R::Err>`. The yielded item is [`Ok`] if a byte was successfully read and - /// [`Err`] otherwise for I/O errors. EOF is mapped to returning [`None`] from - /// this iterator. + /// The returned type implements [`Iterator`] where the `Item` is + /// [`Result`]`<`[`u8`]`, `[`io::Error`]`>`. + /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] + /// otherwise. EOF is mapped to returning [`None`] from this iterator. /// /// # Examples /// @@ -748,6 +765,7 @@ pub trait Read { /// [file]: ../fs/struct.File.html /// [`Iterator`]: ../../std/iter/trait.Iterator.html /// [`Result`]: ../../std/result/enum.Result.html + /// [`io::Error`]: ../../std/io/struct.Error.html /// [`u8`]: ../../std/primitive.u8.html /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok /// [`Err`]: ../../std/result/enum.Result.html#variant.Err @@ -967,6 +985,7 @@ impl Initializer { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[doc(spotlight)] pub trait Write { /// Write a buffer into this object, returning how many bytes were written. /// @@ -1410,6 +1429,8 @@ pub trait BufRead: Read { /// /// If successful, this function will return the total number of bytes read. /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// /// # Errors /// /// This function will ignore all instances of [`ErrorKind::Interrupted`] and @@ -1470,6 +1491,8 @@ pub trait BufRead: Read { /// /// If successful, this function will return the total number of bytes read. /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// /// # Errors /// /// This function has the same error semantics as [`read_until`] and will diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index fb489bf487b8b..831688bb73d1c 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -121,10 +121,8 @@ impl io::Read for Maybe { } fn handle_ebadf(r: io::Result, default: T) -> io::Result { - use sys::stdio::EBADF_ERR; - match r { - Err(ref e) if e.raw_os_error() == Some(EBADF_ERR) => Ok(default), + Err(ref e) if stdio::is_ebadf(e) => Ok(default), r => r } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9fc7e2c01aa19..bf177ac7f2c23 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -243,7 +243,10 @@ #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(align_offset)] +#![feature(array_error_internals)] +#![feature(ascii_ctype)] #![feature(asm)] +#![feature(attr_literals)] #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] @@ -257,6 +260,7 @@ #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] +#![feature(fixed_size_array)] #![feature(float_from_str_radix)] #![feature(fn_traits)] #![feature(fnbox)] @@ -290,9 +294,9 @@ #![feature(prelude_import)] #![feature(rand)] #![feature(raw)] +#![feature(repr_align)] #![feature(repr_simd)] #![feature(rustc_attrs)] -#![cfg_attr(not(stage0), feature(rustc_const_unstable))] #![feature(shared)] #![feature(sip_hash_13)] #![feature(slice_bytes)] @@ -315,18 +319,9 @@ #![feature(vec_push_all)] #![feature(doc_cfg)] #![feature(doc_masked)] +#![feature(doc_spotlight)] #![cfg_attr(test, feature(update_panic_count))] - -#![cfg_attr(not(stage0), feature(const_max_value))] -#![cfg_attr(not(stage0), feature(const_atomic_bool_new))] -#![cfg_attr(not(stage0), feature(const_atomic_isize_new))] -#![cfg_attr(not(stage0), feature(const_atomic_usize_new))] -#![cfg_attr(all(not(stage0), windows), feature(const_atomic_ptr_new))] -#![cfg_attr(not(stage0), feature(const_unsafe_cell_new))] -#![cfg_attr(not(stage0), feature(const_cell_new))] -#![cfg_attr(not(stage0), feature(const_once_new))] -#![cfg_attr(not(stage0), feature(const_ptr_null))] -#![cfg_attr(not(stage0), feature(const_ptr_null_mut))] +#![cfg_attr(windows, feature(used))] #![default_lib_allocator] @@ -352,6 +347,7 @@ use prelude::v1::*; // Access to Bencher, etc. #[cfg(test)] extern crate test; +#[cfg(test)] extern crate rand; // We want to reexport a few macros from core but libcore has already been // imported by the compiler (via our #[no_std] attribute) In this case we just @@ -360,9 +356,6 @@ use prelude::v1::*; debug_assert_ne, unreachable, unimplemented, write, writeln, try)] extern crate core as __core; -#[doc(masked)] -#[allow(deprecated)] -extern crate rand as core_rand; #[macro_use] #[macro_reexport(vec, format)] extern crate alloc; @@ -500,24 +493,12 @@ mod sys; // Private support modules mod panicking; -mod rand; mod memchr; // The runtime entry point and a few unstable public functions used by the // compiler pub mod rt; -// Some external utilities of the standard library rely on randomness (aka -// rustc_back::TempDir and tests) and need a way to get at the OS rng we've got -// here. This module is not at all intended for stabilization as-is, however, -// but it may be stabilized long-term. As a result we're exposing a hidden, -// unstable module so we can get our build working. -#[doc(hidden)] -#[unstable(feature = "rand", issue = "27703")] -pub mod __rand { - pub use rand::{thread_rng, ThreadRng, Rng}; -} - // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 8089671f309d2..7d62f94056fb2 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -325,9 +325,10 @@ pub mod builtin { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] - macro_rules! format_args { ($fmt:expr, $($args:tt)*) => ({ - /* compiler built-in */ - }) } + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }); + } /// Inspect an environment variable at compile time. /// @@ -348,7 +349,10 @@ pub mod builtin { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] - macro_rules! env { ($name:expr) => ({ /* compiler built-in */ }) } + macro_rules! env { + ($name:expr) => ({ /* compiler built-in */ }); + ($name:expr,) => ({ /* compiler built-in */ }); + } /// Optionally inspect an environment variable at compile time. /// @@ -400,7 +404,8 @@ pub mod builtin { #[unstable(feature = "concat_idents_macro", issue = "29599")] #[macro_export] macro_rules! concat_idents { - ($($e:ident),*) => ({ /* compiler built-in */ }) + ($($e:ident),*) => ({ /* compiler built-in */ }); + ($($e:ident,)*) => ({ /* compiler built-in */ }); } /// Concatenates literals into a static string slice. @@ -420,7 +425,10 @@ pub mod builtin { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] - macro_rules! concat { ($($e:expr),*) => ({ /* compiler built-in */ }) } + macro_rules! concat { + ($($e:expr),*) => ({ /* compiler built-in */ }); + ($($e:expr,)*) => ({ /* compiler built-in */ }); + } /// A macro which expands to the line number on which it was invoked. /// @@ -490,7 +498,7 @@ pub mod builtin { #[macro_export] macro_rules! file { () => ({ /* compiler built-in */ }) } - /// A macro which stringifies its argument. + /// A macro which stringifies its arguments. /// /// This macro will yield an expression of type `&'static str` which is the /// stringification of all the tokens passed to the macro. No restrictions @@ -507,7 +515,7 @@ pub mod builtin { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] - macro_rules! stringify { ($t:tt) => ({ /* compiler built-in */ }) } + macro_rules! stringify { ($($t:tt)*) => ({ /* compiler built-in */ }) } /// Includes a utf8-encoded file as a string. /// @@ -663,3 +671,39 @@ pub mod builtin { #[macro_export] macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) } } + +/// A macro for defining #[cfg] if-else statements. +/// +/// This is similar to the `if/elif` C preprocessor macro by allowing definition +/// of a cascade of `#[cfg]` cases, emitting the implementation which matches +/// first. +/// +/// This allows you to conveniently provide a long list #[cfg]'d blocks of code +/// without having to rewrite each clause multiple times. +macro_rules! cfg_if { + ($( + if #[cfg($($meta:meta),*)] { $($it:item)* } + ) else * else { + $($it2:item)* + }) => { + __cfg_if_items! { + () ; + $( ( ($($meta),*) ($($it)*) ), )* + ( () ($($it2)*) ), + } + } +} + +macro_rules! __cfg_if_items { + (($($not:meta,)*) ; ) => {}; + (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { + __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } + __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } + } +} + +macro_rules! __cfg_if_apply { + ($m:meta, $($it:item)*) => { + $(#[$m] $it)* + } +} diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index e1d7a2531b6c9..1ca7e66ed9ca9 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -759,7 +759,7 @@ impl hash::Hash for SocketAddrV6 { /// ``` /// /// [`TcpStream::connect`] is an example of an function that utilizes -/// `ToSocketsAddr` as a trait bound on its parameter in order to accept +/// `ToSocketAddrs` as a trait bound on its parameter in order to accept /// different types: /// /// ```no_run diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index eea604943af8b..c832f8a934d3d 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -719,7 +719,8 @@ impl Eq for Ipv4Addr {} #[stable(feature = "rust1", since = "1.0.0")] impl hash::Hash for Ipv4Addr { fn hash(&self, s: &mut H) { - self.inner.s_addr.hash(s) + // `inner` is #[repr(packed)], so we need to copy `s_addr`. + {self.inner.s_addr}.hash(s) } } diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index aff9af66444c4..539ff1df1876f 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -167,7 +167,7 @@ impl TcpStream { /// connection request. /// /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - #[stable(feature = "tcpstream_connect_timeout", since = "1.22.0")] + #[stable(feature = "tcpstream_connect_timeout", since = "1.21.0")] pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { net_imp::TcpStream::connect_timeout(addr, timeout).map(TcpStream) } @@ -194,12 +194,12 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; + /// use std::net::{IpAddr, Ipv4Addr, TcpStream}; /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// assert_eq!(stream.local_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// assert_eq!(stream.local_addr().unwrap().ip(), + /// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn local_addr(&self) -> io::Result { @@ -498,18 +498,46 @@ impl TcpStream { /// Moves this TCP stream into or out of nonblocking mode. /// - /// On Unix this corresponds to calling fcntl, and on Windows this - /// corresponds to calling ioctlsocket. + /// This will result in `read`, `write`, `recv` and `send` operations + /// becoming nonblocking, i.e. immediately returning from their calls. + /// If the IO operation is successful, `Ok` is returned and no further + /// action is required. If the IO operation could not be completed and needs + /// to be retried, an error with kind [`io::ErrorKind::WouldBlock`] is + /// returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. /// /// # Examples /// + /// Reading bytes from a TCP stream in non-blocking mode: + /// /// ```no_run + /// use std::io::{self, Read}; /// use std::net::TcpStream; /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); + /// let mut stream = TcpStream::connect("127.0.0.1:7878") + /// .expect("Couldn't connect to the server..."); /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// let mut buf = vec![]; + /// loop { + /// match stream.read_to_end(&mut buf) { + /// Ok(_) => break, + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// }; + /// }; + /// println!("bytes: {:?}", buf); /// ``` + /// + /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) @@ -780,17 +808,48 @@ impl TcpListener { /// Moves this TCP stream into or out of nonblocking mode. /// - /// On Unix this corresponds to calling fcntl, and on Windows this - /// corresponds to calling ioctlsocket. + /// This will result in the `accept` operation becoming nonblocking, + /// i.e. immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. /// /// # Examples /// + /// Bind a TCP listener to an address, listen for connections, and read + /// bytes in nonblocking mode: + /// /// ```no_run + /// use std::io; /// use std::net::TcpListener; /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// # fn handle_connection(stream: std::net::TcpStream) { unimplemented!() } + /// for stream in listener.incoming() { + /// match stream { + /// Ok(s) => { + /// // do something with the TcpStream + /// handle_connection(s); + /// } + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// continue; + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// } + /// } /// ``` + /// + /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) @@ -1579,6 +1638,21 @@ mod tests { "bad error: {} {:?}", e, e.kind()); } + #[test] + fn connect_timeout_unbound() { + // bind and drop a socket to track down a "probably unassigned" port + let socket = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = socket.local_addr().unwrap(); + drop(socket); + + let timeout = Duration::from_secs(1); + let e = TcpStream::connect_timeout(&addr, timeout).unwrap_err(); + assert!(e.kind() == io::ErrorKind::ConnectionRefused || + e.kind() == io::ErrorKind::TimedOut || + e.kind() == io::ErrorKind::Other, + "bad error: {} {:?}", e, e.kind()); + } + #[test] fn connect_timeout_valid() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index a8a242846d7ce..84ceaa659510f 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -168,7 +168,7 @@ impl UdpSocket { /// This will return an error when the IP version of the local socket /// does not match that returned from [`ToSocketAddrs`]. /// - /// See https://github.com/rust-lang/rust/issues/34202 for more details. + /// See for more details. /// /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html /// @@ -721,16 +721,45 @@ impl UdpSocket { /// Moves this UDP socket into or out of nonblocking mode. /// - /// On Unix this corresponds to calling fcntl, and on Windows this - /// corresponds to calling ioctlsocket. + /// This will result in `recv`, `recv_from`, `send`, and `send_to` + /// operations becoming nonblocking, i.e. immediately returning from their + /// calls. If the IO operation is successful, `Ok` is returned and no + /// further action is required. If the IO operation could not be completed + /// and needs to be retried, an error with kind + /// [`io::ErrorKind::WouldBlock`] is returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. + /// + /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock /// /// # Examples /// + /// Create a UDP socket bound to `127.0.0.1:7878` and read bytes in + /// nonblocking mode: + /// /// ```no_run + /// use std::io; /// use std::net::UdpSocket; /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_nonblocking(true).expect("set_nonblocking call failed"); + /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); + /// socket.set_nonblocking(true).unwrap(); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// let mut buf = [0; 10]; + /// let (num_bytes_read, _) = loop { + /// match socket.recv_from(&mut buf) { + /// Ok(n) => break n, + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// } + /// }; + /// println!("bytes: {:?}", &buf[..num_bytes_read]); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { diff --git a/src/libstd/os/linux/fs.rs b/src/libstd/os/linux/fs.rs index 7ebda5ed744fd..5d37d970e89b6 100644 --- a/src/libstd/os/linux/fs.rs +++ b/src/libstd/os/linux/fs.rs @@ -24,9 +24,25 @@ pub trait MetadataExt { /// Gain a reference to the underlying `stat` structure which contains /// the raw information returned by the OS. /// - /// The contents of the returned `stat` are **not** consistent across + /// The contents of the returned [`stat`] are **not** consistent across /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the /// cross-Unix abstractions contained within the raw stat. + /// + /// [`stat`]: ../../../../std/os/linux/raw/struct.stat.html + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let stat = meta.as_raw_stat(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] #[rustc_deprecated(since = "1.8.0", reason = "deprecated in favor of the accessor \ @@ -35,54 +51,278 @@ pub trait MetadataExt { fn as_raw_stat(&self) -> &raw::stat; /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_dev(&self) -> u64; /// Returns the inode number. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_ino(&self) -> u64; /// Returns the file type and mode. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_mode(&self) -> u32; /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_nlink(&self) -> u64; /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_uid(&self) -> u32; /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_gid(&self) -> u32; /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_rdev(&self) -> u64; /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. /// /// The size of a symbolic link is the length of the pathname it contains, /// without a terminating null byte. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_size(&self) -> u64; /// Returns the last access time. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_atime(&self) -> i64; /// Returns the last access time, nano seconds part. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_atime_nsec(&self) -> i64; /// Returns the last modification time. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_mtime(&self) -> i64; /// Returns the last modification time, nano seconds part. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_mtime_nsec(&self) -> i64; /// Returns the last status change time. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_ctime(&self) -> i64; /// Returns the last status change time, nano seconds part. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_ctime_nsec(&self) -> i64; /// Returns the "preferred" blocksize for efficient filesystem I/O. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_blksize(&self) -> u64; /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::linux::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_blocks(&self) -> u64; } diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index b460bd90f1735..ac7809451d152 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -13,36 +13,53 @@ #![stable(feature = "os", since = "1.0.0")] #![allow(missing_docs, bad_style, missing_debug_implementations)] -#[cfg(all(not(dox), any(target_os = "redox", unix)))] -#[stable(feature = "rust1", since = "1.0.0")] -pub use sys::ext as unix; -#[cfg(all(not(dox), windows))] -#[stable(feature = "rust1", since = "1.0.0")] -pub use sys::ext as windows; - -#[cfg(dox)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use sys::unix_ext as unix; -#[cfg(dox)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use sys::windows_ext as windows; - -#[cfg(any(dox, target_os = "linux", target_os = "l4re"))] -#[doc(cfg(target_os = "linux"))] -pub mod linux; - -#[cfg(all(not(dox), target_os = "android"))] pub mod android; -#[cfg(all(not(dox), target_os = "bitrig"))] pub mod bitrig; -#[cfg(all(not(dox), target_os = "dragonfly"))] pub mod dragonfly; -#[cfg(all(not(dox), target_os = "freebsd"))] pub mod freebsd; -#[cfg(all(not(dox), target_os = "haiku"))] pub mod haiku; -#[cfg(all(not(dox), target_os = "ios"))] pub mod ios; -#[cfg(all(not(dox), target_os = "macos"))] pub mod macos; -#[cfg(all(not(dox), target_os = "nacl"))] pub mod nacl; -#[cfg(all(not(dox), target_os = "netbsd"))] pub mod netbsd; -#[cfg(all(not(dox), target_os = "openbsd"))] pub mod openbsd; -#[cfg(all(not(dox), target_os = "solaris"))] pub mod solaris; -#[cfg(all(not(dox), target_os = "emscripten"))] pub mod emscripten; -#[cfg(all(not(dox), target_os = "fuchsia"))] pub mod fuchsia; +cfg_if! { + if #[cfg(dox)] { + + // When documenting libstd we want to show unix/windows/linux modules as + // these are the "main modules" that are used across platforms. This + // should help show platform-specific functionality in a hopefully + // cross-platform way in the documentation + + #[stable(feature = "rust1", since = "1.0.0")] + pub use sys::unix_ext as unix; + + #[stable(feature = "rust1", since = "1.0.0")] + pub use sys::windows_ext as windows; + + #[doc(cfg(target_os = "linux"))] + pub mod linux; + + } else { + + // If we're not documenting libstd then we just expose everything as we + // otherwise would. + + #[cfg(target_os = "android")] pub mod android; + #[cfg(target_os = "bitrig")] pub mod bitrig; + #[cfg(target_os = "dragonfly")] pub mod dragonfly; + #[cfg(target_os = "freebsd")] pub mod freebsd; + #[cfg(target_os = "haiku")] pub mod haiku; + #[cfg(target_os = "ios")] pub mod ios; + #[cfg(target_os = "macos")] pub mod macos; + #[cfg(target_os = "netbsd")] pub mod netbsd; + #[cfg(target_os = "openbsd")] pub mod openbsd; + #[cfg(target_os = "solaris")] pub mod solaris; + #[cfg(target_os = "emscripten")] pub mod emscripten; + #[cfg(target_os = "fuchsia")] pub mod fuchsia; + + #[cfg(any(target_os = "redox", unix))] + #[stable(feature = "rust1", since = "1.0.0")] + pub use sys::ext as unix; + + #[cfg(windows)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use sys::ext as windows; + + #[cfg(any(target_os = "linux", target_os = "l4re"))] + pub mod linux; + + } +} pub mod raw; diff --git a/src/libstd/os/nacl/fs.rs b/src/libstd/os/nacl/fs.rs deleted file mode 100644 index 3e0fb44b01e30..0000000000000 --- a/src/libstd/os/nacl/fs.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use libc; - -use fs::Metadata; -use sys_common::AsInner; - -#[allow(deprecated)] -use os::nacl::raw; - -/// OS-specific extension methods for `fs::Metadata` -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated(since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait")] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { - &*(self.as_inner().as_inner() as *const libc::stat64 - as *const raw::stat) - } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } -} diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs deleted file mode 100644 index 3c3d4410a2a16..0000000000000 --- a/src/libstd/os/nacl/raw.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Nacl-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![rustc_deprecated(since = "1.8.0", - reason = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions")] -#![allow(deprecated)] - -#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; - -#[repr(C)] -#[derive(Copy, Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime_nsec: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime_nsec: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime_nsec: i64, -} diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 97b09b7e2ad99..219e55d6c1206 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Panic support in the standard library +//! Panic support in the standard library. #![stable(feature = "std_panic", since = "1.9.0")] @@ -188,6 +188,8 @@ pub struct AssertUnwindSafe( // * Types like Mutex/RwLock which are explicilty poisoned are unwind safe // * Our custom AssertUnwindSafe wrapper is indeed unwind safe #[stable(feature = "catch_unwind", since = "1.9.0")] +#[allow(unknown_lints)] +#[allow(auto_impl)] impl UnwindSafe for .. {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<'a, T: ?Sized> !UnwindSafe for &'a mut T {} @@ -221,6 +223,8 @@ impl UnwindSafe for Arc {} // only thing which doesn't implement it (which then transitively applies to // everything else). #[stable(feature = "catch_unwind", since = "1.9.0")] +#[allow(unknown_lints)] +#[allow(auto_impl)] impl RefUnwindSafe for .. {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl !RefUnwindSafe for UnsafeCell {} diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 830b9dc475d6d..eb125a4737a1c 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -77,7 +77,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -use ascii::*; use borrow::{Borrow, Cow}; use cmp; use error::Error; @@ -86,8 +85,9 @@ use fs; use hash::{Hash, Hasher}; use io; use iter::{self, FusedIterator}; -use mem; use ops::{self, Deref}; +use rc::Rc; +use sync::Arc; use ffi::{OsStr, OsString}; @@ -317,10 +317,10 @@ fn iter_after(mut iter: I, mut prefix: J) -> Option // See note at the top of this module to understand why these are used: fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { - unsafe { mem::transmute(s) } + unsafe { &*(s as *const OsStr as *const [u8]) } } unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { - mem::transmute(s) + &*(s as *const [u8] as *const OsStr) } // Detect scheme on Redox @@ -1334,7 +1334,8 @@ impl PathBuf { /// [`Path`]: struct.Path.html #[stable(feature = "into_boxed_path", since = "1.20.0")] pub fn into_boxed_path(self) -> Box { - unsafe { mem::transmute(self.inner.into_boxed_os_str()) } + let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; + unsafe { Box::from_raw(rw) } } } @@ -1342,7 +1343,8 @@ impl PathBuf { impl<'a> From<&'a Path> for Box { fn from(path: &'a Path) -> Box { let boxed: Box = path.inner.into(); - unsafe { mem::transmute(boxed) } + let rw = Box::into_raw(boxed) as *mut Path; + unsafe { Box::from_raw(rw) } } } @@ -1452,6 +1454,42 @@ impl<'a> From for Cow<'a, Path> { } } +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Arc { + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a Path> for Arc { + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl From for Rc { + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.23.0")] +impl<'a> From<&'a Path> for Rc { + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for Path { type Owned = PathBuf; @@ -1589,7 +1627,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new + ?Sized>(s: &S) -> &Path { - unsafe { mem::transmute(s.as_ref()) } + unsafe { &*(s.as_ref() as *const OsStr as *const Path) } } /// Yields the underlying [`OsStr`] slice. @@ -1690,11 +1728,11 @@ impl Path { #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - if !cfg!(target_os = "redox") { - self.has_root() && (cfg!(unix) || self.prefix().is_some()) - } else { + if cfg!(target_os = "redox") { // FIXME: Allow Redox prefixes - has_redox_scheme(self.as_u8_slice()) + self.has_root() || has_redox_scheme(self.as_u8_slice()) + } else { + self.has_root() && (cfg!(unix) || self.prefix().is_some()) } } @@ -2312,7 +2350,8 @@ impl Path { /// [`PathBuf`]: struct.PathBuf.html #[stable(feature = "into_boxed_path", since = "1.20.0")] pub fn into_path_buf(self: Box) -> PathBuf { - let inner: Box = unsafe { mem::transmute(self) }; + let rw = Box::into_raw(self) as *mut OsStr; + let inner = unsafe { Box::from_raw(rw) }; PathBuf { inner: OsString::from(inner) } } } @@ -2567,6 +2606,9 @@ impl Error for StripPrefixError { mod tests { use super::*; + use rc::Rc; + use sync::Arc; + macro_rules! t( ($path:expr, iter: $iter:expr) => ( { @@ -3751,7 +3793,7 @@ mod tests { } #[test] - fn test_eq_recievers() { + fn test_eq_receivers() { use borrow::Cow; let borrowed: &Path = Path::new("foo/bar"); @@ -3969,4 +4011,21 @@ mod tests { assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b"); assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); } + + #[test] + fn into_rc() { + let orig = "hello/world"; + let path = Path::new(orig); + let rc: Rc = Rc::from(path); + let arc: Arc = Arc::from(path); + + assert_eq!(&*rc, path); + assert_eq!(&*arc, path); + + let rc2: Rc = Rc::from(path.to_owned()); + let arc2: Arc = Arc::from(path.to_owned()); + + assert_eq!(&*rc2, path); + assert_eq!(&*arc2, path); + } } diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 1edb35d8fe741..9e1da318242bc 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -284,7 +284,6 @@ mod prim_pointer { } /// Arrays of sizes from 0 to 32 (inclusive) implement the following traits if /// the element type allows it: /// -/// - [`Clone`][clone] (only if `T: `[`Copy`][copy]) /// - [`Debug`][debug] /// - [`IntoIterator`][intoiterator] (implemented for `&[T; N]` and `&mut [T; N]`) /// - [`PartialEq`][partialeq], [`PartialOrd`][partialord], [`Eq`][eq], [`Ord`][ord] @@ -299,8 +298,10 @@ mod prim_pointer { } /// entirely different types. As a stopgap, trait implementations are /// statically generated up to size 32. /// -/// Arrays of *any* size are [`Copy`][copy] if the element type is [`Copy`][copy]. This -/// works because the [`Copy`][copy] trait is specially known to the compiler. +/// Arrays of *any* size are [`Copy`][copy] if the element type is [`Copy`][copy] +/// and [`Clone`][clone] if the element type is [`Clone`][clone]. This works +/// because [`Copy`][copy] and [`Clone`][clone] traits are specially known +/// to the compiler. /// /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on /// an array. Indeed, this provides most of the API for working with arrays. diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 1869ad3ed707a..2335695ae42d4 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -10,25 +10,66 @@ //! A module for working with processes. //! -//! # Examples +//! This module is mostly concerned with spawning and interacting with child +//! processes, but it also provides [`abort`] and [`exit`] for terminating the +//! current process. //! -//! Basic usage where we try to execute the `cat` shell command: +//! # Spawning a process //! -//! ```should_panic +//! The [`Command`] struct is used to configure and spawn processes: +//! +//! ``` //! use std::process::Command; //! -//! let mut child = Command::new("/bin/cat") -//! .arg("file.txt") -//! .spawn() -//! .expect("failed to execute child"); +//! let output = Command::new("echo") +//! .arg("Hello world") +//! .output() +//! .expect("Failed to execute command"); +//! +//! assert_eq!(b"Hello world\n", output.stdout.as_slice()); +//! ``` +//! +//! Several methods on [`Command`], such as [`spawn`] or [`output`], can be used +//! to spawn a process. In particular, [`output`] spawns the child process and +//! waits until the process terminates, while [`spawn`] will return a [`Child`] +//! that represents the spawned child process. +//! +//! # Handling I/O +//! +//! The [`stdout`], [`stdin`], and [`stderr`] of a child process can be +//! configured by passing an [`Stdio`] to the corresponding method on +//! [`Command`]. Once spawned, they can be accessed from the [`Child`]. For +//! example, piping output from one command into another command can be done +//! like so: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! +//! // stdout must be configured with `Stdio::piped` in order to use +//! // `echo_child.stdout` +//! let echo_child = Command::new("echo") +//! .arg("Oh no, a tpyo!") +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start echo process"); //! -//! let ecode = child.wait() -//! .expect("failed to wait on child"); +//! // Note that `echo_child` is moved here, but we won't be needing +//! // `echo_child` anymore +//! let echo_out = echo_child.stdout.expect("Failed to open echo stdout"); //! -//! assert!(ecode.success()); +//! let mut sed_child = Command::new("sed") +//! .arg("s/tpyo/typo/") +//! .stdin(Stdio::from(echo_out)) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start sed process"); +//! +//! let output = sed_child.wait_with_output().expect("Failed to wait on sed"); +//! assert_eq!(b"Oh no, a typo!\n", output.stdout.as_slice()); //! ``` //! -//! Calling a command with input and reading its output: +//! Note that [`ChildStderr`] and [`ChildStdout`] implement [`Write`] and +//! [`ChildStdin`] implements [`Read`]: //! //! ```no_run //! use std::process::{Command, Stdio}; @@ -52,6 +93,26 @@ //! //! assert_eq!(b"test", output.stdout.as_slice()); //! ``` +//! +//! [`abort`]: fn.abort.html +//! [`exit`]: fn.exit.html +//! +//! [`Command`]: struct.Command.html +//! [`spawn`]: struct.Command.html#method.spawn +//! [`output`]: struct.Command.html#method.output +//! +//! [`Child`]: struct.Child.html +//! [`ChildStdin`]: struct.ChildStdin.html +//! [`ChildStdout`]: struct.ChildStdout.html +//! [`ChildStderr`]: struct.ChildStderr.html +//! [`Stdio`]: struct.Stdio.html +//! +//! [`stdout`]: struct.Command.html#method.stdout +//! [`stdin`]: struct.Command.html#method.stdin +//! [`stderr`]: struct.Command.html#method.stderr +//! +//! [`Write`]: ../io/trait.Write.html +//! [`Read`]: ../io/trait.Read.html #![stable(feature = "process", since = "1.0.0")] @@ -343,7 +404,7 @@ impl Command { /// The search path to be used may be controlled by setting the /// `PATH` environment variable on the Command, /// but this has some implementation limitations on Windows - /// (see https://github.com/rust-lang/rust/issues/37519). + /// (see ). /// /// # Examples /// @@ -552,6 +613,12 @@ impl Command { /// Configuration for the child process's standard input (stdin) handle. /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: struct.Stdio.html#method.inherit + /// [`piped`]: struct.Stdio.html#method.piped + /// /// # Examples /// /// Basic usage: @@ -572,6 +639,12 @@ impl Command { /// Configuration for the child process's standard output (stdout) handle. /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: struct.Stdio.html#method.inherit + /// [`piped`]: struct.Stdio.html#method.piped + /// /// # Examples /// /// Basic usage: @@ -592,6 +665,12 @@ impl Command { /// Configuration for the child process's standard error (stderr) handle. /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: struct.Stdio.html#method.inherit + /// [`piped`]: struct.Stdio.html#method.piped + /// /// # Examples /// /// Basic usage: @@ -633,8 +712,10 @@ impl Command { /// Executes the command as a child process, waiting for it to finish and /// collecting all of its output. /// - /// By default, stdin, stdout and stderr are captured (and used to - /// provide the resulting output). + /// By default, stdout and stderr are captured (and used to provide the + /// resulting output). Stdin is not inherited from the parent and any + /// attempt by the child process to read from the stdin stream will result + /// in the stream immediately closing. /// /// # Examples /// @@ -702,6 +783,15 @@ impl AsInnerMut for Command { } /// The output of a finished process. +/// +/// This is returned in a Result by either the [`output`] method of a +/// [`Command`], or the [`wait_with_output`] method of a [`Child`] +/// process. +/// +/// [`Command`]: struct.Command.html +/// [`Child`]: struct.Child.html +/// [`output`]: struct.Command.html#method.output +/// [`wait_with_output`]: struct.Child.html#method.wait_with_output #[derive(PartialEq, Eq, Clone)] #[stable(feature = "process", since = "1.0.0")] pub struct Output { @@ -742,21 +832,128 @@ impl fmt::Debug for Output { } } -/// Describes what to do with a standard I/O stream for a child process. +/// Describes what to do with a standard I/O stream for a child process when +/// passed to the [`stdin`], [`stdout`], and [`stderr`] methods of [`Command`]. +/// +/// [`stdin`]: struct.Command.html#method.stdin +/// [`stdout`]: struct.Command.html#method.stdout +/// [`stderr`]: struct.Command.html#method.stderr +/// [`Command`]: struct.Command.html #[stable(feature = "process", since = "1.0.0")] pub struct Stdio(imp::Stdio); impl Stdio { /// A new pipe should be arranged to connect the parent and child processes. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "Hello, world!\n"); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::io::Write; + /// use std::process::{Command, Stdio}; + /// + /// let mut child = Command::new("rev") + /// .stdin(Stdio::piped()) + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("Failed to spawn child process"); + /// + /// { + /// let mut stdin = child.stdin.as_mut().expect("Failed to open stdin"); + /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); + /// } + /// + /// let output = child.wait_with_output().expect("Failed to read stdout"); + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH\n"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) } /// The child inherits from the corresponding parent descriptor. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::inherit()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // "Hello, world!" echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::inherit()) + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// println!("You piped in the reverse of: {}", String::from_utf8_lossy(&output.stdout)); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn inherit() -> Stdio { Stdio(imp::Stdio::Inherit) } /// This stream will be ignored. This is the equivalent of attaching the /// stream to `/dev/null` + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::null()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Ignores any piped-in input + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn null() -> Stdio { Stdio(imp::Stdio::Null) } } @@ -1083,8 +1280,6 @@ impl Child { /// function and compute the exit code from its return value: /// /// ``` -/// use std::io::{self, Write}; -/// /// fn run_app() -> Result<(), ()> { /// // Application logic here /// Ok(()) @@ -1094,7 +1289,7 @@ impl Child { /// ::std::process::exit(match run_app() { /// Ok(_) => 0, /// Err(err) => { -/// writeln!(io::stderr(), "error: {:?}", err).unwrap(); +/// eprintln!("error: {:?}", err); /// 1 /// } /// }); @@ -1124,7 +1319,15 @@ pub fn exit(code: i32) -> ! { /// /// Note that because this function never returns, and that it terminates the /// process, no destructors on the current stack or any other thread's stack -/// will be run. If a clean shutdown is needed it is recommended to only call +/// will be run. +/// +/// This is in contrast to the default behaviour of [`panic!`] which unwinds +/// the current thread's stack and calls all destructors. +/// When `panic="abort"` is set, either as an argument to `rustc` or in a +/// crate's Cargo.toml, [`panic!`] and `abort` are similar. However, +/// [`panic!`] will still call the [panic hook] while `abort` will not. +/// +/// If a clean shutdown is needed it is recommended to only call /// this function at a known point where there are no more destructors left /// to run. /// @@ -1142,7 +1345,7 @@ pub fn exit(code: i32) -> ! { /// } /// ``` /// -/// The [`abort`] function terminates the process, so the destructor will not +/// The `abort` function terminates the process, so the destructor will not /// get run on the example below: /// /// ```no_run @@ -1162,11 +1365,33 @@ pub fn exit(code: i32) -> ! { /// // the destructor implemented for HasDrop will never get run /// } /// ``` +/// +/// [`panic!`]: ../../std/macro.panic.html +/// [panic hook]: ../../std/panic/fn.set_hook.html #[stable(feature = "process_abort", since = "1.17.0")] pub fn abort() -> ! { unsafe { ::sys::abort_internal() }; } +/// Returns the OS-assigned process identifier associated with this process. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// #![feature(getpid)] +/// use std::process; +/// +/// println!("My pid is {}", process::id()); +/// ``` +/// +/// +#[unstable(feature = "getpid", issue = "44971", reason = "recently added")] +pub fn id() -> u32 { + ::sys::os::getpid() +} + #[cfg(all(test, not(target_os = "emscripten")))] mod tests { use io::prelude::*; diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs deleted file mode 100644 index 8da070e7a497a..0000000000000 --- a/src/libstd/rand/mod.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utilities for random number generation -//! -//! The key functions are `random()` and `Rng::gen()`. These are polymorphic -//! and so can be used to generate any type that implements `Rand`. Type inference -//! means that often a simple call to `rand::random()` or `rng.gen()` will -//! suffice, but sometimes an annotation is required, e.g. `rand::random::()`. -//! -//! See the `distributions` submodule for sampling random numbers from -//! distributions like normal and exponential. -//! -//! # Thread-local RNG -//! -//! There is built-in support for a RNG associated with each thread stored -//! in thread-local storage. This RNG can be accessed via `thread_rng`, or -//! used implicitly via `random`. This RNG is normally randomly seeded -//! from an operating-system source of randomness, e.g. `/dev/urandom` on -//! Unix systems, and will automatically reseed itself from this source -//! after generating 32 KiB of random data. -//! -//! # Cryptographic security -//! -//! An application that requires an entropy source for cryptographic purposes -//! must use `OsRng`, which reads randomness from the source that the operating -//! system provides (e.g. `/dev/urandom` on Unixes or `CryptGenRandom()` on Windows). -//! The other random number generators provided by this module are not suitable -//! for such purposes. -//! -//! *Note*: many Unix systems provide `/dev/random` as well as `/dev/urandom`. -//! This module uses `/dev/urandom` for the following reasons: -//! -//! - On Linux, `/dev/random` may block if entropy pool is empty; `/dev/urandom` will not block. -//! This does not mean that `/dev/random` provides better output than -//! `/dev/urandom`; the kernel internally runs a cryptographically secure pseudorandom -//! number generator (CSPRNG) based on entropy pool for random number generation, -//! so the "quality" of `/dev/random` is not better than `/dev/urandom` in most cases. -//! However, this means that `/dev/urandom` can yield somewhat predictable randomness -//! if the entropy pool is very small, such as immediately after first booting. -//! Linux 3.17 added the `getrandom(2)` system call which solves the issue: it blocks if entropy -//! pool is not initialized yet, but it does not block once initialized. -//! `getrandom(2)` was based on `getentropy(2)`, an existing system call in OpenBSD. -//! `OsRng` tries to use `getrandom(2)` if available, and use `/dev/urandom` fallback if not. -//! If an application does not have `getrandom` and likely to be run soon after first booting, -//! or on a system with very few entropy sources, one should consider using `/dev/random` via -//! `ReaderRng`. -//! - On some systems (e.g. FreeBSD, OpenBSD and macOS) there is no difference -//! between the two sources. (Also note that, on some systems e.g. FreeBSD, both `/dev/random` -//! and `/dev/urandom` may block once if the CSPRNG has not seeded yet.) - -#![unstable(feature = "rand", issue = "27703")] - -use cell::RefCell; -use fmt; -use io; -use mem; -use rc::Rc; -use sys; - -#[cfg(target_pointer_width = "32")] -use core_rand::IsaacRng as IsaacWordRng; -#[cfg(target_pointer_width = "64")] -use core_rand::Isaac64Rng as IsaacWordRng; - -pub use core_rand::{Rand, Rng, SeedableRng}; -pub use core_rand::{XorShiftRng, IsaacRng, Isaac64Rng}; -pub use core_rand::reseeding; - -pub mod reader; - -/// The standard RNG. This is designed to be efficient on the current -/// platform. -#[derive(Copy, Clone)] -pub struct StdRng { - rng: IsaacWordRng, -} - -impl StdRng { - /// Create a randomly seeded instance of `StdRng`. - /// - /// This is a very expensive operation as it has to read - /// randomness from the operating system and use this in an - /// expensive seeding operation. If one is only generating a small - /// number of random numbers, or doesn't need the utmost speed for - /// generating each number, `thread_rng` and/or `random` may be more - /// appropriate. - /// - /// Reading the randomness from the OS may fail, and any error is - /// propagated via the `io::Result` return value. - pub fn new() -> io::Result { - OsRng::new().map(|mut r| StdRng { rng: r.gen() }) - } -} - -impl Rng for StdRng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.rng.next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.rng.next_u64() - } -} - -impl<'a> SeedableRng<&'a [usize]> for StdRng { - fn reseed(&mut self, seed: &'a [usize]) { - // the internal RNG can just be seeded from the above - // randomness. - self.rng.reseed(unsafe {mem::transmute(seed)}) - } - - fn from_seed(seed: &'a [usize]) -> StdRng { - StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) } - } -} - -/// Controls how the thread-local RNG is reseeded. -struct ThreadRngReseeder; - -impl reseeding::Reseeder for ThreadRngReseeder { - fn reseed(&mut self, rng: &mut StdRng) { - *rng = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not reseed thread_rng: {}", e) - } - } -} -const THREAD_RNG_RESEED_THRESHOLD: usize = 32_768; -type ThreadRngInner = reseeding::ReseedingRng; - -/// The thread-local RNG. -#[derive(Clone)] -pub struct ThreadRng { - rng: Rc>, -} - -impl fmt::Debug for ThreadRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad("ThreadRng { .. }") - } -} - -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`. -/// -/// The RNG provided will reseed itself from the operating system -/// after generating a certain amount of randomness. -/// -/// The internal RNG used is platform and architecture dependent, even -/// if the operating system random number generator is rigged to give -/// the same sequence always. If absolute consistency is required, -/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. -pub fn thread_rng() -> ThreadRng { - // used to make space in TLS for a random number generator - thread_local!(static THREAD_RNG_KEY: Rc> = { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize thread_rng: {}", e) - }; - let rng = reseeding::ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - ThreadRngReseeder); - Rc::new(RefCell::new(rng)) - }); - - ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } -} - -impl Rng for ThreadRng { - fn next_u32(&mut self) -> u32 { - self.rng.borrow_mut().next_u32() - } - - fn next_u64(&mut self) -> u64 { - self.rng.borrow_mut().next_u64() - } - - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.borrow_mut().fill_bytes(bytes) - } -} - -/// A random number generator that retrieves randomness straight from -/// the operating system. Platform sources: -/// -/// - Unix-like systems (Linux, Android, macOS): read directly from -/// `/dev/urandom`, or from `getrandom(2)` system call if available. -/// - Windows: calls `CryptGenRandom`, using the default cryptographic -/// service provider with the `PROV_RSA_FULL` type. -/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. -/// - OpenBSD: uses the `getentropy(2)` system call. -/// -/// This does not block. -pub struct OsRng(sys::rand::OsRng); - -impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - sys::rand::OsRng::new().map(OsRng) - } -} - -impl Rng for OsRng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.0.fill_bytes(bytes) - } -} - - -#[cfg(test)] -mod tests { - use sync::mpsc::channel; - use rand::Rng; - use super::OsRng; - use thread; - - #[test] - fn test_os_rng() { - let mut r = OsRng::new().unwrap(); - - r.next_u32(); - r.next_u64(); - - let mut v = [0; 1000]; - r.fill_bytes(&mut v); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn test_os_rng_tasks() { - - let mut txs = vec![]; - for _ in 0..20 { - let (tx, rx) = channel(); - txs.push(tx); - - thread::spawn(move|| { - // wait until all the threads are ready to go. - rx.recv().unwrap(); - - // deschedule to attempt to interleave things as much - // as possible (XXX: is this a good test?) - let mut r = OsRng::new().unwrap(); - thread::yield_now(); - let mut v = [0; 1000]; - - for _ in 0..100 { - r.next_u32(); - thread::yield_now(); - r.next_u64(); - thread::yield_now(); - r.fill_bytes(&mut v); - thread::yield_now(); - } - }); - } - - // start all the threads - for tx in &txs { - tx.send(()).unwrap(); - } - } -} diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 06fd838ea06d9..40b24cedcdcf5 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -23,7 +23,6 @@ #![doc(hidden)] - // Reexport some of our utilities which are expected by other crates. pub use panicking::{begin_panic, begin_panic_fmt, update_panic_count}; diff --git a/src/libstd/sync/mpsc/cache_aligned.rs b/src/libstd/sync/mpsc/cache_aligned.rs new file mode 100644 index 0000000000000..5af01262573f3 --- /dev/null +++ b/src/libstd/sync/mpsc/cache_aligned.rs @@ -0,0 +1,37 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ops::{Deref, DerefMut}; + +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(align(64))] +pub(super) struct Aligner; + +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(super) struct CacheAligned(pub T, pub Aligner); + +impl Deref for CacheAligned { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CacheAligned { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl CacheAligned { + pub(super) fn new(t: T) -> Self { + CacheAligned(t, Aligner) + } +} diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index dcd4c8dfdf549..2dd3aebe6108e 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -297,6 +297,8 @@ mod sync; mod mpsc_queue; mod spsc_queue; +mod cache_aligned; + /// The receiving half of Rust's [`channel`][] (or [`sync_channel`]) type. /// This half can only be owned by one thread. /// @@ -919,7 +921,7 @@ impl Drop for Sender { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Sender {{ .. }}") + f.debug_struct("Sender").finish() } } @@ -1049,7 +1051,7 @@ impl Drop for SyncSender { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for SyncSender { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "SyncSender {{ .. }}") + f.debug_struct("SyncSender").finish() } } @@ -1295,11 +1297,72 @@ impl Receiver { Err(TryRecvError::Disconnected) => Err(RecvTimeoutError::Disconnected), Err(TryRecvError::Empty) - => self.recv_max_until(Instant::now() + timeout) + => self.recv_deadline(Instant::now() + timeout) } } - fn recv_max_until(&self, deadline: Instant) -> Result { + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if `deadline` is reached. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`][] (or [`SyncSender`]), then this + /// receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// [`Sender`]: struct.Sender.html + /// [`SyncSender`]: struct.SyncSender.html + /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// Successfully receiving value before reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Err(mpsc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "deadline_api", issue = "46316")] + pub fn recv_deadline(&self, deadline: Instant) -> Result { use self::RecvTimeoutError::*; loop { @@ -1551,7 +1614,7 @@ impl Drop for Receiver { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Receiver {{ .. }}") + f.debug_struct("Receiver").finish() } } @@ -1623,6 +1686,15 @@ impl error::Error for TrySendError { } } +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From> for TrySendError { + fn from(err: SendError) -> TrySendError { + match err { + SendError(t) => TrySendError::Disconnected(t), + } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for RecvError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1675,6 +1747,15 @@ impl error::Error for TryRecvError { } } +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for TryRecvError { + fn from(err: RecvError) -> TryRecvError { + match err { + RecvError => TryRecvError::Disconnected, + } + } +} + #[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] impl fmt::Display for RecvTimeoutError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1707,6 +1788,15 @@ impl error::Error for RecvTimeoutError { } } +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for RecvTimeoutError { + fn from(err: RecvError) -> RecvTimeoutError { + match err { + RecvError => RecvTimeoutError::Disconnected, + } + } +} + #[cfg(all(test, not(target_os = "emscripten")))] mod tests { use env; @@ -3009,22 +3099,4 @@ mod sync_tests { repro() } } - - #[test] - fn fmt_debug_sender() { - let (tx, _) = channel::(); - assert_eq!(format!("{:?}", tx), "Sender { .. }"); - } - - #[test] - fn fmt_debug_recv() { - let (_, rx) = channel::(); - assert_eq!(format!("{:?}", rx), "Receiver { .. }"); - } - - #[test] - fn fmt_debug_sync_sender() { - let (tx, _) = sync_channel::(1); - assert_eq!(format!("{:?}", tx), "SyncSender { .. }"); - } } diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs index e49f4cff02403..a9f3cea243f36 100644 --- a/src/libstd/sync/mpsc/select.rs +++ b/src/libstd/sync/mpsc/select.rs @@ -354,13 +354,13 @@ impl Iterator for Packets { impl fmt::Debug for Select { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Select {{ .. }}") + f.debug_struct("Select").finish() } } impl<'rx, T:Send+'rx> fmt::Debug for Handle<'rx, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Handle {{ .. }}") + f.debug_struct("Handle").finish() } } @@ -774,18 +774,4 @@ mod tests { } } } - - #[test] - fn fmt_debug_select() { - let sel = Select::new(); - assert_eq!(format!("{:?}", sel), "Select { .. }"); - } - - #[test] - fn fmt_debug_handle() { - let (_, rx) = channel::(); - let sel = Select::new(); - let handle = sel.handle(&rx); - assert_eq!(format!("{:?}", handle), "Handle { .. }"); - } } diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs index 1148bc66fbabb..cc4be92276a3b 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/src/libstd/sync/mpsc/spsc_queue.rs @@ -22,12 +22,15 @@ use core::cell::UnsafeCell; use sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use super::cache_aligned::CacheAligned; + // Node within the linked list queue of messages to send struct Node { // FIXME: this could be an uninitialized T if we're careful enough, and // that would reduce memory usage (and be a bit faster). // is it worth it? value: Option, // nullable for re-use of nodes + cached: bool, // This node goes into the node cache next: AtomicPtr>, // next node in the queue } @@ -35,38 +38,55 @@ struct Node { /// but it can be safely shared in an Arc if it is guaranteed that there /// is only one popper and one pusher touching the queue at any one point in /// time. -pub struct Queue { +pub struct Queue { // consumer fields + consumer: CacheAligned>, + + // producer fields + producer: CacheAligned>, +} + +struct Consumer { tail: UnsafeCell<*mut Node>, // where to pop from tail_prev: AtomicPtr>, // where to pop from + cache_bound: usize, // maximum cache size + cached_nodes: AtomicUsize, // number of nodes marked as cachable + addition: Addition, +} - // producer fields +struct Producer { head: UnsafeCell<*mut Node>, // where to push to first: UnsafeCell<*mut Node>, // where to get new nodes from tail_copy: UnsafeCell<*mut Node>, // between first/tail - - // Cache maintenance fields. Additions and subtractions are stored - // separately in order to allow them to use nonatomic addition/subtraction. - cache_bound: usize, - cache_additions: AtomicUsize, - cache_subtractions: AtomicUsize, + addition: Addition, } -unsafe impl Send for Queue { } +unsafe impl Send for Queue { } -unsafe impl Sync for Queue { } +unsafe impl Sync for Queue { } impl Node { fn new() -> *mut Node { Box::into_raw(box Node { value: None, + cached: false, next: AtomicPtr::new(ptr::null_mut::>()), }) } } -impl Queue { - /// Creates a new queue. +impl Queue { + + /// Creates a new queue. With given additional elements in the producer and + /// consumer portions of the queue. + /// + /// Due to the performance implications of cache-contention, + /// we wish to keep fields used mainly by the producer on a separate cache + /// line than those used by the consumer. + /// Since cache lines are usually 64 bytes, it is unreasonably expensive to + /// allocate one for small fields, so we allow users to insert additional + /// fields into the cache lines already allocated by this for the producer + /// and consumer. /// /// This is unsafe as the type system doesn't enforce a single /// consumer-producer relationship. It also allows the consumer to `pop` @@ -83,19 +103,28 @@ impl Queue { /// cache (if desired). If the value is 0, then the cache has /// no bound. Otherwise, the cache will never grow larger than /// `bound` (although the queue itself could be much larger. - pub unsafe fn new(bound: usize) -> Queue { + pub unsafe fn with_additions( + bound: usize, + producer_addition: ProducerAddition, + consumer_addition: ConsumerAddition, + ) -> Self { let n1 = Node::new(); let n2 = Node::new(); (*n1).next.store(n2, Ordering::Relaxed); Queue { - tail: UnsafeCell::new(n2), - tail_prev: AtomicPtr::new(n1), - head: UnsafeCell::new(n2), - first: UnsafeCell::new(n1), - tail_copy: UnsafeCell::new(n1), - cache_bound: bound, - cache_additions: AtomicUsize::new(0), - cache_subtractions: AtomicUsize::new(0), + consumer: CacheAligned::new(Consumer { + tail: UnsafeCell::new(n2), + tail_prev: AtomicPtr::new(n1), + cache_bound: bound, + cached_nodes: AtomicUsize::new(0), + addition: consumer_addition + }), + producer: CacheAligned::new(Producer { + head: UnsafeCell::new(n2), + first: UnsafeCell::new(n1), + tail_copy: UnsafeCell::new(n1), + addition: producer_addition + }), } } @@ -109,35 +138,25 @@ impl Queue { assert!((*n).value.is_none()); (*n).value = Some(t); (*n).next.store(ptr::null_mut(), Ordering::Relaxed); - (**self.head.get()).next.store(n, Ordering::Release); - *self.head.get() = n; + (**self.producer.head.get()).next.store(n, Ordering::Release); + *(&self.producer.head).get() = n; } } unsafe fn alloc(&self) -> *mut Node { // First try to see if we can consume the 'first' node for our uses. - // We try to avoid as many atomic instructions as possible here, so - // the addition to cache_subtractions is not atomic (plus we're the - // only one subtracting from the cache). - if *self.first.get() != *self.tail_copy.get() { - if self.cache_bound > 0 { - let b = self.cache_subtractions.load(Ordering::Relaxed); - self.cache_subtractions.store(b + 1, Ordering::Relaxed); - } - let ret = *self.first.get(); - *self.first.get() = (*ret).next.load(Ordering::Relaxed); + if *self.producer.first.get() != *self.producer.tail_copy.get() { + let ret = *self.producer.first.get(); + *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed); return ret; } // If the above fails, then update our copy of the tail and try // again. - *self.tail_copy.get() = self.tail_prev.load(Ordering::Acquire); - if *self.first.get() != *self.tail_copy.get() { - if self.cache_bound > 0 { - let b = self.cache_subtractions.load(Ordering::Relaxed); - self.cache_subtractions.store(b + 1, Ordering::Relaxed); - } - let ret = *self.first.get(); - *self.first.get() = (*ret).next.load(Ordering::Relaxed); + *self.producer.0.tail_copy.get() = + self.consumer.tail_prev.load(Ordering::Acquire); + if *self.producer.first.get() != *self.producer.tail_copy.get() { + let ret = *self.producer.first.get(); + *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed); return ret; } // If all of that fails, then we have to allocate a new node @@ -153,27 +172,27 @@ impl Queue { // sentinel from where we should start popping from. Hence, look at // tail's next field and see if we can use it. If we do a pop, then // the current tail node is a candidate for going into the cache. - let tail = *self.tail.get(); + let tail = *self.consumer.tail.get(); let next = (*tail).next.load(Ordering::Acquire); if next.is_null() { return None } assert!((*next).value.is_some()); let ret = (*next).value.take(); - *self.tail.get() = next; - if self.cache_bound == 0 { - self.tail_prev.store(tail, Ordering::Release); + *self.consumer.0.tail.get() = next; + if self.consumer.cache_bound == 0 { + self.consumer.tail_prev.store(tail, Ordering::Release); } else { - // FIXME: this is dubious with overflow. - let additions = self.cache_additions.load(Ordering::Relaxed); - let subtractions = self.cache_subtractions.load(Ordering::Relaxed); - let size = additions - subtractions; - - if size < self.cache_bound { - self.tail_prev.store(tail, Ordering::Release); - self.cache_additions.store(additions + 1, Ordering::Relaxed); + let cached_nodes = self.consumer.cached_nodes.load(Ordering::Relaxed); + if cached_nodes < self.consumer.cache_bound && !(*tail).cached { + self.consumer.cached_nodes.store(cached_nodes, Ordering::Relaxed); + (*tail).cached = true; + } + + if (*tail).cached { + self.consumer.tail_prev.store(tail, Ordering::Release); } else { - (*self.tail_prev.load(Ordering::Relaxed)) - .next.store(next, Ordering::Relaxed); + (*self.consumer.tail_prev.load(Ordering::Relaxed)) + .next.store(next, Ordering::Relaxed); // We have successfully erased all references to 'tail', so // now we can safely drop it. let _: Box> = Box::from_raw(tail); @@ -194,17 +213,25 @@ impl Queue { // This is essentially the same as above with all the popping bits // stripped out. unsafe { - let tail = *self.tail.get(); + let tail = *self.consumer.tail.get(); let next = (*tail).next.load(Ordering::Acquire); if next.is_null() { None } else { (*next).value.as_mut() } } } + + pub fn producer_addition(&self) -> &ProducerAddition { + &self.producer.addition + } + + pub fn consumer_addition(&self) -> &ConsumerAddition { + &self.consumer.addition + } } -impl Drop for Queue { +impl Drop for Queue { fn drop(&mut self) { unsafe { - let mut cur = *self.first.get(); + let mut cur = *self.producer.first.get(); while !cur.is_null() { let next = (*cur).next.load(Ordering::Relaxed); let _n: Box> = Box::from_raw(cur); @@ -224,7 +251,7 @@ mod tests { #[test] fn smoke() { unsafe { - let queue = Queue::new(0); + let queue = Queue::with_additions(0, (), ()); queue.push(1); queue.push(2); assert_eq!(queue.pop(), Some(1)); @@ -241,7 +268,7 @@ mod tests { #[test] fn peek() { unsafe { - let queue = Queue::new(0); + let queue = Queue::with_additions(0, (), ()); queue.push(vec![1]); // Ensure the borrowchecker works @@ -264,7 +291,7 @@ mod tests { #[test] fn drop_full() { unsafe { - let q: Queue> = Queue::new(0); + let q: Queue> = Queue::with_additions(0, (), ()); q.push(box 1); q.push(box 2); } @@ -273,7 +300,7 @@ mod tests { #[test] fn smoke_bound() { unsafe { - let q = Queue::new(0); + let q = Queue::with_additions(0, (), ()); q.push(1); q.push(2); assert_eq!(q.pop(), Some(1)); @@ -295,7 +322,7 @@ mod tests { } unsafe fn stress_bound(bound: usize) { - let q = Arc::new(Queue::new(bound)); + let q = Arc::new(Queue::with_additions(bound, (), ())); let (tx, rx) = channel(); let q2 = q.clone(); diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index 47cd8977fda23..d1515eba68c3e 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -41,15 +41,22 @@ const MAX_STEALS: isize = 5; const MAX_STEALS: isize = 1 << 20; pub struct Packet { - queue: spsc::Queue>, // internal queue for all message + // internal queue for all messages + queue: spsc::Queue, ProducerAddition, ConsumerAddition>, +} +struct ProducerAddition { cnt: AtomicIsize, // How many items are on this channel - steals: UnsafeCell, // How many times has a port received without blocking? to_wake: AtomicUsize, // SignalToken for the blocked thread to wake up port_dropped: AtomicBool, // flag if the channel has been destroyed. } +struct ConsumerAddition { + steals: UnsafeCell, // How many times has a port received without blocking? +} + + pub enum Failure { Empty, Disconnected, @@ -78,13 +85,18 @@ enum Message { impl Packet { pub fn new() -> Packet { Packet { - queue: unsafe { spsc::Queue::new(128) }, - - cnt: AtomicIsize::new(0), - steals: UnsafeCell::new(0), - to_wake: AtomicUsize::new(0), - - port_dropped: AtomicBool::new(false), + queue: unsafe { spsc::Queue::with_additions( + 128, + ProducerAddition { + cnt: AtomicIsize::new(0), + to_wake: AtomicUsize::new(0), + + port_dropped: AtomicBool::new(false), + }, + ConsumerAddition { + steals: UnsafeCell::new(0), + } + )}, } } @@ -92,7 +104,7 @@ impl Packet { // If the other port has deterministically gone away, then definitely // must return the data back up the stack. Otherwise, the data is // considered as being sent. - if self.port_dropped.load(Ordering::SeqCst) { return Err(t) } + if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) { return Err(t) } match self.do_send(Data(t)) { UpSuccess | UpDisconnected => {}, @@ -104,14 +116,16 @@ impl Packet { pub fn upgrade(&self, up: Receiver) -> UpgradeResult { // If the port has gone away, then there's no need to proceed any // further. - if self.port_dropped.load(Ordering::SeqCst) { return UpDisconnected } + if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) { + return UpDisconnected + } self.do_send(GoUp(up)) } fn do_send(&self, t: Message) -> UpgradeResult { self.queue.push(t); - match self.cnt.fetch_add(1, Ordering::SeqCst) { + match self.queue.producer_addition().cnt.fetch_add(1, Ordering::SeqCst) { // As described in the mod's doc comment, -1 == wakeup -1 => UpWoke(self.take_to_wake()), // As as described before, SPSC queues must be >= -2 @@ -125,7 +139,7 @@ impl Packet { // will never remove this data. We can only have at most one item to // drain (the port drains the rest). DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); + self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); let first = self.queue.pop(); let second = self.queue.pop(); assert!(second.is_none()); @@ -144,8 +158,8 @@ impl Packet { // Consumes ownership of the 'to_wake' field. fn take_to_wake(&self) -> SignalToken { - let ptr = self.to_wake.load(Ordering::SeqCst); - self.to_wake.store(0, Ordering::SeqCst); + let ptr = self.queue.producer_addition().to_wake.load(Ordering::SeqCst); + self.queue.producer_addition().to_wake.store(0, Ordering::SeqCst); assert!(ptr != 0); unsafe { SignalToken::cast_from_usize(ptr) } } @@ -154,14 +168,16 @@ impl Packet { // back if it shouldn't sleep. Note that this is the location where we take // steals into account. fn decrement(&self, token: SignalToken) -> Result<(), SignalToken> { - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), 0); let ptr = unsafe { token.cast_to_usize() }; - self.to_wake.store(ptr, Ordering::SeqCst); + self.queue.producer_addition().to_wake.store(ptr, Ordering::SeqCst); - let steals = unsafe { ptr::replace(self.steals.get(), 0) }; + let steals = unsafe { ptr::replace(self.queue.consumer_addition().steals.get(), 0) }; - match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) { - DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } + match self.queue.producer_addition().cnt.fetch_sub(1 + steals, Ordering::SeqCst) { + DISCONNECTED => { + self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); + } // If we factor in our steals and notice that the channel has no // data, we successfully sleep n => { @@ -170,7 +186,7 @@ impl Packet { } } - self.to_wake.store(0, Ordering::SeqCst); + self.queue.producer_addition().to_wake.store(0, Ordering::SeqCst); Err(unsafe { SignalToken::cast_from_usize(ptr) }) } @@ -201,7 +217,7 @@ impl Packet { // "steal" factored into the channel count above). data @ Ok(..) | data @ Err(Upgraded(..)) => unsafe { - *self.steals.get() -= 1; + *self.queue.consumer_addition().steals.get() -= 1; data }, @@ -223,20 +239,21 @@ impl Packet { // down as much as possible (without going negative), and then // adding back in whatever we couldn't factor into steals. Some(data) => unsafe { - if *self.steals.get() > MAX_STEALS { - match self.cnt.swap(0, Ordering::SeqCst) { + if *self.queue.consumer_addition().steals.get() > MAX_STEALS { + match self.queue.producer_addition().cnt.swap(0, Ordering::SeqCst) { DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); + self.queue.producer_addition().cnt.store( + DISCONNECTED, Ordering::SeqCst); } n => { - let m = cmp::min(n, *self.steals.get()); - *self.steals.get() -= m; + let m = cmp::min(n, *self.queue.consumer_addition().steals.get()); + *self.queue.consumer_addition().steals.get() -= m; self.bump(n - m); } } - assert!(*self.steals.get() >= 0); + assert!(*self.queue.consumer_addition().steals.get() >= 0); } - *self.steals.get() += 1; + *self.queue.consumer_addition().steals.get() += 1; match data { Data(t) => Ok(t), GoUp(up) => Err(Upgraded(up)), @@ -244,7 +261,7 @@ impl Packet { }, None => { - match self.cnt.load(Ordering::SeqCst) { + match self.queue.producer_addition().cnt.load(Ordering::SeqCst) { n if n != DISCONNECTED => Err(Empty), // This is a little bit of a tricky case. We failed to pop @@ -273,7 +290,7 @@ impl Packet { pub fn drop_chan(&self) { // Dropping a channel is pretty simple, we just flag it as disconnected // and then wakeup a blocker if there is one. - match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) { + match self.queue.producer_addition().cnt.swap(DISCONNECTED, Ordering::SeqCst) { -1 => { self.take_to_wake().signal(); } DISCONNECTED => {} n => { assert!(n >= 0); } @@ -300,7 +317,7 @@ impl Packet { // sends are gated on this flag, so we're immediately guaranteed that // there are a bounded number of active sends that we'll have to deal // with. - self.port_dropped.store(true, Ordering::SeqCst); + self.queue.producer_addition().port_dropped.store(true, Ordering::SeqCst); // Now that we're guaranteed to deal with a bounded number of senders, // we need to drain the queue. This draining process happens atomically @@ -310,9 +327,9 @@ impl Packet { // continue to fail while active senders send data while we're dropping // data, but eventually we're guaranteed to break out of this loop // (because there is a bounded number of senders). - let mut steals = unsafe { *self.steals.get() }; + let mut steals = unsafe { *self.queue.consumer_addition().steals.get() }; while { - let cnt = self.cnt.compare_and_swap( + let cnt = self.queue.producer_addition().cnt.compare_and_swap( steals, DISCONNECTED, Ordering::SeqCst); cnt != DISCONNECTED && cnt != steals } { @@ -353,9 +370,9 @@ impl Packet { // increment the count on the channel (used for selection) fn bump(&self, amt: isize) -> isize { - match self.cnt.fetch_add(amt, Ordering::SeqCst) { + match self.queue.producer_addition().cnt.fetch_add(amt, Ordering::SeqCst) { DISCONNECTED => { - self.cnt.store(DISCONNECTED, Ordering::SeqCst); + self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst); DISCONNECTED } n => n @@ -404,8 +421,8 @@ impl Packet { // this end. This is fine because we know it's a small bounded windows // of time until the data is actually sent. if was_upgrade { - assert_eq!(unsafe { *self.steals.get() }, 0); - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + assert_eq!(unsafe { *self.queue.consumer_addition().steals.get() }, 0); + assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), 0); return Ok(true) } @@ -418,7 +435,7 @@ impl Packet { // If we were previously disconnected, then we know for sure that there // is no thread in to_wake, so just keep going let has_data = if prev == DISCONNECTED { - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), 0); true // there is data, that data is that we're disconnected } else { let cur = prev + steals + 1; @@ -441,13 +458,13 @@ impl Packet { if prev < 0 { drop(self.take_to_wake()); } else { - while self.to_wake.load(Ordering::SeqCst) != 0 { + while self.queue.producer_addition().to_wake.load(Ordering::SeqCst) != 0 { thread::yield_now(); } } unsafe { - assert_eq!(*self.steals.get(), 0); - *self.steals.get() = steals; + assert_eq!(*self.queue.consumer_addition().steals.get(), 0); + *self.queue.consumer_addition().steals.get() = steals; } // if we were previously positive, then there's surely data to @@ -481,7 +498,7 @@ impl Drop for Packet { // disconnection, but also a proper fence before the read of // `to_wake`, so this assert cannot be removed with also removing // the `to_wake` assert. - assert_eq!(self.cnt.load(Ordering::SeqCst), DISCONNECTED); - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + assert_eq!(self.queue.producer_addition().cnt.load(Ordering::SeqCst), DISCONNECTED); + assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), 0); } } diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 62d8de18f4b45..81f5594bc5231 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -19,10 +19,10 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// A mutual exclusion primitive useful for protecting shared data /// /// This mutex will block threads waiting for the lock to become available. The -/// mutex can also be statically initialized or created via a `new` +/// mutex can also be statically initialized or created via a [`new`] /// constructor. Each mutex has a type parameter which represents the data that /// it is protecting. The data can only be accessed through the RAII guards -/// returned from `lock` and `try_lock`, which guarantees that the data is only +/// returned from [`lock`] and [`try_lock`], which guarantees that the data is only /// ever accessed when the mutex is locked. /// /// # Poisoning @@ -33,16 +33,24 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// data by default as it is likely tainted (some invariant is not being /// upheld). /// -/// For a mutex, this means that the `lock` and `try_lock` methods return a -/// `Result` which indicates whether a mutex has been poisoned or not. Most -/// usage of a mutex will simply `unwrap()` these results, propagating panics +/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a +/// [`Result`] which indicates whether a mutex has been poisoned or not. Most +/// usage of a mutex will simply [`unwrap()`] these results, propagating panics /// among threads to ensure that a possibly invalid invariant is not witnessed. /// /// A poisoned mutex, however, does not prevent all access to the underlying -/// data. The `PoisonError` type has an `into_inner` method which will return +/// data. The [`PoisonError`] type has an [`into_inner`] method which will return /// the guard that would have otherwise been returned on a successful lock. This /// allows access to the data, despite the lock being poisoned. /// +/// [`new`]: #method.new +/// [`lock`]: #method.lock +/// [`try_lock`]: #method.try_lock +/// [`Result`]: ../../std/result/enum.Result.html +/// [`unwrap()`]: ../../std/result/enum.Result.html#method.unwrap +/// [`PoisonError`]: ../../std/sync/struct.PoisonError.html +/// [`into_inner`]: ../../std/sync/struct.PoisonError.html#method.into_inner +/// /// # Examples /// /// ``` @@ -226,7 +234,7 @@ impl Mutex { /// Attempts to acquire this lock. /// - /// If the lock could not be acquired at this time, then `Err` is returned. + /// If the lock could not be acquired at this time, then [`Err`] is returned. /// Otherwise, an RAII guard is returned. The lock will be unlocked when the /// guard is dropped. /// @@ -238,6 +246,8 @@ impl Mutex { /// this call will return failure if the mutex would otherwise be /// acquired. /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// /// # Examples /// /// ``` @@ -372,6 +382,17 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex { } } +#[stable(feature = "mutex_from", since = "1.22.0")] +impl From for Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// This is equivalent to [`Mutex::new`]. + /// + /// [`Mutex::new`]: #method.new + fn from(t: T) -> Self { + Mutex::new(t) + } +} + #[stable(feature = "mutex_default", since = "1.10.0")] impl Default for Mutex { /// Creates a `Mutex`, with the `Default` value for T. @@ -384,11 +405,18 @@ impl Default for Mutex { impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_lock() { - Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard), + Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), Err(TryLockError::Poisoned(err)) => { - write!(f, "Mutex {{ data: Poisoned({:?}) }}", &**err.get_ref()) + f.debug_struct("Mutex").field("data", &&**err.get_ref()).finish() }, - Err(TryLockError::WouldBlock) => write!(f, "Mutex {{ }}") + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("") } + } + + f.debug_struct("Mutex").field("data", &LockedPlaceholder).finish() + } } } } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 015106fc2e598..6fd8b6a5bbae4 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -103,8 +103,8 @@ unsafe impl Sync for Once {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Once {} -/// State yielded to the [`call_once_force`] method which can be used to query -/// whether the [`Once`] was previously poisoned or not. +/// State yielded to [`call_once_force`]’s closure parameter. The state can be +/// used to query the poison status of the [`Once`]. /// /// [`call_once_force`]: struct.Once.html#method.call_once_force /// [`Once`]: struct.Once.html @@ -156,7 +156,6 @@ struct Finish { impl Once { /// Creates a new `Once` value. #[stable(feature = "once_new", since = "1.2.0")] - #[cfg_attr(not(stage0), rustc_const_unstable(feature = "const_once_new"))] pub const fn new() -> Once { Once { state: AtomicUsize::new(INCOMPLETE), @@ -230,17 +229,50 @@ impl Once { /// Performs the same function as [`call_once`] except ignores poisoning. /// + /// Unlike [`call_once`], if this `Once` has been poisoned (i.e. a previous + /// call to `call_once` or `call_once_force` caused a panic), calling + /// `call_once_force` will still invoke the closure `f` and will _not_ + /// result in an immediate panic. If `f` panics, the `Once` will remain + /// in a poison state. If `f` does _not_ panic, the `Once` will no + /// longer be in a poison state and all future calls to `call_once` or + /// `call_one_force` will no-op. + /// + /// The closure `f` is yielded a [`OnceState`] structure which can be used + /// to query the poison status of the `Once`. + /// /// [`call_once`]: struct.Once.html#method.call_once + /// [`OnceState`]: struct.OnceState.html /// - /// If this `Once` has been poisoned (some initialization panicked) then - /// this function will continue to attempt to call initialization functions - /// until one of them doesn't panic. + /// # Examples /// - /// The closure `f` is yielded a [`OnceState`] structure which can be used to query the - /// state of this `Once` (whether initialization has previously panicked or - /// not). + /// ``` + /// #![feature(once_poison)] /// - /// [`OnceState`]: struct.OnceState.html + /// use std::sync::{Once, ONCE_INIT}; + /// use std::thread; + /// + /// static INIT: Once = ONCE_INIT; + /// + /// // poison the once + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| panic!()); + /// }); + /// assert!(handle.join().is_err()); + /// + /// // poisoning propagates + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| {}); + /// }); + /// assert!(handle.join().is_err()); + /// + /// // call_once_force will still run and reset the poisoned state + /// INIT.call_once_force(|state| { + /// assert!(state.poisoned()); + /// }); + /// + /// // once any success happens, we stop propagating the poison + /// INIT.call_once(|| {}); + /// ``` #[unstable(feature = "once_poison", issue = "33577")] pub fn call_once_force(&'static self, f: F) where F: FnOnce(&OnceState) { // same as above, just with a different parameter to `call_inner`. @@ -386,12 +418,47 @@ impl Drop for Finish { } impl OnceState { - /// Returns whether the associated [`Once`] has been poisoned. - /// - /// Once an initialization routine for a [`Once`] has panicked it will forever - /// indicate to future forced initialization routines that it is poisoned. + /// Returns whether the associated [`Once`] was poisoned prior to the + /// invocation of the closure passed to [`call_once_force`]. /// + /// [`call_once_force`]: struct.Once.html#method.call_once_force /// [`Once`]: struct.Once.html + /// + /// # Examples + /// + /// A poisoned `Once`: + /// + /// ``` + /// #![feature(once_poison)] + /// + /// use std::sync::{Once, ONCE_INIT}; + /// use std::thread; + /// + /// static INIT: Once = ONCE_INIT; + /// + /// // poison the once + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| panic!()); + /// }); + /// assert!(handle.join().is_err()); + /// + /// INIT.call_once_force(|state| { + /// assert!(state.poisoned()); + /// }); + /// ``` + /// + /// An unpoisoned `Once`: + /// + /// ``` + /// #![feature(once_poison)] + /// + /// use std::sync::{Once, ONCE_INIT}; + /// + /// static INIT: Once = ONCE_INIT; + /// + /// INIT.call_once_force(|state| { + /// assert!(!state.poisoned()); + /// }); #[unstable(feature = "once_poison", issue = "33577")] pub fn poisoned(&self) -> bool { self.poisoned diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 4757faabfb873..fd6cff6b69c40 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -10,7 +10,6 @@ use cell::UnsafeCell; use fmt; -use marker; use mem; use ops::{Deref, DerefMut}; use ptr; @@ -82,7 +81,7 @@ pub struct RwLock { } #[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for RwLock {} +unsafe impl Send for RwLock {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for RwLock {} @@ -102,7 +101,10 @@ pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {} +impl<'a, T: ?Sized> !Send for RwLockReadGuard<'a, T> {} + +#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] +unsafe impl<'a, T: ?Sized + Sync> Sync for RwLockReadGuard<'a, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped. @@ -121,7 +123,10 @@ pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: ?Sized> !marker::Send for RwLockWriteGuard<'a, T> {} +impl<'a, T: ?Sized> !Send for RwLockWriteGuard<'a, T> {} + +#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] +unsafe impl<'a, T: ?Sized + Sync> Sync for RwLockWriteGuard<'a, T> {} impl RwLock { /// Creates a new instance of an `RwLock` which is unlocked. @@ -428,11 +433,18 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock { impl fmt::Debug for RwLock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_read() { - Ok(guard) => write!(f, "RwLock {{ data: {:?} }}", &*guard), + Ok(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), Err(TryLockError::Poisoned(err)) => { - write!(f, "RwLock {{ data: Poisoned({:?}) }}", &**err.get_ref()) + f.debug_struct("RwLock").field("data", &&**err.get_ref()).finish() }, - Err(TryLockError::WouldBlock) => write!(f, "RwLock {{ }}") + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("") } + } + + f.debug_struct("RwLock").field("data", &LockedPlaceholder).finish() + } } } } @@ -445,6 +457,17 @@ impl Default for RwLock { } } +#[stable(feature = "rw_lock_from", since = "1.22.0")] +impl From for RwLock { + /// Creates a new instance of an `RwLock` which is unlocked. + /// This is equivalent to [`RwLock::new`]. + /// + /// [`RwLock::new`]: #method.new + fn from(t: T) -> Self { + RwLock::new(t) + } +} + impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { @@ -542,8 +565,6 @@ impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { #[cfg(all(test, not(target_os = "emscripten")))] mod tests { - #![allow(deprecated)] // rand - use rand::{self, Rng}; use sync::mpsc::channel; use thread; @@ -564,7 +585,7 @@ mod tests { #[test] fn frob() { - const N: usize = 10; + const N: u32 = 10; const M: usize = 1000; let r = Arc::new(RwLock::new(())); diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index d91c2073a23a8..be8cb88416bb6 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -32,46 +32,66 @@ #![allow(missing_debug_implementations)] -pub use self::imp::*; - -#[cfg(unix)] -#[path = "unix/mod.rs"] -mod imp; - -#[cfg(windows)] -#[path = "windows/mod.rs"] -mod imp; - -#[cfg(target_os = "redox")] -#[path = "redox/mod.rs"] -mod imp; - - -// Import essential modules from both platforms when documenting. - -#[cfg(all(dox, not(unix)))] -use os::linux as platform; - -#[cfg(all(dox, not(any(unix, target_os = "redox"))))] -#[path = "unix/ext/mod.rs"] -pub mod unix_ext; - -#[cfg(all(dox, any(unix, target_os = "redox")))] -pub use self::ext as unix_ext; - - -#[cfg(all(dox, not(windows)))] -#[macro_use] -#[path = "windows/compat.rs"] -mod compat; - -#[cfg(all(dox, not(windows)))] -#[path = "windows/c.rs"] -mod c; - -#[cfg(all(dox, not(windows)))] -#[path = "windows/ext/mod.rs"] -pub mod windows_ext; - -#[cfg(all(dox, windows))] -pub use self::ext as windows_ext; +cfg_if! { + if #[cfg(unix)] { + mod unix; + pub use self::unix::*; + } else if #[cfg(windows)] { + mod windows; + pub use self::windows::*; + } else if #[cfg(target_os = "redox")] { + mod redox; + pub use self::redox::*; + } else if #[cfg(target_arch = "wasm32")] { + mod wasm; + pub use self::wasm::*; + } else { + compile_error!("libstd doesn't compile for this platform yet"); + } +} + +// Import essential modules from both platforms when documenting. These are +// then later used in the `std::os` module when documenting, for example, +// Windows when we're compiling for Linux. + +#[cfg(dox)] +cfg_if! { + if #[cfg(any(unix, target_os = "redox"))] { + // On unix we'll document what's already available + pub use self::ext as unix_ext; + } else if #[cfg(target_arch = "wasm32")] { + // On wasm right now the module below doesn't compile (missing things + // in `libc` which is empty) so just omit everything with an empty module + #[unstable(issue = "0", feature = "std_internals")] + pub mod unix_ext {} + } else { + // On other platforms like Windows document the bare bones of unix + use os::linux as platform; + #[path = "unix/ext/mod.rs"] + pub mod unix_ext; + } +} + +#[cfg(dox)] +cfg_if! { + if #[cfg(windows)] { + // On windows we'll just be documenting what's already available + pub use self::ext as windows_ext; + } else if #[cfg(target_arch = "wasm32")] { + // On wasm right now the shim below doesn't compile, so just omit it + #[unstable(issue = "0", feature = "std_internals")] + pub mod windows_ext {} + } else { + // On all other platforms (aka linux/osx/etc) then pull in a "minimal" + // amount of windows goop which ends up compiling + #[macro_use] + #[path = "windows/compat.rs"] + mod compat; + + #[path = "windows/c.rs"] + mod c; + + #[path = "windows/ext/mod.rs"] + pub mod windows_ext; + } +} diff --git a/src/libstd/sys/redox/backtrace/tracing.rs b/src/libstd/sys/redox/backtrace/tracing.rs index cfeabaddda985..0a174b3c3f586 100644 --- a/src/libstd/sys/redox/backtrace/tracing.rs +++ b/src/libstd/sys/redox/backtrace/tracing.rs @@ -96,8 +96,8 @@ extern fn trace_fn(ctx: *mut uw::_Unwind_Context, if cx.idx < cx.frames.len() { cx.frames[cx.idx] = Frame { - symbol_addr: symaddr, - exact_position: ip, + symbol_addr: symaddr as *mut u8, + exact_position: ip as *mut u8, }; cx.idx += 1; } diff --git a/src/libstd/sys/redox/cmath.rs b/src/libstd/sys/redox/cmath.rs new file mode 100644 index 0000000000000..2bc96651b0c84 --- /dev/null +++ b/src/libstd/sys/redox/cmath.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +use libc::{c_float, c_double}; + +#[link_name = "m"] +extern { + pub fn acos(n: c_double) -> c_double; + pub fn acosf(n: c_float) -> c_float; + pub fn asin(n: c_double) -> c_double; + pub fn asinf(n: c_float) -> c_float; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn coshf(n: c_float) -> c_float; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + pub fn hypot(x: c_double, y: c_double) -> c_double; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn sinhf(n: c_float) -> c_float; + pub fn tan(n: c_double) -> c_double; + pub fn tanf(n: c_float) -> c_float; + pub fn tanh(n: c_double) -> c_double; + pub fn tanhf(n: c_float) -> c_float; +} diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs index fe4a89c6f3ebb..2a611ed7dabbe 100644 --- a/src/libstd/sys/redox/condvar.rs +++ b/src/libstd/sys/redox/condvar.rs @@ -9,12 +9,12 @@ // except according to those terms. use cell::UnsafeCell; -use intrinsics::{atomic_cxchg, atomic_xadd, atomic_xchg}; +use intrinsics::{atomic_cxchg, atomic_load, atomic_xadd, atomic_xchg}; use ptr; use time::Duration; use sys::mutex::{mutex_unlock, Mutex}; -use sys::syscall::{futex, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; +use sys::syscall::{futex, TimeSpec, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; pub struct Condvar { lock: UnsafeCell<*mut i32>, @@ -63,33 +63,50 @@ impl Condvar { } #[inline] - pub fn wait(&self, mutex: &Mutex) { - unsafe { - let lock = self.lock.get(); - let seq = self.seq.get(); - - if *lock != mutex.lock.get() { - if *lock != ptr::null_mut() { - panic!("Condvar used with more than one Mutex"); - } + unsafe fn wait_inner(&self, mutex: &Mutex, timeout_ptr: *const TimeSpec) -> bool { + let lock = self.lock.get(); + let seq = self.seq.get(); - atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); + if *lock != mutex.lock.get() { + if *lock != ptr::null_mut() { + panic!("Condvar used with more than one Mutex"); } - mutex_unlock(*lock); + atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); + } - let _ = futex(seq, FUTEX_WAIT, *seq, 0, ptr::null_mut()); + mutex_unlock(*lock); - while atomic_xchg(*lock, 2) != 0 { - let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); - } + let seq_before = atomic_load(seq); + + let _ = futex(seq, FUTEX_WAIT, seq_before, timeout_ptr as usize, ptr::null_mut()); + + let seq_after = atomic_load(seq); + + while atomic_xchg(*lock, 2) != 0 { + let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); + } + + seq_before != seq_after + } + + #[inline] + pub fn wait(&self, mutex: &Mutex) { + unsafe { + assert!(self.wait_inner(mutex, ptr::null())); } } #[inline] - pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - ::sys_common::util::dumb_print(format_args!("condvar wait_timeout\n")); - unimplemented!(); + pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + unsafe { + let timeout = TimeSpec { + tv_sec: dur.as_secs() as i64, + tv_nsec: dur.subsec_nanos() as i32 + }; + + self.wait_inner(mutex, &timeout as *const TimeSpec) + } } #[inline] diff --git a/src/libstd/sys/redox/fast_thread_local.rs b/src/libstd/sys/redox/fast_thread_local.rs index 9f0eee024d56f..6a007e98827b6 100644 --- a/src/libstd/sys/redox/fast_thread_local.rs +++ b/src/libstd/sys/redox/fast_thread_local.rs @@ -81,7 +81,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { unsafe extern fn run_dtors(mut ptr: *mut u8) { while !ptr.is_null() { let list: Box = Box::from_raw(ptr as *mut List); - for &(ptr, dtor) in list.iter() { + for (ptr, dtor) in list.into_iter() { dtor(ptr); } ptr = DTORS.get(); diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs index 918893097f841..3483477d40cf6 100644 --- a/src/libstd/sys/redox/fs.rs +++ b/src/libstd/sys/redox/fs.rs @@ -437,8 +437,7 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { } pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - ::sys_common::util::dumb_print(format_args!("Link\n")); - unimplemented!(); + Err(Error::from_raw_os_error(syscall::ENOSYS)) } pub fn stat(p: &Path) -> io::Result { diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs index 7c728ebb1af2e..4352b72c30773 100644 --- a/src/libstd/sys/redox/mod.rs +++ b/src/libstd/sys/redox/mod.rs @@ -12,9 +12,13 @@ use io::{self, ErrorKind}; +pub use libc::strlen; +pub use self::rand::hashmap_random_keys; + pub mod args; #[cfg(feature = "backtrace")] pub mod backtrace; +pub mod cmath; pub mod condvar; pub mod env; pub mod ext; diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/redox/os.rs index efddd5f029484..480765b77a028 100644 --- a/src/libstd/sys/redox/os.rs +++ b/src/libstd/sys/redox/os.rs @@ -209,3 +209,11 @@ pub fn exit(code: i32) -> ! { let _ = syscall::exit(code as usize); unreachable!(); } + +pub fn getpid() -> u32 { + syscall::getpid().unwrap() as u32 +} + +pub fn getppid() -> u32 { + syscall::getppid().unwrap() as u32 +} diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs index c54286353a92f..5c40d42fa0a44 100644 --- a/src/libstd/sys/redox/os_str.rs +++ b/src/libstd/sys/redox/os_str.rs @@ -15,6 +15,8 @@ use borrow::Cow; use fmt; use str; use mem; +use rc::Rc; +use sync::Arc; use sys_common::{AsInner, IntoInner}; use std_unicode::lossy::Utf8Lossy; @@ -123,6 +125,16 @@ impl Buf { let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; Buf { inner: inner.into_vec() } } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } } impl Slice { @@ -156,4 +168,16 @@ impl Slice { let boxed: Box<[u8]> = Default::default(); unsafe { mem::transmute(boxed) } } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } } diff --git a/src/libstd/sys/redox/rand.rs b/src/libstd/sys/redox/rand.rs index eb28eca38bcd9..3b378f53429ed 100644 --- a/src/libstd/sys/redox/rand.rs +++ b/src/libstd/sys/redox/rand.rs @@ -8,50 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use io; -use rand::Rng; - -// FIXME: Use rand: -pub struct OsRng { - state: [u64; 2] -} - -impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - Ok(OsRng { - state: [0xBADF00D1, 0xDEADBEEF] - }) - } -} - -impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 - } - fn next_u64(&mut self) -> u64 { - // Store the first and second part. - let mut x = self.state[0]; - let y = self.state[1]; - - // Put the second part into the first slot. - self.state[0] = y; - // Twist the first slot. - x ^= x << 23; - // Update the second slot. - self.state[1] = x ^ y ^ (x >> 17) ^ (y >> 26); - - // Generate the final integer. - self.state[1].wrapping_add(y) - - } - fn fill_bytes(&mut self, buf: &mut [u8]) { - for chunk in buf.chunks_mut(8) { - let mut rand: u64 = self.next_u64(); - for b in chunk.iter_mut() { - *b = rand as u8; - rand = rand >> 8; - } - } - } +pub fn hashmap_random_keys() -> (u64, u64) { + (0, 0) } diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs index c839531cc26c6..3abb094ac34e3 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/redox/stdio.rs @@ -70,5 +70,8 @@ impl io::Write for Stderr { } } -pub const EBADF_ERR: i32 = ::sys::syscall::EBADF; +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(::sys::syscall::EBADF as i32) +} + pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/src/libstd/sys/unix/backtrace/printing/dladdr.rs b/src/libstd/sys/unix/backtrace/printing/dladdr.rs index 21f0b3724c130..bc56fd6594ea6 100644 --- a/src/libstd/sys/unix/backtrace/printing/dladdr.rs +++ b/src/libstd/sys/unix/backtrace/printing/dladdr.rs @@ -22,7 +22,7 @@ pub fn resolve_symname(frame: Frame, { unsafe { let mut info: Dl_info = intrinsics::init(); - let symname = if dladdr(frame.exact_position, &mut info) == 0 || + let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 || info.dli_sname.is_null() { None } else { @@ -41,6 +41,5 @@ struct Dl_info { } extern { - fn dladdr(addr: *const libc::c_void, - info: *mut Dl_info) -> libc::c_int; + fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int; } diff --git a/src/libstd/sys/unix/backtrace/printing/mod.rs b/src/libstd/sys/unix/backtrace/printing/mod.rs index 8bd2d9eccd82a..caa60712b1d58 100644 --- a/src/libstd/sys/unix/backtrace/printing/mod.rs +++ b/src/libstd/sys/unix/backtrace/printing/mod.rs @@ -20,7 +20,7 @@ pub use self::dladdr::resolve_symname; #[cfg(target_os = "emscripten")] pub fn foreach_symbol_fileline(_: Frame, _: F, _: &BacktraceContext) -> io::Result where - F: FnMut(&[u8], ::libc::c_int) -> io::Result<()> + F: FnMut(&[u8], u32) -> io::Result<()> { Ok(false) } diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs index ecd32aa9462a9..400d39cd4bdcb 100644 --- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs +++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs @@ -36,8 +36,8 @@ pub fn unwind_backtrace(frames: &mut [Frame]) } as usize; for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) { *to = Frame { - exact_position: *from, - symbol_addr: *from, + exact_position: *from as *mut u8, + symbol_addr: *from as *mut u8, }; } Ok((nb_frames as usize, BacktraceContext)) diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs index e3ffbe88acd45..000c08d2e0d19 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs @@ -96,8 +96,8 @@ extern fn trace_fn(ctx: *mut uw::_Unwind_Context, if cx.idx < cx.frames.len() { cx.frames[cx.idx] = Frame { - symbol_addr: symaddr, - exact_position: ip, + symbol_addr: symaddr as *mut u8, + exact_position: ip as *mut u8, }; cx.idx += 1; } diff --git a/src/libstd/sys/unix/cmath.rs b/src/libstd/sys/unix/cmath.rs new file mode 100644 index 0000000000000..2bc96651b0c84 --- /dev/null +++ b/src/libstd/sys/unix/cmath.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +use libc::{c_float, c_double}; + +#[link_name = "m"] +extern { + pub fn acos(n: c_double) -> c_double; + pub fn acosf(n: c_float) -> c_float; + pub fn asin(n: c_double) -> c_double; + pub fn asinf(n: c_float) -> c_float; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn coshf(n: c_float) -> c_float; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + pub fn hypot(x: c_double, y: c_double) -> c_double; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn sinhf(n: c_float) -> c_float; + pub fn tan(n: c_double) -> c_double; + pub fn tanf(n: c_float) -> c_float; + pub fn tanh(n: c_double) -> c_double; + pub fn tanhf(n: c_float) -> c_float; +} diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index 89a44b9765783..4f878d8ad1fa8 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -92,14 +92,15 @@ impl Condvar { assert_eq!(r, 0); // Nanosecond calculations can't overflow because both values are below 1e9. - let nsec = dur.subsec_nanos() as libc::c_long + now.tv_nsec as libc::c_long; + let nsec = dur.subsec_nanos() + now.tv_nsec as u32; + let sec = saturating_cast_to_time_t(dur.as_secs()) .checked_add((nsec / 1_000_000_000) as libc::time_t) .and_then(|s| s.checked_add(now.tv_sec)); let nsec = nsec % 1_000_000_000; let timeout = sec.map(|s| { - libc::timespec { tv_sec: s, tv_nsec: nsec } + libc::timespec { tv_sec: s, tv_nsec: nsec as _} }).unwrap_or(TIMESPEC_MAX); let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index 3d9a06bedd57c..00cf7eca75dc3 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -118,27 +118,6 @@ pub mod os { pub const EXE_EXTENSION: &'static str = ""; } -#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] -pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "nacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".nexe"; - pub const EXE_EXTENSION: &'static str = "nexe"; -} -#[cfg(all(target_os = "nacl", target_arch = "le32"))] -pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "pnacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".pso"; - pub const DLL_EXTENSION: &'static str = "pso"; - pub const EXE_SUFFIX: &'static str = ".pexe"; - pub const EXE_EXTENSION: &'static str = "pexe"; -} - #[cfg(target_os = "haiku")] pub mod os { pub const FAMILY: &'static str = "unix"; diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 3e631ad40ac7f..2e17fd58e0a1e 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -20,7 +20,9 @@ use sys; use sys_common::{FromInner, AsInner, AsInnerMut}; use sys::platform::fs::MetadataExt as UnixMetadataExt; -/// Unix-specific extensions to `File` +/// Unix-specific extensions to [`File`]. +/// +/// [`File`]: ../../../../std/fs/struct.File.html #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Reads a number of bytes starting from a given offset. @@ -32,8 +34,28 @@ pub trait FileExt { /// /// The current file cursor is not affected by this function. /// - /// Note that similar to `File::read`, it is not an error to return with a + /// Note that similar to [`File::read`], it is not an error to return with a /// short read. + /// + /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::prelude::FileExt; + /// use std::fs::File; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read 8 bytes from the offset 10. + /// let num_bytes_read = file.read_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", num_bytes_read, buf); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; @@ -49,8 +71,26 @@ pub trait FileExt { /// When writing beyond the end of the file, the file is appropriately /// extended and the intermediate bytes are initialized with the value 0. /// - /// Note that similar to `File::write`, it is not an error to return a + /// Note that similar to [`File::write`], it is not an error to return a /// short write. + /// + /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::prelude::FileExt; + /// use std::fs::File; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_at(b"sushi", 10)?; + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; } @@ -215,36 +255,282 @@ impl OpenOptionsExt for OpenOptions { // casts and rely on manual lowering to `stat` if the raw type is desired. #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { + /// Returns the ID of the device containing the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let dev_id = meta.dev(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let inode = meta.ino(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ino(&self) -> u64; + /// Returns the rights applied to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let mode = meta.mode(); + /// let user_has_write_access = mode & 0o200; + /// let user_has_read_write_access = mode & 0o600; + /// let group_has_read_access = mode & 0o040; + /// let others_have_exec_access = mode & 0o001; + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mode(&self) -> u32; + /// Returns the number of hard links pointing to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nb_hard_links = meta.nlink(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn nlink(&self) -> u64; + /// Returns the user ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let user_id = meta.uid(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn uid(&self) -> u32; + /// Returns the group ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let group_id = meta.gid(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn gid(&self) -> u32; + /// Returns the device ID of this file (if it is a special one). + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let device_id = meta.rdev(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn rdev(&self) -> u64; + /// Returns the total size of this file in bytes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let file_size = meta.size(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn size(&self) -> u64; + /// Returns the time of the last access to the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_access_time = meta.atime(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn atime(&self) -> i64; + /// Returns the time of the last access to the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_access_time = meta.atime_nsec(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn atime_nsec(&self) -> i64; + /// Returns the time of the last modification of the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_modification_time = meta.mtime(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mtime(&self) -> i64; + /// Returns the time of the last modification of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_modification_time = meta.mtime_nsec(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mtime_nsec(&self) -> i64; + /// Returns the time of the last status change of the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_status_change_time = meta.ctime(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ctime(&self) -> i64; + /// Returns the time of the last status change of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_status_change_time = meta.ctime_nsec(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ctime_nsec(&self) -> i64; + /// Returns the blocksize for filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let blocksize = meta.blksize(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, in 512-byte units. + /// + /// Please note that this may be smaller than `st_size / 512` when the file has holes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let blocks = meta.blocks(); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn blocks(&self) -> u64; } @@ -269,19 +555,79 @@ impl MetadataExt for fs::Metadata { fn blocks(&self) -> u64 { self.st_blocks() } } -/// Add special unix types (block/char device, fifo and socket) +/// Add support for special unix types (block/char device, fifo and socket). #[stable(feature = "file_type_ext", since = "1.5.0")] pub trait FileTypeExt { /// Returns whether this file type is a block device. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("block_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_block_device()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_block_device(&self) -> bool; /// Returns whether this file type is a char device. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("char_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_char_device()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_char_device(&self) -> bool; /// Returns whether this file type is a fifo. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("fifo_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_fifo()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_fifo(&self) -> bool; /// Returns whether this file type is a socket. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// + /// # use std::io; + /// # fn f() -> io::Result<()> { + /// let meta = fs::metadata("unix.socket")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_socket()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_socket(&self) -> bool; } @@ -294,7 +640,9 @@ impl FileTypeExt for fs::FileType { fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) } } -/// Unix-specific extension methods for `fs::DirEntry` +/// Unix-specific extension methods for [`fs::DirEntry`]. +/// +/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html #[stable(feature = "dir_entry_ext", since = "1.1.0")] pub trait DirEntryExt { /// Returns the underlying `d_ino` field in the contained `dirent` @@ -354,7 +702,9 @@ pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> } #[stable(feature = "dir_builder", since = "1.6.0")] -/// An extension trait for `fs::DirBuilder` for unix-specific options. +/// An extension trait for [`fs::DirBuilder`] for unix-specific options. +/// +/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html pub trait DirBuilderExt { /// Sets the mode to create new directories with. This option defaults to /// 0o777. diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index 98bc90dd4e132..c221f7c8cfe24 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -10,8 +10,14 @@ //! Experimental extensions to `std` for Unix platforms. //! -//! For now, this module is limited to extracting file descriptors, -//! but its functionality will grow over time. +//! Provides access to platform-level information on Unix platforms, and +//! exposes Unix-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings (`OsStr`, +//! `OsString`), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. //! //! # Examples //! diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index cde21b089a20d..60309bec6d4fc 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -191,3 +191,9 @@ impl IntoRawFd for process::ChildStderr { self.into_inner().into_fd().into_raw() } } + +/// Returns the OS-assigned process identifier associated with this process's parent. +#[unstable(feature = "unix_ppid", issue = "46104")] +pub fn parent_id() -> u32 { + ::sys::os::getppid() +} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index c4616c3b395be..a1ca839dc1872 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -132,14 +132,14 @@ impl FileAttr { pub fn modified(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtime_nsec as libc::c_long, + tv_nsec: self.stat.st_mtime_nsec as _, })) } pub fn accessed(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atime_nsec as libc::c_long, + tv_nsec: self.stat.st_atime_nsec as _, })) } diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs index 2121848967939..c3e8d0b7d95a8 100644 --- a/src/libstd/sys/unix/l4re.rs +++ b/src/libstd/sys/unix/l4re.rs @@ -437,5 +437,9 @@ pub mod net { pub fn lookup_host(_: &str) -> io::Result { unimpl!(); } + + pub fn res_init_if_glibc_before_2_26() -> io::Result<()> { + unimpl!(); + } } diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 1b3f1000b77bb..9bdea945ea42e 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -22,7 +22,6 @@ use libc; #[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform; #[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform; #[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform; -#[cfg(all(not(dox), target_os = "nacl"))] pub use os::nacl as platform; #[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform; #[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform; #[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform; @@ -30,6 +29,9 @@ use libc; #[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform; #[cfg(all(not(dox), target_os = "l4re"))] pub use os::linux as platform; +pub use self::rand::hashmap_random_keys; +pub use libc::strlen; + #[macro_use] pub mod weak; @@ -37,6 +39,7 @@ pub mod args; pub mod android; #[cfg(feature = "backtrace")] pub mod backtrace; +pub mod cmath; pub mod condvar; pub mod env; pub mod ext; @@ -77,11 +80,11 @@ pub fn init() { reset_sigpipe(); } - #[cfg(not(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia")))] + #[cfg(not(any(target_os = "emscripten", target_os="fuchsia")))] unsafe fn reset_sigpipe() { assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); } - #[cfg(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia"))] + #[cfg(any(target_os = "emscripten", target_os="fuchsia"))] unsafe fn reset_sigpipe() {} } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 668b2f92aba07..e775f857f2b40 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -176,11 +176,16 @@ impl Socket { } 0 => {} _ => { - if pollfd.revents & libc::POLLOUT == 0 { - if let Some(e) = self.take_error()? { - return Err(e); - } + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & libc::POLLHUP != 0 { + let e = self.take_error()? + .unwrap_or_else(|| { + io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); } + return Ok(()); } } @@ -355,3 +360,82 @@ impl FromInner for Socket { impl IntoInner for Socket { fn into_inner(self) -> c_int { self.0.into_raw() } } + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +pub fn res_init_if_glibc_before_2_26() -> io::Result<()> { + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { + if let Some(version) = parse_glibc_version(version_str) { + if version < (2, 26) { + let ret = unsafe { libc::res_init() }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + } + } + } + Ok(()) +} + +fn glibc_version_cstr() -> Option<&'static CStr> { + weak! { + fn gnu_get_libc_version() -> *const libc::c_char + } + if let Some(f) = gnu_get_libc_version.get() { + unsafe { Some(CStr::from_ptr(f())) } + } else { + None + } +} + +// Returns Some((major, minor)) if the string is a valid "x.y" version, +// ignoring any extra dot-separated parts. Otherwise return None. +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split(".").map(str::parse::).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_res_init() { + // This mostly just tests that the weak linkage doesn't panic wildly... + res_init_if_glibc_before_2_26().unwrap(); + } + + #[test] + fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } + } +} diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 5ef98d247105e..4f33a2b12fe5d 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -223,7 +223,34 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "netbsd")] pub fn current_exe() -> io::Result { - ::fs::read_link("/proc/curproc/exe") + fn sysctl() -> io::Result { + unsafe { + let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME]; + let mut path_len: usize = 0; + cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint, + ptr::null_mut(), &mut path_len, + ptr::null(), 0))?; + if path_len <= 1 { + return Err(io::Error::new(io::ErrorKind::Other, + "KERN_PROC_PATHNAME sysctl returned zero-length string")) + } + let mut path: Vec = Vec::with_capacity(path_len); + cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint, + path.as_ptr() as *mut libc::c_void, &mut path_len, + ptr::null(), 0))?; + path.set_len(path_len - 1); // chop off NUL + Ok(PathBuf::from(OsString::from_vec(path))) + } + } + fn procfs() -> io::Result { + let curproc_exe = path::Path::new("/proc/curproc/exe"); + if curproc_exe.is_file() { + return ::fs::read_link(curproc_exe); + } + Err(io::Error::new(io::ErrorKind::Other, + "/proc/curproc/exe doesn't point to regular file.")) + } + sysctl().or_else(|_| procfs()) } #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] @@ -483,12 +510,10 @@ pub fn home_dir() -> Option { #[cfg(any(target_os = "android", target_os = "ios", - target_os = "nacl", target_os = "emscripten"))] unsafe fn fallback() -> Option { None } #[cfg(not(any(target_os = "android", target_os = "ios", - target_os = "nacl", target_os = "emscripten")))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { @@ -513,3 +538,11 @@ pub fn home_dir() -> Option { pub fn exit(code: i32) -> ! { unsafe { libc::exit(code as c_int) } } + +pub fn getpid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn getppid() -> u32 { + unsafe { libc::getppid() as u32 } +} diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs index 777db17e3e164..a27e76a0e3bcc 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys/unix/os_str.rs @@ -15,6 +15,8 @@ use borrow::Cow; use fmt; use str; use mem; +use rc::Rc; +use sync::Arc; use sys_common::{AsInner, IntoInner}; use std_unicode::lossy::Utf8Lossy; @@ -123,6 +125,16 @@ impl Buf { let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; Buf { inner: inner.into_vec() } } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } } impl Slice { @@ -156,4 +168,16 @@ impl Slice { let boxed: Box<[u8]> = Default::default(); unsafe { mem::transmute(boxed) } } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } } diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 689ccd78524a0..383434b1cd870 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -464,7 +464,6 @@ mod tests { // test from being flaky we ignore it on macOS. #[test] #[cfg_attr(target_os = "macos", ignore)] - #[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl. // When run under our current QEMU emulation test suite this test fails, // although the reason isn't very clear as to why. For now this test is // ignored there. diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index 5d34da04446f1..a7a67ed36e823 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -9,7 +9,7 @@ // except according to those terms. use io; -use libc; +use libc::{self, size_t}; use mem; use ptr; @@ -148,8 +148,8 @@ impl Process { use sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: zx_size_t = 0; - let mut avail: zx_size_t = 0; + let mut actual: size_t = 0; + let mut avail: size_t = 0; unsafe { zx_cvt(zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, @@ -171,8 +171,8 @@ impl Process { use sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: zx_size_t = 0; - let mut avail: zx_size_t = 0; + let mut actual: size_t = 0; + let mut avail: size_t = 0; unsafe { let status = zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 870db8200273e..743c458d580c6 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -184,8 +184,8 @@ impl Command { *sys::os::environ() = envp.as_ptr(); } - // NaCl has no signal support. - #[cfg(not(any(target_os = "nacl", target_os = "emscripten")))] + // emscripten has no signal support. + #[cfg(not(any(target_os = "emscripten")))] { use mem; // Reset signal handling so the child process starts in a diff --git a/src/libstd/sys/unix/process/zircon.rs b/src/libstd/sys/unix/process/zircon.rs index b5ec11b40fd3d..90864e6ef3ff2 100644 --- a/src/libstd/sys/unix/process/zircon.rs +++ b/src/libstd/sys/unix/process/zircon.rs @@ -15,15 +15,13 @@ use io; use os::raw::c_char; use u64; -use libc::{c_int, c_void}; +use libc::{c_int, c_void, size_t}; -pub type zx_handle_t = i32; +pub type zx_handle_t = u32; pub type zx_vaddr_t = usize; pub type zx_rights_t = u32; pub type zx_status_t = i32; -pub type zx_size_t = usize; - pub const ZX_HANDLE_INVALID: zx_handle_t = 0; pub type zx_time_t = u64; @@ -115,36 +113,37 @@ extern { pending: *mut zx_signals_t) -> zx_status_t; pub fn zx_object_get_info(handle: zx_handle_t, topic: u32, buffer: *mut c_void, - buffer_size: zx_size_t, actual_size: *mut zx_size_t, - avail: *mut zx_size_t) -> zx_status_t; + buffer_size: size_t, actual_size: *mut size_t, + avail: *mut size_t) -> zx_status_t; } // From `enum special_handles` in system/ulib/launchpad/launchpad.c // HND_LOADER_SVC = 0 // HND_EXEC_VMO = 1 -pub const HND_SPECIAL_COUNT: usize = 2; +// HND_SEGMENTS_VMAR = 2 +const HND_SPECIAL_COUNT: c_int = 3; #[repr(C)] pub struct launchpad_t { argc: u32, envc: u32, args: *const c_char, - args_len: usize, + args_len: size_t, env: *const c_char, - env_len: usize, + env_len: size_t, handles: *mut zx_handle_t, handles_info: *mut u32, - handle_count: usize, - handle_alloc: usize, + handle_count: size_t, + handle_alloc: size_t, entry: zx_vaddr_t, base: zx_vaddr_t, vdso_base: zx_vaddr_t, - stack_size: usize, + stack_size: size_t, - special_handles: [zx_handle_t; HND_SPECIAL_COUNT], + special_handles: [zx_handle_t; HND_SPECIAL_COUNT as usize], loader_message: bool, } diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index fd066c9cdbeef..caa1894576552 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -8,20 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::imp::OsRng; - use mem; +use slice; -fn next_u32(fill_buf: &mut FnMut(&mut [u8])) -> u32 { - let mut buf: [u8; 4] = [0; 4]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 4], u32>(buf) } -} - -fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { - let mut buf: [u8; 8] = [0; 8]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 8], u64>(buf) } +pub fn hashmap_random_keys() -> (u64, u64) { + let mut v = (0, 0); + unsafe { + let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, + mem::size_of_val(&v)); + imp::fill_bytes(view); + } + return v } #[cfg(all(unix, @@ -30,56 +27,22 @@ fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { not(target_os = "freebsd"), not(target_os = "fuchsia")))] mod imp { - use self::OsRngInner::*; - use super::{next_u32, next_u64}; - use fs::File; - use io; + use io::Read; use libc; - use rand::Rng; - use rand::reader::ReaderRng; use sys::os::errno; - #[cfg(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x")))] + #[cfg(any(target_os = "linux", target_os = "android"))] fn getrandom(buf: &mut [u8]) -> libc::c_long { - #[cfg(target_arch = "x86_64")] - const NR_GETRANDOM: libc::c_long = 318; - #[cfg(target_arch = "x86")] - const NR_GETRANDOM: libc::c_long = 355; - #[cfg(target_arch = "arm")] - const NR_GETRANDOM: libc::c_long = 384; - #[cfg(target_arch = "s390x")] - const NR_GETRANDOM: libc::c_long = 349; - #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] - const NR_GETRANDOM: libc::c_long = 359; - #[cfg(target_arch = "aarch64")] - const NR_GETRANDOM: libc::c_long = 278; - - const GRND_NONBLOCK: libc::c_uint = 0x0001; - unsafe { - libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK) + libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(not(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x"))))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } - fn getrandom_fill_bytes(v: &mut [u8]) { + fn getrandom_fill_bytes(v: &mut [u8]) -> bool { let mut read = 0; while read < v.len() { let result = getrandom(&mut v[read..]); @@ -88,18 +51,7 @@ mod imp { if err == libc::EINTR { continue; } else if err == libc::EAGAIN { - // if getrandom() returns EAGAIN it would have blocked - // because the non-blocking pool (urandom) has not - // initialized in the kernel yet due to a lack of entropy - // the fallback we do here is to avoid blocking applications - // which could depend on this call without ever knowing - // they do and don't have a work around. The PRNG of - // /dev/urandom will still be used but not over a completely - // full entropy pool - let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom"); - let mut reader_rng = ReaderRng::new(reader); - reader_rng.fill_bytes(&mut v[read..]); - read += v.len(); + return false } else { panic!("unexpected getrandom error: {}", err); } @@ -107,17 +59,13 @@ mod imp { read += result as usize; } } + + return true } - #[cfg(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x")))] + #[cfg(any(target_os = "linux", target_os = "android"))] fn is_getrandom_available() -> bool { + use io; use sync::atomic::{AtomicBool, Ordering}; use sync::Once; @@ -139,99 +87,40 @@ mod imp { AVAILABLE.load(Ordering::Relaxed) } - #[cfg(not(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x"))))] + #[cfg(not(any(target_os = "linux", target_os = "android")))] fn is_getrandom_available() -> bool { false } - pub struct OsRng { - inner: OsRngInner, - } - - enum OsRngInner { - OsGetrandomRng, - OsReaderRng(ReaderRng), - } - - impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - if is_getrandom_available() { - return Ok(OsRng { inner: OsGetrandomRng }); - } - - let reader = File::open("/dev/urandom")?; - let reader_rng = ReaderRng::new(reader); - - Ok(OsRng { inner: OsReaderRng(reader_rng) }) + pub fn fill_bytes(v: &mut [u8]) { + // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN, + // meaning it would have blocked because the non-blocking pool (urandom) + // has not initialized in the kernel yet due to a lack of entropy the + // fallback we do here is to avoid blocking applications which could + // depend on this call without ever knowing they do and don't have a + // work around. The PRNG of /dev/urandom will still be used but not + // over a completely full entropy pool + if is_getrandom_available() && getrandom_fill_bytes(v) { + return } - } - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - match self.inner { - OsGetrandomRng => next_u32(&mut getrandom_fill_bytes), - OsReaderRng(ref mut rng) => rng.next_u32(), - } - } - fn next_u64(&mut self) -> u64 { - match self.inner { - OsGetrandomRng => next_u64(&mut getrandom_fill_bytes), - OsReaderRng(ref mut rng) => rng.next_u64(), - } - } - fn fill_bytes(&mut self, v: &mut [u8]) { - match self.inner { - OsGetrandomRng => getrandom_fill_bytes(v), - OsReaderRng(ref mut rng) => rng.fill_bytes(v) - } - } + let mut file = File::open("/dev/urandom") + .expect("failed to open /dev/urandom"); + file.read_exact(v).expect("failed to read /dev/urandom"); } } #[cfg(target_os = "openbsd")] mod imp { - use super::{next_u32, next_u64}; - - use io; use libc; use sys::os::errno; - use rand::Rng; - - pub struct OsRng { - // dummy field to ensure that this struct cannot be constructed outside - // of this module - _dummy: (), - } - - impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - Ok(OsRng { _dummy: () }) - } - } - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { - libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) - }; - if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); - } + pub fn fill_bytes(v: &mut [u8]) { + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { + libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) + }; + if ret == -1 { + panic!("unexpected getentropy error: {}", errno()); } } } @@ -239,18 +128,9 @@ mod imp { #[cfg(target_os = "ios")] mod imp { - use super::{next_u32, next_u64}; - use io; - use ptr; - use rand::Rng; use libc::{c_int, size_t}; - - pub struct OsRng { - // dummy field to ensure that this struct cannot be constructed outside - // of this module - _dummy: (), - } + use ptr; enum SecRandom {} @@ -259,79 +139,41 @@ mod imp { extern { fn SecRandomCopyBytes(rnd: *const SecRandom, - count: size_t, bytes: *mut u8) -> c_int; + count: size_t, + bytes: *mut u8) -> c_int; } - impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - Ok(OsRng { _dummy: () }) - } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - let ret = unsafe { - SecRandomCopyBytes(kSecRandomDefault, v.len(), - v.as_mut_ptr()) - }; - if ret == -1 { - panic!("couldn't generate random bytes: {}", - io::Error::last_os_error()); - } + pub fn fill_bytes(v: &mut [u8]) { + let ret = unsafe { + SecRandomCopyBytes(kSecRandomDefault, + v.len(), + v.as_mut_ptr()) + }; + if ret == -1 { + panic!("couldn't generate random bytes: {}", + io::Error::last_os_error()); } } } #[cfg(target_os = "freebsd")] mod imp { - use super::{next_u32, next_u64}; - - use io; use libc; - use rand::Rng; use ptr; - pub struct OsRng { - // dummy field to ensure that this struct cannot be constructed outside - // of this module - _dummy: (), - } - - impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - Ok(OsRng { _dummy: () }) - } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - // kern.arandom permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let mut s_len = s.len(); - let ret = unsafe { - libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint, - s.as_mut_ptr() as *mut _, &mut s_len, - ptr::null(), 0) - }; - if ret == -1 || s_len != s.len() { - panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", - ret, s.len(), s_len); - } + pub fn fill_bytes(v: &mut [u8]) { + let mib = [libc::CTL_KERN, libc::KERN_ARND]; + // kern.arandom permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let mut s_len = s.len(); + let ret = unsafe { + libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint, + s.as_mut_ptr() as *mut _, &mut s_len, + ptr::null(), 0) + }; + if ret == -1 || s_len != s.len() { + panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", + ret, s.len(), s_len); } } } @@ -339,11 +181,6 @@ mod imp { #[cfg(target_os = "fuchsia")] mod imp { - use super::{next_u32, next_u64}; - - use io; - use rand::Rng; - #[link(name = "zircon")] extern { fn zx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32; @@ -361,39 +198,18 @@ mod imp { } } - pub struct OsRng { - // dummy field to ensure that this struct cannot be constructed outside - // of this module - _dummy: (), - } - - impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - Ok(OsRng { _dummy: () }) - } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - let mut buf = v; - while !buf.is_empty() { - let ret = getrandom(buf); - match ret { - Err(err) => { - panic!("kernel zx_cprng_draw call failed! (returned {}, buf.len() {})", - err, buf.len()) - } - Ok(actual) => { - let move_buf = buf; - buf = &mut move_buf[(actual as usize)..]; - } + pub fn fill_bytes(v: &mut [u8]) { + let mut buf = v; + while !buf.is_empty() { + let ret = getrandom(buf); + match ret { + Err(err) => { + panic!("kernel zx_cprng_draw call failed! (returned {}, buf.len() {})", + err, buf.len()) + } + Ok(actual) => { + let move_buf = buf; + buf = &mut move_buf[(actual as usize)..]; } } } diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 7a8fe25d98ee1..e9b3d4affc7dd 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -70,5 +70,8 @@ impl io::Write for Stderr { } } -pub const EBADF_ERR: i32 = ::libc::EBADF as i32; +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 6c4a332429646..9da33f5adac14 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -87,7 +87,7 @@ impl Thread { }; extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { start_thread(main); } + unsafe { start_thread(main as *mut u8); } ptr::null_mut() } } @@ -149,7 +149,7 @@ impl Thread { pub fn sleep(dur: Duration) { let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as libc::c_long; + let mut nsecs = dur.subsec_nanos() as _; // If we're awoken with a signal then the return value will be -1 and // nanosleep will fill in `ts` with the remaining time. diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index c1bea95ce91ab..837cd7292e280 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -60,7 +60,7 @@ impl Timespec { Timespec { t: libc::timespec { tv_sec: secs, - tv_nsec: nsec as libc::c_long, + tv_nsec: nsec as _, }, } } @@ -83,7 +83,7 @@ impl Timespec { Timespec { t: libc::timespec { tv_sec: secs, - tv_nsec: nsec as libc::c_long, + tv_nsec: nsec as _, }, } } diff --git a/src/libstd/sys/wasm/args.rs b/src/libstd/sys/wasm/args.rs new file mode 100644 index 0000000000000..d2a4a7b19d548 --- /dev/null +++ b/src/libstd/sys/wasm/args.rs @@ -0,0 +1,90 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use marker::PhantomData; +use mem; +use vec; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // On wasm these should always be null, so there's nothing for us to do here +} + +pub unsafe fn cleanup() { +} + +pub fn args() -> Args { + // When the runtime debugging is enabled we'll link to some extra runtime + // functions to actually implement this. These are for now just implemented + // in a node.js script but they're off by default as they're sort of weird + // in a web-wasm world. + if !super::DEBUG { + return Args { + iter: Vec::new().into_iter(), + _dont_send_or_sync_me: PhantomData, + } + } + + // You'll find the definitions of these in `src/etc/wasm32-shim.js`. These + // are just meant for debugging and should not be relied on. + extern { + fn rust_wasm_args_count() -> usize; + fn rust_wasm_args_arg_size(a: usize) -> usize; + fn rust_wasm_args_arg_fill(a: usize, ptr: *mut u8); + } + + unsafe { + let cnt = rust_wasm_args_count(); + let mut v = Vec::with_capacity(cnt); + for i in 0..cnt { + let n = rust_wasm_args_arg_size(i); + let mut data = vec![0; n]; + rust_wasm_args_arg_fill(i, data.as_mut_ptr()); + v.push(mem::transmute::, OsString>(data)); + } + Args { + iter: v.into_iter(), + _dont_send_or_sync_me: PhantomData, + } + } +} + +pub struct Args { + iter: vec::IntoIter, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.iter.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} diff --git a/src/libstd/sys/wasm/backtrace.rs b/src/libstd/sys/wasm/backtrace.rs new file mode 100644 index 0000000000000..9a8c48ff29fc7 --- /dev/null +++ b/src/libstd/sys/wasm/backtrace.rs @@ -0,0 +1,37 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::unsupported; +use sys_common::backtrace::Frame; + +pub struct BacktraceContext; + +pub fn unwind_backtrace(_frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + unsupported() +} + +pub fn resolve_symname(_frame: Frame, + _callback: F, + _: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + unsupported() +} + +pub fn foreach_symbol_fileline(_: Frame, + _: F, + _: &BacktraceContext) -> io::Result + where F: FnMut(&[u8], u32) -> io::Result<()> +{ + unsupported() +} diff --git a/src/libstd/sys/wasm/cmath.rs b/src/libstd/sys/wasm/cmath.rs new file mode 100644 index 0000000000000..87ac2091cad41 --- /dev/null +++ b/src/libstd/sys/wasm/cmath.rs @@ -0,0 +1,119 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[inline] +pub unsafe fn cbrtf(n: f32) -> f32 { + f64::cbrt(n as f64) as f32 +} + +#[inline] +pub unsafe fn expm1f(n: f32) -> f32 { + f64::exp_m1(n as f64) as f32 +} + +#[inline] +#[allow(deprecated)] +pub unsafe fn fdimf(a: f32, b: f32) -> f32 { + f64::abs_sub(a as f64, b as f64) as f32 +} + +#[inline] +pub unsafe fn log1pf(n: f32) -> f32 { + f64::ln_1p(n as f64) as f32 +} + +#[inline] +pub unsafe fn hypotf(x: f32, y: f32) -> f32 { + f64::hypot(x as f64, y as f64) as f32 +} + +#[inline] +pub unsafe fn acosf(n: f32) -> f32 { + f64::acos(n as f64) as f32 +} + +#[inline] +pub unsafe fn asinf(n: f32) -> f32 { + f64::asin(n as f64) as f32 +} + +#[inline] +pub unsafe fn atan2f(n: f32, b: f32) -> f32 { + f64::atan2(n as f64, b as f64) as f32 +} + +#[inline] +pub unsafe fn atanf(n: f32) -> f32 { + f64::atan(n as f64) as f32 +} + +#[inline] +pub unsafe fn coshf(n: f32) -> f32 { + f64::cosh(n as f64) as f32 +} + +#[inline] +pub unsafe fn sinhf(n: f32) -> f32 { + f64::sinh(n as f64) as f32 +} + +#[inline] +pub unsafe fn tanf(n: f32) -> f32 { + f64::tan(n as f64) as f32 +} + +#[inline] +pub unsafe fn tanhf(n: f32) -> f32 { + f64::tanh(n as f64) as f32 +} + +// Right now all these functions, the f64 version of the functions above, all +// shell out to random names. These names aren't actually defined anywhere, per +// se, but we need this to compile somehow. +// +// The idea with this is that when you're using wasm then, for now, we have no +// way of providing an implementation of these which delegates to a "correct" +// implementation. For example most wasm applications probably just want to +// delegate to the javascript `Math` object and its related functions, but wasm +// doesn't currently have the ability to seamlessly do that (when you +// instantiate a module you have to set that up). +// +// As a result these are just defined here with "hopefully helpful" names. The +// symbols won't ever be needed or show up unless these functions are called, +// and hopefully when they're called the errors are self-explanatory enough to +// figure out what's going on. + +extern { + #[link_name = "Math_acos"] + pub fn acos(n: f64) -> f64; + #[link_name = "Math_asin"] + pub fn asin(n: f64) -> f64; + #[link_name = "Math_atan"] + pub fn atan(n: f64) -> f64; + #[link_name = "Math_atan2"] + pub fn atan2(a: f64, b: f64) -> f64; + #[link_name = "Math_cbrt"] + pub fn cbrt(n: f64) -> f64; + #[link_name = "Math_cosh"] + pub fn cosh(n: f64) -> f64; + #[link_name = "Math_expm1"] + pub fn expm1(n: f64) -> f64; + pub fn fdim(a: f64, b: f64) -> f64; + #[link_name = "Math_log1p"] + pub fn log1p(n: f64) -> f64; + #[link_name = "Math_sinh"] + pub fn sinh(n: f64) -> f64; + #[link_name = "Math_tan"] + pub fn tan(n: f64) -> f64; + #[link_name = "Math_tanh"] + pub fn tanh(n: f64) -> f64; + #[link_name = "Math_hypot"] + pub fn hypot(x: f64, y: f64) -> f64; +} diff --git a/src/libstd/sys/wasm/condvar.rs b/src/libstd/sys/wasm/condvar.rs new file mode 100644 index 0000000000000..afa7afeef5988 --- /dev/null +++ b/src/libstd/sys/wasm/condvar.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::mutex::Mutex; +use time::Duration; + +pub struct Condvar { } + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn notify_one(&self) { + } + + #[inline] + pub unsafe fn notify_all(&self) { + } + + pub unsafe fn wait(&self, _mutex: &Mutex) { + panic!("can't block with web assembly") + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("can't block with web assembly"); + } + + #[inline] + pub unsafe fn destroy(&self) { + } +} diff --git a/src/libstd/sys/wasm/env.rs b/src/libstd/sys/wasm/env.rs new file mode 100644 index 0000000000000..1422042bd0228 --- /dev/null +++ b/src/libstd/sys/wasm/env.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = ""; + pub const OS: &'static str = ""; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".wasm"; + pub const DLL_EXTENSION: &'static str = "wasm"; + pub const EXE_SUFFIX: &'static str = ".wasm"; + pub const EXE_EXTENSION: &'static str = "wasm"; +} diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/wasm/fs.rs new file mode 100644 index 0000000000000..b3c70a6685a6c --- /dev/null +++ b/src/libstd/sys/wasm/fs.rs @@ -0,0 +1,304 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use fmt; +use hash::{Hash, Hasher}; +use io::{self, SeekFrom}; +use path::{Path, PathBuf}; +use sys::time::SystemTime; +use sys::{unsupported, Void}; + +pub struct File(Void); + +pub struct FileAttr(Void); + +pub struct ReadDir(Void); + +pub struct DirEntry(Void); + +#[derive(Clone, Debug)] +pub struct OpenOptions { } + +pub struct FilePermissions(Void); + +pub struct FileType(Void); + +#[derive(Debug)] +pub struct DirBuilder { } + +impl FileAttr { + pub fn size(&self) -> u64 { + match self.0 {} + } + + pub fn perm(&self) -> FilePermissions { + match self.0 {} + } + + pub fn file_type(&self) -> FileType { + match self.0 {} + } + + pub fn modified(&self) -> io::Result { + match self.0 {} + } + + pub fn accessed(&self) -> io::Result { + match self.0 {} + } + + pub fn created(&self) -> io::Result { + match self.0 {} + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + match self.0 {} + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + match self.0 {} + } + + pub fn set_readonly(&mut self, _readonly: bool) { + match self.0 {} + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + match self.0 {} + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + match self.0 {} + } +} + +impl Eq for FilePermissions { +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + match self.0 {} + } + + pub fn is_file(&self) -> bool { + match self.0 {} + } + + pub fn is_symlink(&self) -> bool { + match self.0 {} + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + match self.0 {} + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + match self.0 {} + } +} + +impl Eq for FileType { +} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + match self.0 {} + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match self.0 {} + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + match self.0 {} + } + + pub fn file_name(&self) -> OsString { + match self.0 {} + } + + pub fn metadata(&self) -> io::Result { + match self.0 {} + } + + pub fn file_type(&self) -> io::Result { + match self.0 {} + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { } + } + + pub fn read(&mut self, _read: bool) { } + pub fn write(&mut self, _write: bool) { } + pub fn append(&mut self, _append: bool) { } + pub fn truncate(&mut self, _truncate: bool) { } + pub fn create(&mut self, _create: bool) { } + pub fn create_new(&mut self, _create_new: bool) { } +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + match self.0 {} + } + + pub fn fsync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn datasync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + match self.0 {} + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn flush(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { } + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/src/libstd/sys/wasm/memchr.rs b/src/libstd/sys/wasm/memchr.rs new file mode 100644 index 0000000000000..e611d94af30b1 --- /dev/null +++ b/src/libstd/sys/wasm/memchr.rs @@ -0,0 +1,11 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use sys_common::memchr::fallback::{memchr, memrchr}; diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs new file mode 100644 index 0000000000000..b838dbafd6f0c --- /dev/null +++ b/src/libstd/sys/wasm/mod.rs @@ -0,0 +1,104 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! System bindings for the wasm/web platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for wasm. Note that this wasm is *not* the emscripten +//! wasm, so we have no runtime here. +//! +//! This is all super highly experimental and not actually intended for +//! wide/production use yet, it's still all in the experimental category. This +//! will likely change over time. +//! +//! Currently all functions here are basically stubs that immediately return +//! errors. The hope is that with a portability lint we can turn actually just +//! remove all this and just omit parts of the standard library if we're +//! compiling for wasm. That way it's a compile time error for something that's +//! guaranteed to be a runtime error! + +use io; +use os::raw::c_char; + +// Right now the wasm backend doesn't even have the ability to print to the +// console by default. Wasm can't import anything from JS! (you have to +// explicitly provide it). +// +// Sometimes that's a real bummer, though, so this flag can be set to `true` to +// enable calling various shims defined in `src/etc/wasm32-shim.js` which should +// help receive debug output and see what's going on. In general this flag +// currently controls "will we call out to our own defined shims in node.js", +// and this flag should always be `false` for release builds. +const DEBUG: bool = false; + +pub mod args; +pub mod backtrace; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod thread; +pub mod thread_local; +pub mod time; +pub mod stdio; + +#[cfg(not(test))] +pub fn init() { +} + +pub fn unsupported() -> io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> io::Error { + io::Error::new(io::ErrorKind::Other, + "operation not supported on wasm yet") +} + +pub fn decode_error_kind(_code: i32) -> io::ErrorKind { + io::ErrorKind::Other +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(mut s: *const c_char) -> usize { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + +pub unsafe fn abort_internal() -> ! { + ::intrinsics::abort(); +} + +// We don't have randomness yet, but I totally used a random number generator to +// generate these numbers. +// +// More seriously though this is just for DOS protection in hash maps. It's ok +// if we don't do that on wasm just yet. +pub fn hashmap_random_keys() -> (u64, u64) { + (1, 2) +} diff --git a/src/libstd/sys/wasm/mutex.rs b/src/libstd/sys/wasm/mutex.rs new file mode 100644 index 0000000000000..4197bdcc80839 --- /dev/null +++ b/src/libstd/sys/wasm/mutex.rs @@ -0,0 +1,79 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; + +pub struct Mutex { + locked: UnsafeCell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} // no threads on wasm + +impl Mutex { + pub const fn new() -> Mutex { + Mutex { locked: UnsafeCell::new(false) } + } + + #[inline] + pub unsafe fn init(&mut self) { + } + + #[inline] + pub unsafe fn lock(&self) { + let locked = self.locked.get(); + assert!(!*locked, "cannot recursively acquire mutex"); + *locked = true; + } + + #[inline] + pub unsafe fn unlock(&self) { + *self.locked.get() = false; + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let locked = self.locked.get(); + if *locked { + false + } else { + *locked = true; + true + } + } + + #[inline] + pub unsafe fn destroy(&self) { + } +} + +// All empty stubs because wasm has no threads yet, so lock acquisition always +// succeeds. +pub struct ReentrantMutex { +} + +impl ReentrantMutex { + pub unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex { } + } + + pub unsafe fn init(&mut self) {} + + pub unsafe fn lock(&self) {} + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + true + } + + pub unsafe fn unlock(&self) {} + + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs new file mode 100644 index 0000000000000..e7476ab37f7c8 --- /dev/null +++ b/src/libstd/sys/wasm/net.rs @@ -0,0 +1,337 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use io; +use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; +use time::Duration; +use sys::{unsupported, Void}; + +pub struct TcpStream(Void); + +impl TcpStream { + pub fn connect(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn peer_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn nodelay(&self) -> io::Result { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct TcpListener(Void); + +impl TcpListener { + pub fn bind(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn only_v6(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct UdpSocket(Void); + +impl UdpSocket { + pub fn bind(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn broadcast(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v6(&self) -> io::Result { + match self.0 {} + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn send(&self, _: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct LookupHost(Void); + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + match self.0 {} + } +} + +pub fn lookup_host(_: &str) -> io::Result { + unsupported() +} + +#[allow(bad_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr { + } + + pub type socklen_t = usize; +} diff --git a/src/libstd/sys/wasm/os.rs b/src/libstd/sys/wasm/os.rs new file mode 100644 index 0000000000000..c98030f7ebf53 --- /dev/null +++ b/src/libstd/sys/wasm/os.rs @@ -0,0 +1,136 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::intrinsics; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io; +use mem; +use path::{self, PathBuf}; +use str; +use sys::{unsupported, Void}; + +pub fn errno() -> i32 { + 0 +} + +pub fn error_string(_errno: i32) -> String { + format!("operation successful") +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(&'a Void); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result + where I: Iterator, T: AsRef +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "not supported on wasm yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { + "not supported on wasm yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(Void); + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + match self.0 {} + } +} + +pub fn env() -> Env { + panic!("not supported on web assembly") +} + +pub fn getenv(k: &OsStr) -> io::Result> { + // If we're debugging the runtime then we actually probe node.js to ask for + // the value of environment variables to help provide inputs to programs. + // The `extern` shims here are defined in `src/etc/wasm32-shim.js` and are + // intended for debugging only, you should not rely on them. + if !super::DEBUG { + return Ok(None) + } + + extern { + fn rust_wasm_getenv_len(k: *const u8, kl: usize) -> isize; + fn rust_wasm_getenv_data(k: *const u8, kl: usize, v: *mut u8); + } + unsafe { + let k: &[u8] = mem::transmute(k); + let n = rust_wasm_getenv_len(k.as_ptr(), k.len()); + if n == -1 { + return Ok(None) + } + let mut data = vec![0; n as usize]; + rust_wasm_getenv_data(k.as_ptr(), k.len(), data.as_mut_ptr()); + Ok(Some(mem::transmute(data))) + } +} + +pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn unsetenv(_n: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on wasm") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(_code: i32) -> ! { + unsafe { intrinsics::abort() } +} + +pub fn getpid() -> u32 { + panic!("no pids on wasm") +} diff --git a/src/libstd/sys/wasm/os_str.rs b/src/libstd/sys/wasm/os_str.rs new file mode 100644 index 0000000000000..0e64b5bc6b8fb --- /dev/null +++ b/src/libstd/sys/wasm/os_str.rs @@ -0,0 +1,183 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec`/`[u8]`. + +use borrow::Cow; +use fmt; +use str; +use mem; +use rc::Rc; +use sync::Arc; +use sys_common::{AsInner, IntoInner}; +use std_unicode::lossy::Utf8Lossy; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec +} + +pub struct Slice { + pub inner: [u8] +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +impl IntoInner> for Buf { + fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } +} diff --git a/src/libstd/sys/wasm/path.rs b/src/libstd/sys/wasm/path.rs new file mode 100644 index 0000000000000..395b8c1e40e98 --- /dev/null +++ b/src/libstd/sys/wasm/path.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path::Prefix; +use ffi::OsStr; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_: &OsStr) -> Option { + None +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/wasm/pipe.rs new file mode 100644 index 0000000000000..992e1ac409cfb --- /dev/null +++ b/src/libstd/sys/wasm/pipe.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec, + _p2: AnonPipe, + _v2: &mut Vec) -> io::Result<()> { + match p1.0 {} +} diff --git a/src/libstd/sys/wasm/process.rs b/src/libstd/sys/wasm/process.rs new file mode 100644 index 0000000000000..4febe8a146382 --- /dev/null +++ b/src/libstd/sys/wasm/process.rs @@ -0,0 +1,151 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use fmt; +use io; +use sys::fs::File; +use sys::pipe::AnonPipe; +use sys::{unsupported, Void}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command {} + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env(&mut self, _key: &OsStr, _val: &OsStr) { + } + + pub fn env_remove(&mut self, _key: &OsStr) { + } + + pub fn env_clear(&mut self) { + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + file.diverge() + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result> { + match self.0 {} + } +} diff --git a/src/libstd/sys/wasm/rwlock.rs b/src/libstd/sys/wasm/rwlock.rs new file mode 100644 index 0000000000000..8b06f54167487 --- /dev/null +++ b/src/libstd/sys/wasm/rwlock.rs @@ -0,0 +1,82 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; + +pub struct RWLock { + mode: UnsafeCell, +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} // no threads on wasm + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + mode: UnsafeCell::new(0), + } + } + + #[inline] + pub unsafe fn read(&self) { + let mode = self.mode.get(); + if *mode >= 0 { + *mode += 1; + } else { + panic!("rwlock locked for writing"); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let mode = self.mode.get(); + if *mode >= 0 { + *mode += 1; + true + } else { + false + } + } + + #[inline] + pub unsafe fn write(&self) { + let mode = self.mode.get(); + if *mode == 0 { + *mode = -1; + } else { + panic!("rwlock locked for reading") + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let mode = self.mode.get(); + if *mode == 0 { + *mode = -1; + true + } else { + false + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + *self.mode.get() -= 1; + } + + #[inline] + pub unsafe fn write_unlock(&self) { + *self.mode.get() += 1; + } + + #[inline] + pub unsafe fn destroy(&self) { + } +} diff --git a/src/libstd/sys/wasm/stack_overflow.rs b/src/libstd/sys/wasm/stack_overflow.rs new file mode 100644 index 0000000000000..bed274142f1ce --- /dev/null +++ b/src/libstd/sys/wasm/stack_overflow.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { +} + +pub unsafe fn cleanup() { +} diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/wasm/stdio.rs new file mode 100644 index 0000000000000..0f75f24025183 --- /dev/null +++ b/src/libstd/sys/wasm/stdio.rs @@ -0,0 +1,92 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::{Void, unsupported}; + +pub struct Stdin(Void); +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub fn new() -> io::Result { + unsupported() + } + + pub fn read(&self, _data: &mut [u8]) -> io::Result { + match self.0 {} + } +} + +impl Stdout { + pub fn new() -> io::Result { + Ok(Stdout) + } + + pub fn write(&self, data: &[u8]) -> io::Result { + // If runtime debugging is enabled at compile time we'll invoke some + // runtime functions that are defined in our src/etc/wasm32-shim.js + // debugging script. Note that this ffi function call is intended + // *purely* for debugging only and should not be relied upon. + if !super::DEBUG { + return unsupported() + } + extern { + fn rust_wasm_write_stdout(data: *const u8, len: usize); + } + unsafe { + rust_wasm_write_stdout(data.as_ptr(), data.len()) + } + Ok(data.len()) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result { + Ok(Stderr) + } + + pub fn write(&self, data: &[u8]) -> io::Result { + // See comments in stdout for what's going on here. + if !super::DEBUG { + return unsupported() + } + extern { + fn rust_wasm_write_stderr(data: *const u8, len: usize); + } + unsafe { + rust_wasm_write_stderr(data.as_ptr(), data.len()) + } + Ok(data.len()) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + (&*self).write(data) + } + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs new file mode 100644 index 0000000000000..13980e0cc19d1 --- /dev/null +++ b/src/libstd/sys/wasm/thread.rs @@ -0,0 +1,48 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::boxed::FnBox; +use ffi::CStr; +use io; +use sys::{unsupported, Void}; +use time::Duration; + +pub struct Thread(Void); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + pub unsafe fn new<'a>(_stack: usize, _p: Box) + -> io::Result + { + unsupported() + } + + pub fn yield_now() { + // do nothing + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(_dur: Duration) { + panic!("can't sleep"); + } + + pub fn join(self) { + match self.0 {} + } +} + +pub mod guard { + pub unsafe fn current() -> Option { None } + pub unsafe fn init() -> Option { None } +} diff --git a/src/libstd/sys/wasm/thread_local.rs b/src/libstd/sys/wasm/thread_local.rs new file mode 100644 index 0000000000000..442dd3302a058 --- /dev/null +++ b/src/libstd/sys/wasm/thread_local.rs @@ -0,0 +1,50 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use boxed::Box; +use ptr; + +pub type Key = usize; + +struct Allocated { + value: *mut u8, + dtor: Option, +} + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + Box::into_raw(Box::new(Allocated { + value: ptr::null_mut(), + dtor, + })) as usize +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + (*(key as *mut Allocated)).value = value; +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + (*(key as *mut Allocated)).value +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let key = Box::from_raw(key as *mut Allocated); + if let Some(f) = key.dtor { + f(key.value); + } +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/wasm/time.rs new file mode 100644 index 0000000000000..7907720e4dac6 --- /dev/null +++ b/src/libstd/sys/wasm/time.rs @@ -0,0 +1,63 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Instant; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SystemTime; + +pub const UNIX_EPOCH: SystemTime = SystemTime; + +impl Instant { + pub fn now() -> Instant { + panic!("not supported on web assembly"); + } + + pub fn sub_instant(&self, _other: &Instant) -> Duration { + panic!("can't sub yet"); + } + + pub fn add_duration(&self, _other: &Duration) -> Instant { + panic!("can't add yet"); + } + + pub fn sub_duration(&self, _other: &Duration) -> Instant { + panic!("can't sub yet"); + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + panic!("not supported on web assembly"); + } + + pub fn sub_time(&self, _other: &SystemTime) + -> Result { + panic!() + } + + pub fn add_duration(&self, _other: &Duration) -> SystemTime { + panic!() + } + + pub fn sub_duration(&self, _other: &Duration) -> SystemTime { + panic!() + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + panic!() + } +} diff --git a/src/libstd/sys/windows/backtrace/mod.rs b/src/libstd/sys/windows/backtrace/mod.rs index 26b4cb90e0a89..176891fff23f8 100644 --- a/src/libstd/sys/windows/backtrace/mod.rs +++ b/src/libstd/sys/windows/backtrace/mod.rs @@ -95,8 +95,8 @@ pub fn unwind_backtrace(frames: &mut [Frame]) frame.AddrReturn.Offset == 0 { break } frames[i] = Frame { - symbol_addr: (addr - 1) as *const c_void, - exact_position: (addr - 1) as *const c_void, + symbol_addr: (addr - 1) as *const u8, + exact_position: (addr - 1) as *const u8, }; i += 1; } diff --git a/src/libstd/sys/windows/backtrace/printing/msvc.rs b/src/libstd/sys/windows/backtrace/printing/msvc.rs index 3107d78432413..5a49b77af8e75 100644 --- a/src/libstd/sys/windows/backtrace/printing/msvc.rs +++ b/src/libstd/sys/windows/backtrace/printing/msvc.rs @@ -10,7 +10,7 @@ use ffi::CStr; use io; -use libc::{c_ulong, c_int, c_char}; +use libc::{c_ulong, c_char}; use mem; use sys::c; use sys::backtrace::BacktraceContext; @@ -59,7 +59,7 @@ pub fn foreach_symbol_fileline(frame: Frame, mut f: F, context: &BacktraceContext) -> io::Result - where F: FnMut(&[u8], c_int) -> io::Result<()> + where F: FnMut(&[u8], u32) -> io::Result<()> { let SymGetLineFromAddr64 = sym!(&context.dbghelp, "SymGetLineFromAddr64", @@ -76,7 +76,7 @@ pub fn foreach_symbol_fileline(frame: Frame, &mut line); if ret == c::TRUE { let name = CStr::from_ptr(line.Filename).to_bytes(); - f(name, line.LineNumber as c_int)?; + f(name, line.LineNumber as u32)?; } Ok(false) } diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 9535ddfe5cada..6e0cccff00193 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -37,7 +37,6 @@ pub type BOOL = c_int; pub type BYTE = u8; pub type BOOLEAN = BYTE; pub type GROUP = c_uint; -pub type LONG_PTR = isize; pub type LARGE_INTEGER = c_longlong; pub type LONG = c_long; pub type UINT = c_uint; @@ -46,7 +45,6 @@ pub type USHORT = c_ushort; pub type SIZE_T = usize; pub type WORD = u16; pub type CHAR = c_char; -pub type HCRYPTPROV = LONG_PTR; pub type ULONG_PTR = usize; pub type ULONG = c_ulong; #[cfg(target_arch = "x86_64")] @@ -279,16 +277,15 @@ pub const WAIT_TIMEOUT: DWORD = 258; pub const WAIT_FAILED: DWORD = 0xFFFFFFFF; #[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] pub const MAX_SYM_NAME: usize = 2000; #[cfg(target_arch = "x86")] +#[cfg(feature = "backtrace")] pub const IMAGE_FILE_MACHINE_I386: DWORD = 0x014c; #[cfg(target_arch = "x86_64")] +#[cfg(feature = "backtrace")] pub const IMAGE_FILE_MACHINE_AMD64: DWORD = 0x8664; -pub const PROV_RSA_FULL: DWORD = 1; -pub const CRYPT_SILENT: DWORD = 64; -pub const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; - pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; @@ -575,6 +572,7 @@ pub struct OVERLAPPED { #[repr(C)] #[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] pub struct SYMBOL_INFO { pub SizeOfStruct: c_ulong, pub TypeIndex: c_ulong, @@ -598,6 +596,7 @@ pub struct SYMBOL_INFO { #[repr(C)] #[cfg(target_env = "msvc")] +#[cfg(feature = "backtrace")] pub struct IMAGEHLP_LINE64 { pub SizeOfStruct: u32, pub Key: *const c_void, @@ -616,6 +615,7 @@ pub enum ADDRESS_MODE { } #[repr(C)] +#[cfg(feature = "backtrace")] pub struct ADDRESS64 { pub Offset: u64, pub Segment: u16, @@ -623,6 +623,7 @@ pub struct ADDRESS64 { } #[repr(C)] +#[cfg(feature = "backtrace")] pub struct STACKFRAME64 { pub AddrPC: ADDRESS64, pub AddrReturn: ADDRESS64, @@ -638,6 +639,7 @@ pub struct STACKFRAME64 { } #[repr(C)] +#[cfg(feature = "backtrace")] pub struct KDHELP64 { pub Thread: u64, pub ThCallbackStack: DWORD, @@ -1089,6 +1091,7 @@ extern "system" { pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW) -> BOOL; pub fn FindClose(findFile: HANDLE) -> BOOL; + #[cfg(feature = "backtrace")] pub fn RtlCaptureContext(ctx: *mut CONTEXT); pub fn getsockopt(s: SOCKET, level: c_int, @@ -1120,20 +1123,13 @@ extern "system" { res: *mut *mut ADDRINFOA) -> c_int; pub fn freeaddrinfo(res: *mut ADDRINFOA); + #[cfg(feature = "backtrace")] pub fn LoadLibraryW(name: LPCWSTR) -> HMODULE; + #[cfg(feature = "backtrace")] pub fn FreeLibrary(handle: HMODULE) -> BOOL; pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; - pub fn CryptAcquireContextA(phProv: *mut HCRYPTPROV, - pszContainer: LPCSTR, - pszProvider: LPCSTR, - dwProvType: DWORD, - dwFlags: DWORD) -> BOOL; - pub fn CryptGenRandom(hProv: HCRYPTPROV, - dwLen: DWORD, - pbBuffer: *mut BYTE) -> BOOL; - pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); @@ -1164,6 +1160,9 @@ extern "system" { writefds: *mut fd_set, exceptfds: *mut fd_set, timeout: *const timeval) -> c_int; + + #[link_name = "SystemFunction036"] + pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; } // Functions that aren't available on every version of Windows that we support, @@ -1229,7 +1228,7 @@ compat_fn! { } } -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", feature = "backtrace"))] mod gnu { use super::*; @@ -1257,5 +1256,5 @@ mod gnu { } } -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", feature = "backtrace"))] pub use self::gnu::*; diff --git a/src/libstd/sys/windows/cmath.rs b/src/libstd/sys/windows/cmath.rs new file mode 100644 index 0000000000000..b665a2c9ba4db --- /dev/null +++ b/src/libstd/sys/windows/cmath.rs @@ -0,0 +1,103 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +use libc::{c_float, c_double}; + +#[link_name = "m"] +extern { + pub fn acos(n: c_double) -> c_double; + pub fn asin(n: c_double) -> c_double; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn tan(n: c_double) -> c_double; + pub fn tanh(n: c_double) -> c_double; +} + +pub use self::shims::*; + +#[cfg(not(target_env = "msvc"))] +mod shims { + use libc::c_float; + + extern { + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +// On MSVC these functions aren't defined, so we just define shims which promote +// everything fo f64, perform the calculation, and then demote back to f32. +// While not precisely correct should be "correct enough" for now. +#[cfg(target_env = "msvc")] +mod shims { + use libc::c_float; + + #[inline] + pub unsafe fn acosf(n: c_float) -> c_float { + f64::acos(n as f64) as c_float + } + + #[inline] + pub unsafe fn asinf(n: c_float) -> c_float { + f64::asin(n as f64) as c_float + } + + #[inline] + pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { + f64::atan2(n as f64, b as f64) as c_float + } + + #[inline] + pub unsafe fn atanf(n: c_float) -> c_float { + f64::atan(n as f64) as c_float + } + + #[inline] + pub unsafe fn coshf(n: c_float) -> c_float { + f64::cosh(n as f64) as c_float + } + + #[inline] + pub unsafe fn sinhf(n: c_float) -> c_float { + f64::sinh(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanf(n: c_float) -> c_float { + f64::tan(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanhf(n: c_float) -> c_float { + f64::tanh(n as f64) as c_float + } +} diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs index 3f6c2827a3f93..d6b8896ac096d 100644 --- a/src/libstd/sys/windows/ext/ffi.rs +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -9,6 +9,62 @@ // except according to those terms. //! Windows-specific extensions to the primitives in the `std::ffi` module. +//! +//! # Overview +//! +//! For historical reasons, the Windows API uses a form of potentially +//! ill-formed UTF-16 encoding for strings. Specifically, the 16-bit +//! code units in Windows strings may contain [isolated surrogate code +//! points which are not paired together][ill-formed-utf-16]. The +//! Unicode standard requires that surrogate code points (those in the +//! range U+D800 to U+DFFF) always be *paired*, because in the UTF-16 +//! encoding a *surrogate code unit pair* is used to encode a single +//! character. For compatibility with code that does not enforce +//! these pairings, Windows does not enforce them, either. +//! +//! While it is not always possible to convert such a string losslessly into +//! a valid UTF-16 string (or even UTF-8), it is often desirable to be +//! able to round-trip such a string from and to Windows APIs +//! losslessly. For example, some Rust code may be "bridging" some +//! Windows APIs together, just passing `WCHAR` strings among those +//! APIs without ever really looking into the strings. +//! +//! If Rust code *does* need to look into those strings, it can +//! convert them to valid UTF-8, possibly lossily, by substituting +//! invalid sequences with U+FFFD REPLACEMENT CHARACTER, as is +//! conventionally done in other Rust APIs that deal with string +//! encodings. +//! +//! # `OsStringExt` and `OsStrExt` +//! +//! [`OsString`] is the Rust wrapper for owned strings in the +//! preferred representation of the operating system. On Windows, +//! this struct gets augmented with an implementation of the +//! [`OsStringExt`] trait, which has a [`from_wide`] method. This +//! lets you create an [`OsString`] from a `&[u16]` slice; presumably +//! you get such a slice out of a `WCHAR` Windows API. +//! +//! Similarly, [`OsStr`] is the Rust wrapper for borrowed strings from +//! preferred representation of the operating system. On Windows, the +//! [`OsStrExt`] trait provides the [`encode_wide`] method, which +//! outputs an [`EncodeWide`] iterator. You can [`collect`] this +//! iterator, for example, to obtain a `Vec`; you can later get a +//! pointer to this vector's contents and feed it to Windows APIs. +//! +//! These traits, along with [`OsString`] and [`OsStr`], work in +//! conjunction so that it is possible to **round-trip** strings from +//! Windows and back, with no loss of data, even if the strings are +//! ill-formed UTF-16. +//! +//! [ill-formed-utf-16]: https://simonsapin.github.io/wtf-8/#ill-formed-utf-16 +//! [`OsString`]: ../../../ffi/struct.OsString.html +//! [`OsStr`]: ../../../ffi/struct.OsStr.html +//! [`OsStringExt`]: trait.OsStringExt.html +//! [`OsStrExt`]: trait.OsStrExt.html +//! [`EncodeWide`]: struct.EncodeWide.html +//! [`from_wide`]: trait.OsStringExt.html#tymethod.from_wide +//! [`encode_wide`]: trait.OsStrExt.html#tymethod.encode_wide +//! [`collect`]: ../../../iter/trait.Iterator.html#method.collect #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index d58a3505154dd..24c41046f263a 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -32,7 +32,7 @@ pub trait FileExt { /// function, it is set to the end of the read. /// /// Reading beyond the end of the file will always return with a length of - /// 0. + /// 0\. /// /// Note that similar to `File::read`, it is not an error to return with a /// short read. When returning from such a short read, the file pointer is @@ -393,8 +393,8 @@ pub trait MetadataExt { /// to. For a directory, the structure specifies when the directory was /// created. /// - /// If the underlying filesystem does not support the last write time - /// time, the returned value is 0. + /// If the underlying filesystem does not support the last write time, + /// the returned value is 0. /// /// # Examples /// diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index f2487c1b0bd0a..ae9535139d997 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -722,16 +722,16 @@ pub fn canonicalize(p: &Path) -> io::Result { pub fn copy(from: &Path, to: &Path) -> io::Result { unsafe extern "system" fn callback( _TotalFileSize: c::LARGE_INTEGER, - TotalBytesTransferred: c::LARGE_INTEGER, + _TotalBytesTransferred: c::LARGE_INTEGER, _StreamSize: c::LARGE_INTEGER, - _StreamBytesTransferred: c::LARGE_INTEGER, - _dwStreamNumber: c::DWORD, + StreamBytesTransferred: c::LARGE_INTEGER, + dwStreamNumber: c::DWORD, _dwCallbackReason: c::DWORD, _hSourceFile: c::HANDLE, _hDestinationFile: c::HANDLE, lpData: c::LPVOID, ) -> c::DWORD { - *(lpData as *mut i64) = TotalBytesTransferred; + if dwStreamNumber == 1 {*(lpData as *mut i64) = StreamBytesTransferred;} c::PROGRESS_CONTINUE } let pfrom = to_u16s(from)?; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index ee58efc5144a8..0d12ecf8fe3a1 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -17,12 +17,18 @@ use os::windows::ffi::{OsStrExt, OsStringExt}; use path::PathBuf; use time::Duration; +pub use libc::strlen; +pub use self::rand::hashmap_random_keys; + #[macro_use] pub mod compat; pub mod args; +#[cfg(feature = "backtrace")] pub mod backtrace; pub mod c; +pub mod cmath; pub mod condvar; +#[cfg(feature = "backtrace")] pub mod dynamic_lib; pub mod env; pub mod ext; diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index a51b458451e86..b94482435597e 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -318,6 +318,10 @@ pub fn exit(code: i32) -> ! { unsafe { c::ExitProcess(code as c::UINT) } } +pub fn getpid() -> u32 { + unsafe { c::GetCurrentProcessId() as u32 } +} + #[cfg(test)] mod tests { use io::Error; diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index 3eb4582718b51..b8d2f7bc53ce7 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -15,6 +15,8 @@ use borrow::Cow; use fmt; use sys_common::wtf8::{Wtf8, Wtf8Buf}; use mem; +use rc::Rc; +use sync::Arc; use sys_common::{AsInner, IntoInner}; #[derive(Clone, Hash)] @@ -115,6 +117,16 @@ impl Buf { let inner: Box = unsafe { mem::transmute(boxed) }; Buf { inner: Wtf8Buf::from_box(inner) } } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } } impl Slice { @@ -144,4 +156,16 @@ impl Slice { pub fn empty_box() -> Box { unsafe { mem::transmute(Wtf8::empty_box()) } } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc = self.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc = self.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } } diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 2b47808451bc2..98d62a0c953a6 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ascii::*; - use path::Prefix; use ffi::OsStr; use mem; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 452d720ce5933..f3b1185c6ea91 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -15,11 +15,13 @@ use io; use mem; use path::Path; use ptr; -use rand::{self, Rng}; use slice; +use sync::atomic::Ordering::SeqCst; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; use sys::c; use sys::fs::{File, OpenOptions}; use sys::handle::Handle; +use sys::hashmap_random_keys; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -71,10 +73,9 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result { let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS; loop { tries += 1; - let key: u64 = rand::thread_rng().gen(); name = format!(r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", c::GetCurrentProcessId(), - key); + random_number()); let wide_name = OsStr::new(&name) .encode_wide() .chain(Some(0)) @@ -156,6 +157,17 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result { } } +fn random_number() -> usize { + static N: AtomicUsize = ATOMIC_USIZE_INIT; + loop { + if N.load(SeqCst) != 0 { + return N.fetch_add(1, SeqCst) + } + + N.store(hashmap_random_keys().0 as usize, SeqCst); + } +} + impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner } pub fn into_handle(self) -> Handle { self.inner } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 0d1766d5aec6d..631d69b05e115 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ascii::*; +use ascii::AsciiExt; use collections::HashMap; use collections; use env::split_paths; diff --git a/src/libstd/sys/windows/rand.rs b/src/libstd/sys/windows/rand.rs index 10e3d45f9d5eb..262323656aa8c 100644 --- a/src/libstd/sys/windows/rand.rs +++ b/src/libstd/sys/windows/rand.rs @@ -8,69 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use io; use mem; -use rand::Rng; use sys::c; -pub struct OsRng { - hcryptprov: c::HCRYPTPROV -} - -impl OsRng { - /// Create a new `OsRng`. - pub fn new() -> io::Result { - let mut hcp = 0; - let ret = unsafe { - c::CryptAcquireContextA(&mut hcp, 0 as c::LPCSTR, 0 as c::LPCSTR, - c::PROV_RSA_FULL, - c::CRYPT_VERIFYCONTEXT | c::CRYPT_SILENT) - }; - - if ret == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(OsRng { hcryptprov: hcp }) - } - } -} - -impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - let mut v = [0; 4]; - self.fill_bytes(&mut v); - unsafe { mem::transmute(v) } - } - fn next_u64(&mut self) -> u64 { - let mut v = [0; 8]; - self.fill_bytes(&mut v); - unsafe { mem::transmute(v) } - } - fn fill_bytes(&mut self, v: &mut [u8]) { - // CryptGenRandom takes a DWORD (u32) for the length so we need to - // split up the buffer. - for slice in v.chunks_mut(::max_value() as usize) { - let ret = unsafe { - c::CryptGenRandom(self.hcryptprov, slice.len() as c::DWORD, - slice.as_mut_ptr()) - }; - if ret == 0 { - panic!("couldn't generate random bytes: {}", - io::Error::last_os_error()); - } - } - } -} - -impl Drop for OsRng { - fn drop(&mut self) { - let ret = unsafe { - c::CryptReleaseContext(self.hcryptprov, 0) - }; - if ret == 0 { - panic!("couldn't release context: {}", - io::Error::last_os_error()); - } +pub fn hashmap_random_keys() -> (u64, u64) { + let mut v = (0, 0); + let ret = unsafe { + c::RtlGenRandom(&mut v as *mut _ as *mut u8, + mem::size_of_val(&v) as c::ULONG) + }; + if ret == 0 { + panic!("couldn't generate random bytes: {}", + io::Error::last_os_error()); } + return v } diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index b5e5b5760f21b..b43df20bddd08 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -218,7 +218,10 @@ fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CO const CTRL_Z: u8 = 0x1A; const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A -pub const EBADF_ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32; +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) +} + // The default buffer capacity is 64k, but apparently windows // doesn't like 64k reads on stdin. See #13304 for details, but the // idea is that on windows we use a slightly smaller buffer that's diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index c47baaa243402..74786d092855f 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -52,7 +52,7 @@ impl Thread { }; extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { - unsafe { start_thread(main); } + unsafe { start_thread(main as *mut u8); } 0 } } diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 7ae9ed917bdba..cdad320e12241 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -200,8 +200,9 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) { // the address of the symbol to ensure it sticks around. #[link_section = ".CRT$XLB"] -#[linkage = "external"] #[allow(dead_code, unused_variables)] +#[used] // we don't want LLVM eliminating this symbol for any reason, and + // when the symbol makes it to the linker the linker will take over pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = on_tls_callback; diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 617218fe7a5a6..b5cf6d7d34fcc 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -8,15 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg_attr(target_os = "nacl", allow(dead_code))] - /// Common code for printing the backtrace in the same way across the different /// supported platforms. use env; use io::prelude::*; use io; -use libc; use str; use sync::atomic::{self, Ordering}; use path::{self, Path}; @@ -41,9 +38,9 @@ pub const HEX_WIDTH: usize = 10; #[derive(Debug, Copy, Clone)] pub struct Frame { /// Exact address of the call that failed. - pub exact_position: *const libc::c_void, + pub exact_position: *const u8, /// Address of the enclosing function. - pub symbol_addr: *const libc::c_void, + pub symbol_addr: *const u8, } /// Max number of frames to print. @@ -203,8 +200,10 @@ fn output(w: &mut Write, idx: usize, frame: Frame, /// /// See also `output`. #[allow(dead_code)] -fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, - format: PrintFormat) -> io::Result<()> { +fn output_fileline(w: &mut Write, + file: &[u8], + line: u32, + format: PrintFormat) -> io::Result<()> { // prior line: " ##: {:2$} - func" w.write_all(b"")?; match format { @@ -253,8 +252,26 @@ fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, // Note that this demangler isn't quite as fancy as it could be. We have lots // of other information in our symbols like hashes, version, type information, // etc. Additionally, this doesn't handle glue symbols at all. -pub fn demangle(writer: &mut Write, s: &str, format: PrintFormat) -> io::Result<()> { - // First validate the symbol. If it doesn't look like anything we're +pub fn demangle(writer: &mut Write, mut s: &str, format: PrintFormat) -> io::Result<()> { + // During ThinLTO LLVM may import and rename internal symbols, so strip out + // those endings first as they're one of the last manglings applied to + // symbol names. + let llvm = ".llvm."; + if let Some(i) = s.find(llvm) { + let candidate = &s[i + llvm.len()..]; + let all_hex = candidate.chars().all(|c| { + match c { + 'A' ... 'F' | '0' ... '9' => true, + _ => false, + } + }); + + if all_hex { + s = &s[..i]; + } + } + + // Validate the symbol. If it doesn't look like anything we're // expecting, we just print it literally. Note that we must handle non-rust // symbols because we could have any function in the backtrace. let mut valid = true; diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs index 016c840d1541a..75c6bd5d2a2ba 100644 --- a/src/libstd/sys_common/gnu/libbacktrace.rs +++ b/src/libstd/sys_common/gnu/libbacktrace.rs @@ -20,13 +20,13 @@ use sys_common::backtrace::Frame; pub fn foreach_symbol_fileline(frame: Frame, mut f: F, _: &BacktraceContext) -> io::Result -where F: FnMut(&[u8], libc::c_int) -> io::Result<()> +where F: FnMut(&[u8], u32) -> io::Result<()> { // pcinfo may return an arbitrary number of file:line pairs, // in the order of stack trace (i.e. inlined calls first). // in order to avoid allocation, we stack-allocate a fixed size of entries. const FILELINE_SIZE: usize = 32; - let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE]; + let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE]; let ret; let fileline_count = { let state = unsafe { init_state() }; @@ -136,7 +136,7 @@ extern { // helper callbacks //////////////////////////////////////////////////////////////////////// -type FileLine = (*const libc::c_char, libc::c_int); +type FileLine = (*const libc::c_char, u32); extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, _errnum: libc::c_int) { @@ -162,7 +162,7 @@ extern fn pcinfo_cb(data: *mut libc::c_void, // if the buffer is not full, add file:line to the buffer // and adjust the buffer for next possible calls to pcinfo_cb. if !buffer.is_empty() { - buffer[0] = (filename, lineno); + buffer[0] = (filename, lineno as u32); unsafe { ptr::write(slot, &mut buffer[1..]); } } } diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index d7654ce9300b3..14e5697b94e57 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -44,11 +44,15 @@ pub mod thread_local; pub mod util; pub mod wtf8; -#[cfg(any(target_os = "redox", target_os = "l4re"))] -pub use sys::net; - -#[cfg(not(any(target_os = "redox", target_os = "l4re")))] -pub mod net; +cfg_if! { + if #[cfg(any(target_os = "redox", target_os = "l4re"))] { + pub use sys::net; + } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + pub use sys::net; + } else { + pub mod net; + } +} #[cfg(feature = "backtrace")] #[cfg(any(all(unix, not(target_os = "emscripten")), diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index 19dc841b9b502..c76b0bcf1c9ee 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -175,10 +175,15 @@ pub fn lookup_host(host: &str) -> io::Result { }, #[cfg(unix)] Err(e) => { - // The lookup failure could be caused by using a stale /etc/resolv.conf. - // See https://github.com/rust-lang/rust/issues/41570. - // We therefore force a reload of the nameserver information. - c::res_init(); + // If we're running glibc prior to version 2.26, the lookup + // failure could be caused by caching a stale /etc/resolv.conf. + // We need to call libc::res_init() to clear the cache. But we + // shouldn't call it in on any other platform, because other + // res_init implementations aren't thread-safe. See + // https://github.com/rust-lang/rust/issues/41570 and + // https://github.com/rust-lang/rust/issues/43592. + use sys::net::res_init_if_glibc_before_2_26; + let _ = res_init_if_glibc_before_2_26(); Err(e) }, // the cfg is needed here to avoid an "unreachable pattern" warning diff --git a/src/libstd/sys_common/poison.rs b/src/libstd/sys_common/poison.rs index 3c61593acc55b..934ac3edbf1f1 100644 --- a/src/libstd/sys_common/poison.rs +++ b/src/libstd/sys_common/poison.rs @@ -65,6 +65,31 @@ pub struct Guard { /// each lock, but once a lock is poisoned then all future acquisitions will /// return this error. /// +/// # Examples +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// +/// let mutex = Arc::new(Mutex::new(1)); +/// +/// // poison the mutex +/// let c_mutex = mutex.clone(); +/// let _ = thread::spawn(move || { +/// let mut data = c_mutex.lock().unwrap(); +/// *data = 2; +/// panic!(); +/// }).join(); +/// +/// match mutex.lock() { +/// Ok(_) => unreachable!(), +/// Err(p_err) => { +/// let data = p_err.get_ref(); +/// println!("recovered: {}", data); +/// } +/// }; +/// ``` +/// /// [`Mutex`]: ../../std/sync/struct.Mutex.html /// [`RwLock`]: ../../std/sync/struct.RwLock.html #[stable(feature = "rust1", since = "1.0.0")] @@ -72,10 +97,16 @@ pub struct PoisonError { guard: T, } -/// An enumeration of possible errors which can occur while calling the -/// [`try_lock`] method. +/// An enumeration of possible errors associated with a [`TryLockResult`] which +/// can occur while trying to aquire a lock, from the [`try_lock`] method on a +/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. /// +/// [`Mutex`]: struct.Mutex.html +/// [`RwLock`]: struct.RwLock.html +/// [`TryLockResult`]: type.TryLockResult.html /// [`try_lock`]: struct.Mutex.html#method.try_lock +/// [`try_read`]: struct.RwLock.html#method.try_read +/// [`try_write`]: struct.RwLock.html#method.try_write #[stable(feature = "rust1", since = "1.0.0")] pub enum TryLockError { /// The lock could not be acquired because another thread failed while holding @@ -148,6 +179,28 @@ impl PoisonError { /// Consumes this error indicating that a lock is poisoned, returning the /// underlying guard to allow access regardless. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(HashSet::new())); + /// + /// // poison the mutex + /// let c_mutex = mutex.clone(); + /// let _ = thread::spawn(move || { + /// let mut data = c_mutex.lock().unwrap(); + /// data.insert(10); + /// panic!(); + /// }).join(); + /// + /// let p_err = mutex.lock().unwrap_err(); + /// let data = p_err.into_inner(); + /// println!("recovered {} items", data.len()); + /// ``` #[stable(feature = "sync_poison", since = "1.2.0")] pub fn into_inner(self) -> T { self.guard } diff --git a/src/libstd/sys_common/remutex.rs b/src/libstd/sys_common/remutex.rs index 4d0407ccf6c89..ce43ec6d9abf5 100644 --- a/src/libstd/sys_common/remutex.rs +++ b/src/libstd/sys_common/remutex.rs @@ -116,11 +116,18 @@ impl Drop for ReentrantMutex { impl fmt::Debug for ReentrantMutex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_lock() { - Ok(guard) => write!(f, "ReentrantMutex {{ data: {:?} }}", &*guard), + Ok(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), Err(TryLockError::Poisoned(err)) => { - write!(f, "ReentrantMutex {{ data: Poisoned({:?}) }}", &**err.get_ref()) + f.debug_struct("ReentrantMutex").field("data", &**err.get_ref()).finish() }, - Err(TryLockError::WouldBlock) => write!(f, "ReentrantMutex {{ }}") + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("") } + } + + f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() + } } } } diff --git a/src/libstd/sys_common/thread.rs b/src/libstd/sys_common/thread.rs index 87fb34a9dec06..f1379b6ec6375 100644 --- a/src/libstd/sys_common/thread.rs +++ b/src/libstd/sys_common/thread.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use env; use alloc::boxed::FnBox; -use libc; +use env; use sync::atomic::{self, Ordering}; use sys::stack_overflow; use sys::thread as imp; -pub unsafe fn start_thread(main: *mut libc::c_void) { +#[allow(dead_code)] +pub unsafe fn start_thread(main: *mut u8) { // Next, set up our stack overflow handler which may get triggered if we run // out of stack. let _handler = stack_overflow::Handler::new(); diff --git a/src/libstd/sys_common/thread_local.rs b/src/libstd/sys_common/thread_local.rs index 87ffd304e1a33..a4aa3d96d25c0 100644 --- a/src/libstd/sys_common/thread_local.rs +++ b/src/libstd/sys_common/thread_local.rs @@ -262,7 +262,7 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, unsafe extern fn run_dtors(mut ptr: *mut u8) { while !ptr.is_null() { let list: Box = Box::from_raw(ptr as *mut List); - for &(ptr, dtor) in list.iter() { + for (ptr, dtor) in list.into_iter() { dtor(ptr); } ptr = DTORS.get(); diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index b89a73cd28a09..e212b5006f2d1 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -35,8 +35,10 @@ use hash::{Hash, Hasher}; use iter::FromIterator; use mem; use ops; +use rc::Rc; use slice; use str; +use sync::Arc; use sys_common::AsInner; const UTF8_REPLACEMENT_CHARACTER: &'static str = "\u{FFFD}"; @@ -641,6 +643,18 @@ impl Wtf8 { let boxed: Box<[u8]> = Default::default(); unsafe { mem::transmute(boxed) } } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.bytes); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.bytes); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } + } } diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index a53c76a333a99..fcbca38a98f0b 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -190,11 +190,6 @@ macro_rules! __thread_local_inner { } }; ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { - #[cfg(stage0)] - $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!(@key $(#[$attr])* $vis $name, $t, $init); - - #[cfg(not(stage0))] $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = __thread_local_inner!(@key $(#[$attr])* $vis $name, $t, $init); } @@ -325,7 +320,10 @@ impl LocalKey { /// /// Once the initialization expression succeeds, the key transitions to the /// `Valid` state which will guarantee that future calls to [`with`] will - /// succeed within the thread. + /// succeed within the thread. Some keys might skip the `Uninitialized` + /// state altogether and start in the `Valid` state as an optimization + /// (e.g. keys initialized with a constant expression), but no guarantees + /// are made. /// /// When a thread exits, each key will be destroyed in turn, and as keys are /// destroyed they will enter the `Destroyed` state just before the diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 30887b16c6028..ee49bf796b86f 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -25,11 +25,15 @@ //! //! Fatal logic errors in Rust cause *thread panic*, during which //! a thread will unwind the stack, running destructors and freeing -//! owned resources. Thread panic is unrecoverable from within -//! the panicking thread (i.e. there is no 'try/catch' in Rust), but -//! the panic may optionally be detected from a different thread. If -//! the main thread panics, the application will exit with a non-zero -//! exit code. +//! owned resources. While not meant as a 'try/catch' mechanism, panics +//! in Rust can nonetheless be caught (unless compiling with `panic=abort`) with +//! [`catch_unwind`](../../std/panic/fn.catch_unwind.html) and recovered +//! from, or alternatively be resumed with +//! [`resume_unwind`](../../std/panic/fn.resume_unwind.html). If the panic +//! is not caught the thread will exit, but the panic may optionally be +//! detected from a different thread with [`join`]. If the main thread panics +//! without the panic being caught, the application will exit with a +//! non-zero exit code. //! //! When the main thread of a Rust program terminates, the entire program shuts //! down, even if other threads are still running. However, this module provides @@ -171,6 +175,8 @@ use panic; use panicking; use str; use sync::{Mutex, Condvar, Arc}; +use sync::atomic::AtomicUsize; +use sync::atomic::Ordering::SeqCst; use sys::thread as imp; use sys_common::mutex; use sys_common::thread_info; @@ -485,15 +491,17 @@ impl Builder { /// let (tx, rx) = channel(); /// /// let sender = thread::spawn(move || { -/// let _ = tx.send("Hello, thread".to_owned()); +/// tx.send("Hello, thread".to_owned()) +/// .expect("Unable to send on channel"); /// }); /// /// let receiver = thread::spawn(move || { -/// println!("{}", rx.recv().unwrap()); +/// let value = rx.recv().expect("Unable to receive from channel"); +/// println!("{}", value); /// }); /// -/// let _ = sender.join(); -/// let _ = receiver.join(); +/// sender.join().expect("The sender thread has panicked"); +/// receiver.join().expect("The receiver thread has panicked"); /// ``` /// /// A thread can also return a value through its [`JoinHandle`], you can use @@ -692,6 +700,11 @@ pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) } +// constants for park/unpark +const EMPTY: usize = 0; +const PARKED: usize = 1; +const NOTIFIED: usize = 2; + /// Blocks unless or until the current thread's token is made available. /// /// A call to `park` does not guarantee that the thread will remain parked @@ -769,11 +782,27 @@ pub fn sleep(dur: Duration) { #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { let thread = current(); - let mut guard = thread.inner.lock.lock().unwrap(); - while !*guard { - guard = thread.inner.cvar.wait(guard).unwrap(); + + // If we were previously notified then we consume this notification and + // return quickly. + if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + return + } + + // Otherwise we need to coordinate going to sleep + let mut m = thread.inner.lock.lock().unwrap(); + match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + Err(NOTIFIED) => return, // notified after we locked + Err(_) => panic!("inconsistent park state"), + } + loop { + m = thread.inner.cvar.wait(m).unwrap(); + match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { + Ok(_) => return, // got a notification + Err(_) => {} // spurious wakeup, go back to sleep + } } - *guard = false; } /// Use [`park_timeout`]. @@ -840,12 +869,30 @@ pub fn park_timeout_ms(ms: u32) { #[stable(feature = "park_timeout", since = "1.4.0")] pub fn park_timeout(dur: Duration) { let thread = current(); - let mut guard = thread.inner.lock.lock().unwrap(); - if !*guard { - let (g, _) = thread.inner.cvar.wait_timeout(guard, dur).unwrap(); - guard = g; + + // Like `park` above we have a fast path for an already-notified thread, and + // afterwards we start coordinating for a sleep. + // return quickly. + if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + return + } + let m = thread.inner.lock.lock().unwrap(); + match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + Err(NOTIFIED) => return, // notified after we locked + Err(_) => panic!("inconsistent park_timeout state"), + } + + // Wait with a timeout, and if we spuriously wake up or otherwise wake up + // from a notification we just want to unconditionally set the state back to + // empty, either consuming a notification or un-flagging ourselves as + // parked. + let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap(); + match thread.inner.state.swap(EMPTY, SeqCst) { + NOTIFIED => {} // got a notification, hurray! + PARKED => {} // no notification, alas + n => panic!("inconsistent park_timeout state: {}", n), } - *guard = false; } //////////////////////////////////////////////////////////////////////////////// @@ -912,7 +959,10 @@ impl ThreadId { struct Inner { name: Option, // Guaranteed to be UTF-8 id: ThreadId, - lock: Mutex, // true when there is a buffered unpark + + // state for thread park/unpark + state: AtomicUsize, + lock: Mutex<()>, cvar: Condvar, } @@ -956,7 +1006,8 @@ impl Thread { inner: Arc::new(Inner { name: cname, id: ThreadId::new(), - lock: Mutex::new(false), + state: AtomicUsize::new(EMPTY), + lock: Mutex::new(()), cvar: Condvar::new(), }) } @@ -996,10 +1047,22 @@ impl Thread { /// [park]: fn.park.html #[stable(feature = "rust1", since = "1.0.0")] pub fn unpark(&self) { - let mut guard = self.inner.lock.lock().unwrap(); - if !*guard { - *guard = true; - self.inner.cvar.notify_one(); + loop { + match self.inner.state.compare_exchange(EMPTY, NOTIFIED, SeqCst, SeqCst) { + Ok(_) => return, // no one was waiting + Err(NOTIFIED) => return, // already unparked + Err(PARKED) => {} // gotta go wake someone up + _ => panic!("inconsistent state in unpark"), + } + + // Coordinate wakeup through the mutex and a condvar notification + let _lock = self.inner.lock.lock().unwrap(); + match self.inner.state.compare_exchange(PARKED, NOTIFIED, SeqCst, SeqCst) { + Ok(_) => return self.inner.cvar.notify_one(), + Err(NOTIFIED) => return, // a different thread unparked + Err(EMPTY) => {} // parked thread went away, try again + _ => panic!("inconsistent state in unpark"), + } } } @@ -1192,7 +1255,7 @@ impl JoinInner { /// }); /// }); /// -/// let _ = original_thread.join(); +/// original_thread.join().expect("The thread being joined has panicked"); /// println!("Original thread is joined."); /// /// // We make sure that the new thread has time to run, before the main diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index 5c0c7a4fbca35..1c0cdfd8435c3 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -57,6 +57,7 @@ pub use tables::{UnicodeVersion, UNICODE_VERSION}; /// [`to_lowercase`]: ../../std/primitive.char.html#method.to_lowercase /// [`char`]: ../../std/primitive.char.html #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct ToLowercase(CaseMappingIter); #[stable(feature = "rust1", since = "1.0.0")] @@ -78,6 +79,7 @@ impl FusedIterator for ToLowercase {} /// [`to_uppercase`]: ../../std/primitive.char.html#method.to_uppercase /// [`char`]: ../../std/primitive.char.html #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct ToUppercase(CaseMappingIter); #[stable(feature = "rust1", since = "1.0.0")] @@ -91,6 +93,7 @@ impl Iterator for ToUppercase { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for ToUppercase {} +#[derive(Debug)] enum CaseMappingIter { Three(char, char, char), Two(char, char), @@ -923,11 +926,534 @@ impl char { pub fn to_uppercase(self) -> ToUppercase { ToUppercase(CaseMappingIter::new(conversions::to_upper(self))) } + + /// Checks if the value is within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = 'a'; + /// let non_ascii = '❤'; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + *self as u32 <= 0x7F + } + + /// Makes a copy of the value in its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// To uppercase ASCII characters in addition to non-ASCII characters, use + /// [`to_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let ascii = 'a'; + /// let non_ascii = '❤'; + /// + /// assert_eq!('A', ascii.to_ascii_uppercase()); + /// assert_eq!('❤', non_ascii.to_ascii_uppercase()); + /// ``` + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + /// [`to_uppercase`]: #method.to_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> char { + if self.is_ascii() { + (*self as u8).to_ascii_uppercase() as char + } else { + *self + } + } + + /// Makes a copy of the value in its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// To lowercase ASCII characters in addition to non-ASCII characters, use + /// [`to_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let ascii = 'A'; + /// let non_ascii = '❤'; + /// + /// assert_eq!('a', ascii.to_ascii_lowercase()); + /// assert_eq!('❤', non_ascii.to_ascii_lowercase()); + /// ``` + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + /// [`to_lowercase`]: #method.to_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> char { + if self.is_ascii() { + (*self as u8).to_ascii_lowercase() as char + } else { + *self + } + } + + /// Checks that two values are an ASCII case-insensitive match. + /// + /// Equivalent to `to_ascii_lowercase(a) == to_ascii_lowercase(b)`. + /// + /// # Examples + /// + /// ``` + /// let upper_a = 'A'; + /// let lower_a = 'a'; + /// let lower_z = 'z'; + /// + /// assert!(upper_a.eq_ignore_ascii_case(&lower_a)); + /// assert!(upper_a.eq_ignore_ascii_case(&upper_a)); + /// assert!(!upper_a.eq_ignore_ascii_case(&lower_z)); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &char) -> bool { + self.to_ascii_lowercase() == other.to_ascii_lowercase() + } + + /// Converts this type to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut ascii = 'a'; + /// + /// ascii.make_ascii_uppercase(); + /// + /// assert_eq!('A', ascii); + /// ``` + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + *self = self.to_ascii_uppercase(); + } + + /// Converts this type to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut ascii = 'A'; + /// + /// ascii.make_ascii_lowercase(); + /// + /// assert_eq!('a', ascii); + /// ``` + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.21.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + *self = self.to_ascii_lowercase(); + } + + /// Checks if the value is an ASCII alphabetic character: + /// + /// - U+0041 'A' ... U+005A 'Z', or + /// - U+0061 'a' ... U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(uppercase_a.is_ascii_alphabetic()); + /// assert!(uppercase_g.is_ascii_alphabetic()); + /// assert!(a.is_ascii_alphabetic()); + /// assert!(g.is_ascii_alphabetic()); + /// assert!(!zero.is_ascii_alphabetic()); + /// assert!(!percent.is_ascii_alphabetic()); + /// assert!(!space.is_ascii_alphabetic()); + /// assert!(!lf.is_ascii_alphabetic()); + /// assert!(!esc.is_ascii_alphabetic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_alphabetic(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_alphabetic() + } + + /// Checks if the value is an ASCII uppercase character: + /// U+0041 'A' ... U+005A 'Z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(uppercase_a.is_ascii_uppercase()); + /// assert!(uppercase_g.is_ascii_uppercase()); + /// assert!(!a.is_ascii_uppercase()); + /// assert!(!g.is_ascii_uppercase()); + /// assert!(!zero.is_ascii_uppercase()); + /// assert!(!percent.is_ascii_uppercase()); + /// assert!(!space.is_ascii_uppercase()); + /// assert!(!lf.is_ascii_uppercase()); + /// assert!(!esc.is_ascii_uppercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_uppercase(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_uppercase() + } + + /// Checks if the value is an ASCII lowercase character: + /// U+0061 'a' ... U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(!uppercase_a.is_ascii_lowercase()); + /// assert!(!uppercase_g.is_ascii_lowercase()); + /// assert!(a.is_ascii_lowercase()); + /// assert!(g.is_ascii_lowercase()); + /// assert!(!zero.is_ascii_lowercase()); + /// assert!(!percent.is_ascii_lowercase()); + /// assert!(!space.is_ascii_lowercase()); + /// assert!(!lf.is_ascii_lowercase()); + /// assert!(!esc.is_ascii_lowercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_lowercase(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_lowercase() + } + + /// Checks if the value is an ASCII alphanumeric character: + /// + /// - U+0041 'A' ... U+005A 'Z', or + /// - U+0061 'a' ... U+007A 'z', or + /// - U+0030 '0' ... U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(uppercase_a.is_ascii_alphanumeric()); + /// assert!(uppercase_g.is_ascii_alphanumeric()); + /// assert!(a.is_ascii_alphanumeric()); + /// assert!(g.is_ascii_alphanumeric()); + /// assert!(zero.is_ascii_alphanumeric()); + /// assert!(!percent.is_ascii_alphanumeric()); + /// assert!(!space.is_ascii_alphanumeric()); + /// assert!(!lf.is_ascii_alphanumeric()); + /// assert!(!esc.is_ascii_alphanumeric()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_alphanumeric(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_alphanumeric() + } + + /// Checks if the value is an ASCII decimal digit: + /// U+0030 '0' ... U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(!uppercase_a.is_ascii_digit()); + /// assert!(!uppercase_g.is_ascii_digit()); + /// assert!(!a.is_ascii_digit()); + /// assert!(!g.is_ascii_digit()); + /// assert!(zero.is_ascii_digit()); + /// assert!(!percent.is_ascii_digit()); + /// assert!(!space.is_ascii_digit()); + /// assert!(!lf.is_ascii_digit()); + /// assert!(!esc.is_ascii_digit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_digit(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_digit() + } + + /// Checks if the value is an ASCII hexadecimal digit: + /// + /// - U+0030 '0' ... U+0039 '9', or + /// - U+0041 'A' ... U+0046 'F', or + /// - U+0061 'a' ... U+0066 'f'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(uppercase_a.is_ascii_hexdigit()); + /// assert!(!uppercase_g.is_ascii_hexdigit()); + /// assert!(a.is_ascii_hexdigit()); + /// assert!(!g.is_ascii_hexdigit()); + /// assert!(zero.is_ascii_hexdigit()); + /// assert!(!percent.is_ascii_hexdigit()); + /// assert!(!space.is_ascii_hexdigit()); + /// assert!(!lf.is_ascii_hexdigit()); + /// assert!(!esc.is_ascii_hexdigit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_hexdigit(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_hexdigit() + } + + /// Checks if the value is an ASCII punctuation character: + /// + /// - U+0021 ... U+002F `! " # $ % & ' ( ) * + , - . /`, or + /// - U+003A ... U+0040 `: ; < = > ? @`, or + /// - U+005B ... U+0060 ``[ \ ] ^ _ ` ``, or + /// - U+007B ... U+007E `{ | } ~` + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(!uppercase_a.is_ascii_punctuation()); + /// assert!(!uppercase_g.is_ascii_punctuation()); + /// assert!(!a.is_ascii_punctuation()); + /// assert!(!g.is_ascii_punctuation()); + /// assert!(!zero.is_ascii_punctuation()); + /// assert!(percent.is_ascii_punctuation()); + /// assert!(!space.is_ascii_punctuation()); + /// assert!(!lf.is_ascii_punctuation()); + /// assert!(!esc.is_ascii_punctuation()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_punctuation(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_punctuation() + } + + /// Checks if the value is an ASCII graphic character: + /// U+0021 '@' ... U+007E '~'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(uppercase_a.is_ascii_graphic()); + /// assert!(uppercase_g.is_ascii_graphic()); + /// assert!(a.is_ascii_graphic()); + /// assert!(g.is_ascii_graphic()); + /// assert!(zero.is_ascii_graphic()); + /// assert!(percent.is_ascii_graphic()); + /// assert!(!space.is_ascii_graphic()); + /// assert!(!lf.is_ascii_graphic()); + /// assert!(!esc.is_ascii_graphic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_graphic(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_graphic() + } + + /// Checks if the value is an ASCII whitespace character: + /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, + /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// U+000B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(!uppercase_a.is_ascii_whitespace()); + /// assert!(!uppercase_g.is_ascii_whitespace()); + /// assert!(!a.is_ascii_whitespace()); + /// assert!(!g.is_ascii_whitespace()); + /// assert!(!zero.is_ascii_whitespace()); + /// assert!(!percent.is_ascii_whitespace()); + /// assert!(space.is_ascii_whitespace()); + /// assert!(lf.is_ascii_whitespace()); + /// assert!(!esc.is_ascii_whitespace()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_whitespace(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_whitespace() + } + + /// Checks if the value is an ASCII control character: + /// U+0000 NUL ... U+001F UNIT SEPARATOR, or U+007F DELETE. + /// Note that most ASCII whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// + /// let uppercase_a = 'A'; + /// let uppercase_g = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc: char = 0x1b_u8.into(); + /// + /// assert!(!uppercase_a.is_ascii_control()); + /// assert!(!uppercase_g.is_ascii_control()); + /// assert!(!a.is_ascii_control()); + /// assert!(!g.is_ascii_control()); + /// assert!(!zero.is_ascii_control()); + /// assert!(!percent.is_ascii_control()); + /// assert!(!space.is_ascii_control()); + /// assert!(lf.is_ascii_control()); + /// assert!(esc.is_ascii_control()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[inline] + pub fn is_ascii_control(&self) -> bool { + self.is_ascii() && (*self as u8).is_ascii_control() + } } /// An iterator that decodes UTF-16 encoded code points from an iterator of `u16`s. #[stable(feature = "decode_utf16", since = "1.9.0")] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct DecodeUtf16 where I: Iterator { @@ -935,7 +1461,7 @@ pub struct DecodeUtf16 buf: Option, } -/// An iterator that decodes UTF-16 encoded code points from an iterator of `u16`s. +/// An error that can be returned when decoding UTF-16 code points. #[stable(feature = "decode_utf16", since = "1.9.0")] #[derive(Debug, Clone, Eq, PartialEq)] pub struct DecodeUtf16Error { diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index e5a114caed0f4..22f8bdab2f7b5 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -28,8 +28,10 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![deny(warnings)] +#![deny(missing_debug_implementations)] #![no_std] +#![feature(ascii_ctype)] #![feature(core_char_ext)] #![feature(str_internals)] #![feature(decode_utf8)] diff --git a/src/libstd_unicode/lossy.rs b/src/libstd_unicode/lossy.rs index 253dcb6a15951..cc8e93308a527 100644 --- a/src/libstd_unicode/lossy.rs +++ b/src/libstd_unicode/lossy.rs @@ -38,6 +38,7 @@ impl Utf8Lossy { /// Iterator over lossy UTF-8 string #[unstable(feature = "str_internals", issue = "0")] +#[allow(missing_debug_implementations)] pub struct Utf8LossyChunksIter<'a> { source: &'a [u8], } diff --git a/src/libstd_unicode/u_str.rs b/src/libstd_unicode/u_str.rs index 0046e3f7bd093..5d1611acb7ee6 100644 --- a/src/libstd_unicode/u_str.rs +++ b/src/libstd_unicode/u_str.rs @@ -76,6 +76,7 @@ impl UnicodeStr for str { /// Iterator adaptor for encoding `char`s to UTF-16. #[derive(Clone)] +#[allow(missing_debug_implementations)] pub struct Utf16Encoder { chars: I, extra: u16, diff --git a/src/libstd_unicode/unicode.py b/src/libstd_unicode/unicode.py index 1fac859242eab..df79760894e30 100755 --- a/src/libstd_unicode/unicode.py +++ b/src/libstd_unicode/unicode.py @@ -89,7 +89,7 @@ def load_unicode_data(f): if is_surrogate(cp): continue if range_start >= 0: - for i in xrange(range_start, cp): + for i in range(range_start, cp): udict[i] = data range_start = -1 if data[1].endswith(", First>"): @@ -382,7 +382,7 @@ def compute_trie(rawdata, chunksize): root = [] childmap = {} child_data = [] - for i in range(len(rawdata) / chunksize): + for i in range(len(rawdata) // chunksize): data = rawdata[i * chunksize: (i + 1) * chunksize] child = '|'.join(map(str, data)) if child not in childmap: @@ -400,7 +400,7 @@ def emit_bool_trie(f, name, t_data, is_pub=True): # convert to bitmap chunks of 64 bits each chunks = [] - for i in range(0x110000 / CHUNK): + for i in range(0x110000 // CHUNK): chunk = 0 for j in range(64): if rawdata[i * 64 + j]: @@ -412,12 +412,12 @@ def emit_bool_trie(f, name, t_data, is_pub=True): pub_string = "pub " f.write(" %sconst %s: &'static super::BoolTrie = &super::BoolTrie {\n" % (pub_string, name)) f.write(" r1: [\n") - data = ','.join('0x%016x' % chunk for chunk in chunks[0:0x800 / CHUNK]) + data = ','.join('0x%016x' % chunk for chunk in chunks[0:0x800 // CHUNK]) format_table_content(f, data, 12) f.write("\n ],\n") # 0x800..0x10000 trie - (r2, r3) = compute_trie(chunks[0x800 / CHUNK : 0x10000 / CHUNK], 64 / CHUNK) + (r2, r3) = compute_trie(chunks[0x800 // CHUNK : 0x10000 // CHUNK], 64 // CHUNK) f.write(" r2: [\n") data = ','.join(str(node) for node in r2) format_table_content(f, data, 12) @@ -428,7 +428,7 @@ def emit_bool_trie(f, name, t_data, is_pub=True): f.write("\n ],\n") # 0x10000..0x110000 trie - (mid, r6) = compute_trie(chunks[0x10000 / CHUNK : 0x110000 / CHUNK], 64 / CHUNK) + (mid, r6) = compute_trie(chunks[0x10000 // CHUNK : 0x110000 // CHUNK], 64 // CHUNK) (r4, r5) = compute_trie(mid, 64) f.write(" r4: [\n") data = ','.join(str(node) for node in r4) @@ -446,14 +446,14 @@ def emit_bool_trie(f, name, t_data, is_pub=True): f.write(" };\n\n") def emit_small_bool_trie(f, name, t_data, is_pub=True): - last_chunk = max(int(hi / 64) for (lo, hi) in t_data) + last_chunk = max(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) + if cp // 64 >= len(chunks): + print(cp, cp // 64, len(chunks), lo, hi) + chunks[cp // 64] |= 1 << (cp & 63) pub_string = "" if is_pub: @@ -519,32 +519,29 @@ def emit_conversions_module(f, to_upper, to_lower, to_title): pfun = lambda x: "(%s,[%s,%s,%s])" % ( escape_char(x[0]), escape_char(x[1][0]), escape_char(x[1][1]), escape_char(x[1][2])) emit_table(f, "to_lowercase_table", - sorted(to_lower.iteritems(), key=operator.itemgetter(0)), + sorted(to_lower.items(), key=operator.itemgetter(0)), is_pub=False, t_type = t_type, pfun=pfun) emit_table(f, "to_uppercase_table", - sorted(to_upper.iteritems(), key=operator.itemgetter(0)), + sorted(to_upper.items(), key=operator.itemgetter(0)), is_pub=False, t_type = t_type, pfun=pfun) f.write("}\n\n") def emit_norm_module(f, canon, compat, combine, norm_props): - canon_keys = canon.keys() - canon_keys.sort() + canon_keys = sorted(canon.keys()) - compat_keys = compat.keys() - compat_keys.sort() + compat_keys = sorted(compat.keys()) canon_comp = {} comp_exclusions = norm_props["Full_Composition_Exclusion"] for char in canon_keys: - if True in map(lambda (lo, hi): lo <= char <= hi, comp_exclusions): + if any(lo <= char <= hi for lo, hi in comp_exclusions): continue decomp = canon[char] if len(decomp) == 2: - if not canon_comp.has_key(decomp[0]): + if decomp[0] not in canon_comp: canon_comp[decomp[0]] = [] canon_comp[decomp[0]].append( (decomp[1], char) ) - canon_comp_keys = canon_comp.keys() - canon_comp_keys.sort() + canon_comp_keys = sorted(canon_comp.keys()) if __name__ == "__main__": r = "tables.rs" diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0504e889ea10b..3c1d6ea18f7c2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -12,7 +12,6 @@ pub use self::TyParamBound::*; pub use self::UnsafeSource::*; -pub use self::ViewPath_::*; pub use self::PathParameters::*; pub use symbol::{Ident, Symbol as Name}; pub use util::ThinVec; @@ -96,10 +95,15 @@ impl Path { } } + // Add starting "crate root" segment to all paths except those that + // already have it or start with `self`, `super`, `Self` or `$crate`. pub fn default_to_global(mut self) -> Path { - if !self.is_global() && - !::parse::token::Ident(self.segments[0].identifier).is_path_segment_keyword() { - self.segments.insert(0, PathSegment::crate_root(self.span)); + if !self.is_global() { + let ident = self.segments[0].identifier; + if !::parse::token::Ident(ident).is_path_segment_keyword() || + ident.name == keywords::Crate.name() { + self.segments.insert(0, PathSegment::crate_root(self.span)); + } } self } @@ -786,8 +790,6 @@ pub enum MacStmtStyle { NoBraces, } -// FIXME (pending discussion of #1697, #2178...): local should really be -// a refinement on pat. /// Local represents a `let` statement, e.g., `let : = ;` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct Local { @@ -903,7 +905,9 @@ pub enum ExprKind { /// A function call /// /// The first field resolves to the function itself, - /// and the second field is the list of arguments + /// and the second field is the list of arguments. + /// This also represents calling the constructor of + /// tuple-like ADTs such as tuple structs and enum variants. Call(P, Vec>), /// A method call (`x.foo::<'static, Bar, Baz>(a, b, c, d)`) /// @@ -1180,7 +1184,6 @@ pub struct MethodSig { pub constness: Spanned, pub abi: Abi, pub decl: P, - pub generics: Generics, } /// Represents an item declaration within a trait declaration, @@ -1192,6 +1195,7 @@ pub struct TraitItem { pub id: NodeId, pub ident: Ident, pub attrs: Vec, + pub generics: Generics, pub node: TraitItemKind, pub span: Span, /// See `Item::tokens` for what this is @@ -1213,6 +1217,7 @@ pub struct ImplItem { pub vis: Visibility, pub defaultness: Defaultness, pub attrs: Vec, + pub generics: Generics, pub node: ImplItemKind, pub span: Span, /// See `Item::tokens` for what this is @@ -1421,7 +1426,7 @@ pub enum TyKind { Path(Option, Path), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(TyParamBounds), + TraitObject(TyParamBounds, TraitObjectSyntax), /// An `impl Bound1 + Bound2 + Bound3` type /// where `Bound` is a trait or a lifetime. ImplTrait(TyParamBounds), @@ -1440,6 +1445,13 @@ pub enum TyKind { Err, } +/// Syntax used to declare a trait object. +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum TraitObjectSyntax { + Dyn, + None, +} + /// Inline assembly dialect. /// /// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`` @@ -1573,6 +1585,13 @@ impl FnDecl { } } +/// Is the trait definition an auto trait? +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum IsAuto { + Yes, + No +} + #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Unsafety { Unsafe, @@ -1685,46 +1704,20 @@ pub struct Variant_ { pub type Variant = Spanned; -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub struct PathListItem_ { - pub name: Ident, - /// renamed in list, e.g. `use foo::{bar as baz};` - pub rename: Option, - pub id: NodeId, -} - -pub type PathListItem = Spanned; - -pub type ViewPath = Spanned; - #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] -pub enum ViewPath_ { - - /// `foo::bar::baz as quux` - /// - /// or just - /// - /// `foo::bar::baz` (with `as baz` implicitly on the right) - ViewPathSimple(Ident, Path), - - /// `foo::bar::*` - ViewPathGlob(Path), - - /// `foo::bar::{a,b,c}` - ViewPathList(Path, Vec) +pub enum UseTreeKind { + Simple(Ident), + Glob, + Nested(Vec<(UseTree, NodeId)>), } -impl ViewPath_ { - pub fn path(&self) -> &Path { - match *self { - ViewPathSimple(_, ref path) | - ViewPathGlob (ref path) | - ViewPathList(ref path, _) => path - } - } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct UseTree { + pub kind: UseTreeKind, + pub prefix: Path, + pub span: Span, } - /// Distinguishes between Attributes that decorate items and Attributes that /// are contained as statements within items. These two cases need to be /// distinguished for pretty-printing. @@ -1782,10 +1775,19 @@ impl PolyTraitRef { } } +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum CrateSugar { + /// Source is `pub(crate)` + PubCrate, + + /// Source is (just) `crate` + JustCrate, +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Visibility { Public, - Crate(Span), + Crate(Span, CrateSugar), Restricted { path: P, id: NodeId }, Inherited, } @@ -1884,7 +1886,7 @@ pub enum ItemKind { /// A use declaration (`use` or `pub use`) item. /// /// E.g. `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;` - Use(P), + Use(P), /// A static item (`static` or `pub static`). /// /// E.g. `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";` @@ -1925,12 +1927,12 @@ pub enum ItemKind { Union(VariantData, Generics), /// A Trait declaration (`trait` or `pub trait`). /// - /// E.g. `trait Foo { .. }` or `trait Foo { .. }` - Trait(Unsafety, Generics, TyParamBounds, Vec), - // Default trait implementation. + /// E.g. `trait Foo { .. }`, `trait Foo { .. }` or `auto trait Foo {}` + Trait(IsAuto, Unsafety, Generics, TyParamBounds, Vec), + /// Auto trait implementation. /// /// E.g. `impl Trait for .. {}` or `impl Trait for .. {}` - DefaultImpl(Unsafety, TraitRef), + AutoImpl(Unsafety, TraitRef), /// An implementation. /// /// E.g. `impl Foo { .. }` or `impl Trait for Foo { .. }` @@ -1969,7 +1971,7 @@ impl ItemKind { ItemKind::Mac(..) | ItemKind::MacroDef(..) | ItemKind::Impl(..) | - ItemKind::DefaultImpl(..) => "item" + ItemKind::AutoImpl(..) => "item" } } } @@ -1992,13 +1994,16 @@ pub enum ForeignItemKind { /// A foreign static item (`static ext: u8`), with optional mutability /// (the boolean is true when mutable) Static(P, bool), + /// A foreign type + Ty, } impl ForeignItemKind { pub fn descriptive_variant(&self) -> &str { match *self { ForeignItemKind::Fn(..) => "foreign function", - ForeignItemKind::Static(..) => "foreign static item" + ForeignItemKind::Static(..) => "foreign static item", + ForeignItemKind::Ty => "foreign type", } } } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index b1f796084df82..8bd7399092f26 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -371,11 +371,13 @@ impl Attribute { let meta = mk_name_value_item_str( Symbol::intern("doc"), Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))); - if self.style == ast::AttrStyle::Outer { - f(&mk_attr_outer(self.span, self.id, meta)) + let mut attr = if self.style == ast::AttrStyle::Outer { + mk_attr_outer(self.span, self.id, meta) } else { - f(&mk_attr_inner(self.span, self.id, meta)) - } + mk_attr_inner(self.span, self.id, meta) + }; + attr.is_sugared_doc = true; + f(&attr) } else { f(self) } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index cd4a6f921fe6f..3906ed431ce20 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -17,11 +17,15 @@ //! within the CodeMap, which upon request can be converted to line and column //! information, source code snippets, etc. + pub use syntax_pos::*; pub use syntax_pos::hygiene::{ExpnFormat, ExpnInfo, NameAndSpan}; pub use self::ExpnFormat::*; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::StableHasher; use std::cell::{RefCell, Ref}; +use std::hash::Hash; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -98,6 +102,24 @@ impl FileLoader for RealFileLoader { } } +// This is a FileMap identifier that is used to correlate FileMaps between +// subsequent compilation sessions (which is something we need to do during +// incremental compilation). +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +pub struct StableFilemapId(u128); + +impl StableFilemapId { + pub fn new(filemap: &FileMap) -> StableFilemapId { + let mut hasher = StableHasher::new(); + + filemap.name.hash(&mut hasher); + filemap.name_was_remapped.hash(&mut hasher); + filemap.unmapped_path.hash(&mut hasher); + + StableFilemapId(hasher.finish()) + } +} + // _____________________________________________________________________________ // CodeMap // @@ -108,6 +130,7 @@ pub struct CodeMap { // This is used to apply the file path remapping as specified via // -Zremap-path-prefix to all FileMaps allocated within this CodeMap. path_mapping: FilePathMapping, + stable_id_to_filemap: RefCell>>, } impl CodeMap { @@ -116,6 +139,7 @@ impl CodeMap { files: RefCell::new(Vec::new()), file_loader: Box::new(RealFileLoader), path_mapping, + stable_id_to_filemap: RefCell::new(FxHashMap()), } } @@ -126,6 +150,7 @@ impl CodeMap { files: RefCell::new(Vec::new()), file_loader, path_mapping, + stable_id_to_filemap: RefCell::new(FxHashMap()), } } @@ -146,6 +171,10 @@ impl CodeMap { self.files.borrow() } + pub fn filemap_by_stable_id(&self, stable_id: StableFilemapId) -> Option> { + self.stable_id_to_filemap.borrow().get(&stable_id).map(|fm| fm.clone()) + } + fn next_start_pos(&self) -> usize { let files = self.files.borrow(); match files.last() { @@ -162,12 +191,28 @@ impl CodeMap { let start_pos = self.next_start_pos(); let mut files = self.files.borrow_mut(); + // The path is used to determine the directory for loading submodules and + // include files, so it must be before remapping. + // Note that filename may not be a valid path, eg it may be `` etc, + // but this is okay because the directory determined by `path.pop()` will + // be empty, so the working directory will be used. + let unmapped_path = PathBuf::from(filename.clone()); + let (filename, was_remapped) = self.path_mapping.map_prefix(filename); - let filemap = - Rc::new(FileMap::new(filename, was_remapped, src, Pos::from_usize(start_pos))); + let filemap = Rc::new(FileMap::new( + filename, + was_remapped, + unmapped_path, + src, + Pos::from_usize(start_pos), + )); files.push(filemap.clone()); + self.stable_id_to_filemap + .borrow_mut() + .insert(StableFilemapId::new(&filemap), filemap.clone()); + filemap } @@ -197,7 +242,8 @@ impl CodeMap { src_hash: u128, source_len: usize, mut file_local_lines: Vec, - mut file_local_multibyte_chars: Vec) + mut file_local_multibyte_chars: Vec, + mut file_local_non_narrow_chars: Vec) -> Rc { let start_pos = self.next_start_pos(); let mut files = self.files.borrow_mut(); @@ -213,9 +259,14 @@ impl CodeMap { mbc.pos = mbc.pos + start_pos; } + for swc in &mut file_local_non_narrow_chars { + *swc = *swc + start_pos; + } + let filemap = Rc::new(FileMap { name: filename, name_was_remapped, + unmapped_path: None, crate_of_origin, src: None, src_hash, @@ -224,10 +275,15 @@ impl CodeMap { end_pos, lines: RefCell::new(file_local_lines), multibyte_chars: RefCell::new(file_local_multibyte_chars), + non_narrow_chars: RefCell::new(file_local_non_narrow_chars), }); files.push(filemap.clone()); + self.stable_id_to_filemap + .borrow_mut() + .insert(StableFilemapId::new(&filemap), filemap.clone()); + filemap } @@ -247,6 +303,24 @@ impl CodeMap { let line = a + 1; // Line numbers start at 1 let linebpos = (*f.lines.borrow())[a]; let linechpos = self.bytepos_to_file_charpos(linebpos); + let col = chpos - linechpos; + + let col_display = { + let non_narrow_chars = f.non_narrow_chars.borrow(); + let start_width_idx = non_narrow_chars + .binary_search_by_key(&linebpos, |x| x.pos()) + .unwrap_or_else(|x| x); + let end_width_idx = non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let special_chars = end_width_idx - start_width_idx; + let non_narrow: usize = + non_narrow_chars[start_width_idx..end_width_idx] + .into_iter() + .map(|x| x.width()) + .sum(); + col.0 - special_chars + non_narrow + }; debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); debug!("char pos {:?} is on the line at char pos {:?}", @@ -256,21 +330,35 @@ impl CodeMap { Loc { file: f, line, - col: chpos - linechpos, + col, + col_display, } } Err(f) => { + let col_display = { + let non_narrow_chars = f.non_narrow_chars.borrow(); + let end_width_idx = non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let non_narrow: usize = + non_narrow_chars[0..end_width_idx] + .into_iter() + .map(|x| x.width()) + .sum(); + chpos.0 - end_width_idx + non_narrow + }; Loc { file: f, line: 0, col: chpos, + col_display, } } } } // If the relevant filemap is empty, we don't return a line number. - fn lookup_line(&self, pos: BytePos) -> Result> { + pub fn lookup_line(&self, pos: BytePos) -> Result> { let idx = self.lookup_filemap_idx(pos); let files = self.files.borrow(); @@ -342,7 +430,12 @@ impl CodeMap { } pub fn span_to_filename(&self, sp: Span) -> FileName { - self.lookup_char_pos(sp.lo()).file.name.to_string() + self.lookup_char_pos(sp.lo()).file.name.clone() + } + + pub fn span_to_unmapped_path(&self, sp: Span) -> PathBuf { + self.lookup_char_pos(sp.lo()).file.unmapped_path.clone() + .expect("CodeMap::span_to_unmapped_path called for imported FileMap?") } pub fn span_to_lines(&self, sp: Span) -> FileLinesResult { @@ -453,6 +546,17 @@ impl CodeMap { } } + /// Given a `Span`, try to get a shorter span ending just after the first + /// occurrence of `char` `c`. + pub fn span_through_char(&self, sp: Span, c: char) -> Span { + if let Ok(snippet) = self.span_to_snippet(sp) { + if let Some(offset) = snippet.find(c) { + return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); + } + } + sp + } + pub fn def_span(&self, sp: Span) -> Span { self.span_until_char(sp, '{') } diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs index e8ecf58072a69..c01836b619411 100644 --- a/src/libsyntax/diagnostics/macros.rs +++ b/src/libsyntax/diagnostics/macros.rs @@ -18,7 +18,11 @@ macro_rules! register_diagnostic { macro_rules! span_fatal { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.span_fatal_with_code($span, &format!($($message)*), stringify!($code)) + $session.span_fatal_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -26,7 +30,11 @@ macro_rules! span_fatal { macro_rules! span_err { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.span_err_with_code($span, &format!($($message)*), stringify!($code)) + $session.span_err_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -34,7 +42,11 @@ macro_rules! span_err { macro_rules! span_warn { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.span_warn_with_code($span, &format!($($message)*), stringify!($code)) + $session.span_warn_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -42,7 +54,10 @@ macro_rules! span_warn { macro_rules! struct_err { ($session:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.struct_err_with_code(&format!($($message)*), stringify!($code)) + $session.struct_err_with_code( + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -51,9 +66,17 @@ macro_rules! span_err_or_warn { ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); if $is_warning { - $session.span_warn_with_code($span, &format!($($message)*), stringify!($code)) + $session.span_warn_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) } else { - $session.span_err_with_code($span, &format!($($message)*), stringify!($code)) + $session.span_err_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) } }) } @@ -62,7 +85,11 @@ macro_rules! span_err_or_warn { macro_rules! struct_span_fatal { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.struct_span_fatal_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_fatal_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -70,7 +97,11 @@ macro_rules! struct_span_fatal { macro_rules! struct_span_err { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_err_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -89,7 +120,11 @@ macro_rules! type_error_struct { macro_rules! struct_span_warn { ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); - $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_warn_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) }) } @@ -98,9 +133,17 @@ macro_rules! struct_span_err_or_warn { ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ __diagnostic_used!($code); if $is_warning { - $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_warn_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) } else { - $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code)) + $session.struct_span_err_with_code( + $span, + &format!($($message)*), + $crate::errors::DiagnosticId::Error(stringify!($code).to_owned()), + ) } }) } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 0e05cce35e2df..6c96692f719ff 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -665,6 +665,7 @@ pub struct ExtCtxt<'a> { pub parse_sess: &'a parse::ParseSess, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, + pub root_path: PathBuf, pub resolver: &'a mut Resolver, pub resolve_err_count: usize, pub current_expansion: ExpansionData, @@ -680,6 +681,7 @@ impl<'a> ExtCtxt<'a> { parse_sess, ecfg, crate_root: None, + root_path: PathBuf::new(), resolver, resolve_err_count: 0, current_expansion: ExpansionData { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 48d789372a07b..25eef6db93036 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -291,7 +291,7 @@ pub trait AstBuilder { -> ast::MetaItem; fn item_use(&self, sp: Span, - vis: ast::Visibility, vp: P) -> P; + vis: ast::Visibility, vp: P) -> P; fn item_use_simple(&self, sp: Span, vis: ast::Visibility, path: ast::Path) -> P; fn item_use_simple_(&self, sp: Span, vis: ast::Visibility, ident: ast::Ident, path: ast::Path) -> P; @@ -1142,7 +1142,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn item_use(&self, sp: Span, - vis: ast::Visibility, vp: P) -> P { + vis: ast::Visibility, vp: P) -> P { P(ast::Item { id: ast::DUMMY_NODE_ID, ident: keywords::Invalid.ident(), @@ -1161,33 +1161,36 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn item_use_simple_(&self, sp: Span, vis: ast::Visibility, ident: ast::Ident, path: ast::Path) -> P { - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathSimple(ident, - path)))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: path, + kind: ast::UseTreeKind::Simple(ident), + })) } fn item_use_list(&self, sp: Span, vis: ast::Visibility, path: Vec, imports: &[ast::Ident]) -> P { let imports = imports.iter().map(|id| { - let item = ast::PathListItem_ { - name: *id, - rename: None, - id: ast::DUMMY_NODE_ID, - }; - respan(sp, item) + (ast::UseTree { + span: sp, + prefix: self.path(sp, vec![*id]), + kind: ast::UseTreeKind::Simple(*id), + }, ast::DUMMY_NODE_ID) }).collect(); - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathList(self.path(sp, path), - imports)))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: self.path(sp, path), + kind: ast::UseTreeKind::Nested(imports), + })) } fn item_use_glob(&self, sp: Span, vis: ast::Visibility, path: Vec) -> P { - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathGlob(self.path(sp, path))))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: self.path(sp, path), + kind: ast::UseTreeKind::Glob, + })) } } diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 2e70962cad6f8..c7fa0331c1bd5 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -74,7 +74,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] let meta = cx.meta_word(span, Symbol::intern("structural_match")); attrs.push(cx.attribute(span, meta)); } - if names.contains(&Symbol::intern("Copy")) && names.contains(&Symbol::intern("Clone")) { + if names.contains(&Symbol::intern("Copy")) { let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker")); attrs.push(cx.attribute(span, meta)); } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6e7a8203b61ca..0d1b1c65a2934 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -11,7 +11,7 @@ use ast::{self, Block, Ident, NodeId, PatKind, Path}; use ast::{MacStmtStyle, StmtKind, ItemKind}; use attr::{self, HasAttrs}; -use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; +use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, dummy_spanned}; use config::{is_test_or_bench, StripUnconfigured}; use errors::FatalError; use ext::base::*; @@ -29,13 +29,15 @@ use std_inject; use symbol::Symbol; use symbol::keywords; use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::hygiene::ExpnFormat; use tokenstream::{TokenStream, TokenTree}; use util::small_vector::SmallVector; use visit::Visitor; use std::collections::HashMap; +use std::fs::File; +use std::io::Read; use std::mem; -use std::path::PathBuf; use std::rc::Rc; macro_rules! expansions { @@ -152,6 +154,26 @@ impl ExpansionKind { } } +fn macro_bang_format(path: &ast::Path) -> ExpnFormat { + // We don't want to format a path using pretty-printing, + // `format!("{}", path)`, because that tries to insert + // line-breaks and is slow. + let mut path_str = String::with_capacity(64); + for (i, segment) in path.segments.iter().enumerate() { + if i != 0 { + path_str.push_str("::"); + } + + if segment.identifier.name != keywords::CrateRoot.name() && + segment.identifier.name != keywords::DollarCrate.name() + { + path_str.push_str(&segment.identifier.name.as_str()) + } + } + + MacroBang(Symbol::intern(&path_str)) +} + pub struct Invocation { pub kind: InvocationKind, expansion_kind: ExpansionKind, @@ -200,9 +222,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.crate_root = std_inject::injected_crate_name(&krate); let mut module = ModuleData { mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], - directory: PathBuf::from(self.cx.codemap().span_to_filename(krate.span)), + directory: self.cx.codemap().span_to_unmapped_path(krate.span), }; module.directory.pop(); + self.cx.root_path = module.directory.clone(); self.cx.current_expansion.module = Rc::new(module); let orig_mod_span = krate.module.inner; @@ -518,7 +541,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { mark.set_expn_info(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(Symbol::intern(&format!("{}", path))), + format: macro_bang_format(path), span: def_site_span, allow_internal_unstable, allow_internal_unsafe, @@ -565,7 +588,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { invoc.expansion_data.mark.set_expn_info(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(Symbol::intern(&format!("{}", path))), + format: macro_bang_format(path), span: tt_span, allow_internal_unstable, allow_internal_unsafe: false, @@ -601,7 +624,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { invoc.expansion_data.mark.set_expn_info(ExpnInfo { call_site: span, callee: NameAndSpan { - format: MacroBang(Symbol::intern(&format!("{}", path))), + format: macro_bang_format(path), // FIXME procedural macros do not have proper span info // yet, when they do, we should use it here. span: None, @@ -823,6 +846,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { feature_gate::check_attribute(attr, self.cx.parse_sess, features); } } + + fn check_attribute(&mut self, at: &ast::Attribute) { + let features = self.cx.ecfg.features.unwrap(); + feature_gate::check_attribute(at, self.cx.parse_sess, features); + } } pub fn find_attr_invoc(attrs: &mut Vec) -> Option { @@ -952,8 +980,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { module.directory.push(&*item.ident.name.as_str()); } } else { - let mut path = - PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); + let mut path = self.cx.parse_sess.codemap().span_to_unmapped_path(inner); let directory_ownership = match path.file_name().unwrap().to_str() { Some("mod.rs") => DirectoryOwnership::Owned, _ => DirectoryOwnership::UnownedViaMod(false), @@ -1044,6 +1071,84 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } } + fn fold_attribute(&mut self, at: ast::Attribute) -> Option { + // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename", + // contents="file contents")]` attributes + if !at.check_name("doc") { + return noop_fold_attribute(at, self); + } + + if let Some(list) = at.meta_item_list() { + if !list.iter().any(|it| it.check_name("include")) { + return noop_fold_attribute(at, self); + } + + let mut items = vec![]; + + for it in list { + if !it.check_name("include") { + items.push(noop_fold_meta_list_item(it, self)); + continue; + } + + if let Some(file) = it.value_str() { + let err_count = self.cx.parse_sess.span_diagnostic.err_count(); + self.check_attribute(&at); + if self.cx.parse_sess.span_diagnostic.err_count() > err_count { + // avoid loading the file if they haven't enabled the feature + return noop_fold_attribute(at, self); + } + + let mut buf = vec![]; + let filename = self.cx.root_path.join(file.to_string()); + + match File::open(&filename).and_then(|mut f| f.read_to_end(&mut buf)) { + Ok(..) => {} + Err(e) => { + self.cx.span_warn(at.span, + &format!("couldn't read {}: {}", + filename.display(), + e)); + } + } + + match String::from_utf8(buf) { + Ok(src) => { + let include_info = vec![ + dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_name_value_item_str("file".into(), + file))), + dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_name_value_item_str("contents".into(), + (&*src).into()))), + ]; + + items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_list_item("include".into(), include_info)))); + } + Err(_) => { + self.cx.span_warn(at.span, + &format!("{} wasn't a utf-8 file", + filename.display())); + } + } + } else { + items.push(noop_fold_meta_list_item(it, self)); + } + } + + let meta = attr::mk_list_item("doc".into(), items); + match at.style { + ast::AttrStyle::Inner => + Some(attr::mk_spanned_attr_inner(at.span, at.id, meta)), + ast::AttrStyle::Outer => + Some(attr::mk_spanned_attr_outer(at.span, at.id, meta)), + } + } else { + noop_fold_attribute(at, self) + } + } + fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId { if self.monotonic { assert_eq!(id, ast::DUMMY_NODE_ID); diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 4fc2b92d3cd52..2f5b386346bc8 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -32,6 +32,7 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { let ident = keywords::Invalid.ident(); let attrs = Vec::new(); + let generics = ast::Generics::default(); let vis = ast::Visibility::Inherited; let span = DUMMY_SP; let expr_placeholder = || P(ast::Expr { @@ -49,12 +50,12 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { tokens: None, }))), ExpansionKind::TraitItems => Expansion::TraitItems(SmallVector::one(ast::TraitItem { - id, span, ident, attrs, + id, span, ident, attrs, generics, node: ast::TraitItemKind::Macro(mac_placeholder()), tokens: None, })), ExpansionKind::ImplItems => Expansion::ImplItems(SmallVector::one(ast::ImplItem { - id, span, ident, vis, attrs, + id, span, ident, vis, attrs, generics, node: ast::ImplItemKind::Macro(mac_placeholder()), defaultness: ast::Defaultness::Final, tokens: None, diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 18a262d139a27..86657e675b2de 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -197,7 +197,7 @@ fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf { // after macro expansion (that is, they are unhygienic). if !arg.is_absolute() { let callsite = sp.source_callsite(); - let mut path = PathBuf::from(&cx.codemap().span_to_filename(callsite)); + let mut path = cx.codemap().span_to_unmapped_path(callsite); path.pop(); path.push(arg); path diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 4da0df5b0de01..d4b54e896abca 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -33,9 +33,8 @@ use syntax_pos::Span; use errors::{DiagnosticBuilder, Handler, FatalError}; use visit::{self, FnKind, Visitor}; use parse::ParseSess; -use symbol::Symbol; +use symbol::{keywords, Symbol}; -use std::ascii::AsciiExt; use std::env; macro_rules! set { @@ -276,6 +275,9 @@ declare_features! ( // Allows `impl Trait` in function return types. (active, conservative_impl_trait, "1.12.0", Some(34511)), + // Allows `impl Trait` in function arguments. + (active, universal_impl_trait, "1.23.0", Some(34511)), + // The `!` type (active, never_type, "1.13.0", Some(35121)), @@ -378,7 +380,11 @@ declare_features! ( // #[doc(cfg(...))] (active, doc_cfg, "1.21.0", Some(43781)), // #[doc(masked)] - (active, doc_masked, "1.21.0", None), + (active, doc_masked, "1.21.0", Some(44027)), + // #[doc(spotlight)] + (active, doc_spotlight, "1.22.0", Some(45040)), + // #[doc(include="some-file")] + (active, external_doc, "1.22.0", Some(44732)), // allow `#[must_use]` on functions and comparison operators (RFC 1940) (active, fn_must_use, "1.21.0", Some(43302)), @@ -386,6 +392,9 @@ declare_features! ( // allow '|' at beginning of match arms (RFC 1925) (active, match_beginning_vert, "1.21.0", Some(44101)), + // Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008) + (active, non_exhaustive, "1.22.0", Some(44109)), + // Copy/Clone closures (RFC 2132) (active, clone_closures, "1.22.0", Some(44490)), (active, copy_closures, "1.22.0", Some(44490)), @@ -395,6 +404,36 @@ declare_features! ( // allow `..=` in patterns (RFC 1192) (active, dotdoteq_in_patterns, "1.22.0", Some(28237)), + + // Default match binding modes (RFC 2005) + (active, match_default_bindings, "1.22.0", Some(42640)), + + // Trait object syntax with `dyn` prefix + (active, dyn_trait, "1.22.0", Some(44662)), + + // `crate` as visibility modifier, synonymous to `pub(crate)` + (active, crate_visibility_modifier, "1.23.0", Some(45388)), + + // extern types + (active, extern_types, "1.23.0", Some(43467)), + + // Allow trait methods with arbitrary self types + (active, arbitrary_self_types, "1.23.0", Some(44874)), + + // #![wasm_import_memory] attribute + (active, wasm_import_memory, "1.22.0", None), + + // `crate` in paths + (active, crate_in_paths, "1.23.0", Some(45477)), + + // In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`) + (active, in_band_lifetimes, "1.23.0", Some(44524)), + + // Nested groups in `use` (RFC 2128) + (active, use_nested_groups, "1.23.0", Some(44494)), + + // generic associated types (RFC 1598) + (active, generic_associated_types, "1.23.0", Some(44265)), ); declare_features! ( @@ -602,6 +641,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG not yet settled", cfg_fn!(structural_match))), + // RFC #2008 + ("non_exhaustive", Whitelisted, Gated(Stability::Unstable, + "non_exhaustive", + "non exhaustive is an experimental feature", + cfg_fn!(non_exhaustive))), + ("plugin", CrateLevel, Gated(Stability::Unstable, "plugin", "compiler plugins are experimental \ @@ -703,18 +748,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), - ("rustc_metadata_dirty", Whitelisted, Gated(Stability::Unstable, - "rustc_attrs", - "the `#[rustc_metadata_dirty]` attribute \ - is just used for rustc unit tests \ - and will never be stable", - cfg_fn!(rustc_attrs))), - ("rustc_metadata_clean", Whitelisted, Gated(Stability::Unstable, - "rustc_attrs", - "the `#[rustc_metadata_clean]` attribute \ - is just used for rustc unit tests \ - and will never be stable", - cfg_fn!(rustc_attrs))), ("rustc_partition_reused", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "this attribute \ @@ -727,6 +760,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), + ("rustc_synthetic", Whitelisted, Gated(Stability::Unstable, + "rustc_attrs", + "this attribute \ + is just used for rustc unit tests \ + and will never be stable", + cfg_fn!(rustc_attrs))), ("rustc_symbol_name", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "internal rustc attributes will never be stable", @@ -818,7 +857,8 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG ("no_debug", Whitelisted, Gated( Stability::Deprecated("https://github.com/rust-lang/rust/issues/29721"), "no_debug", - "the `#[no_debug]` attribute is an experimental feature", + "the `#[no_debug]` attribute was an experimental feature that has been \ + deprecated due to lack of demand", cfg_fn!(no_debug))), ("omit_gdb_pretty_printer_section", Whitelisted, Gated(Stability::Unstable, "omit_gdb_pretty_printer_section", @@ -889,6 +929,17 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "allow_fail attribute is currently unstable", cfg_fn!(allow_fail))), + ("rustc_std_internal_symbol", Whitelisted, Gated(Stability::Unstable, + "rustc_attrs", + "this is an internal attribute that will \ + never be stable", + cfg_fn!(rustc_attrs))), + + ("wasm_import_memory", Whitelisted, Gated(Stability::Unstable, + "wasm_import_memory", + "wasm_import_memory attribute is currently unstable", + cfg_fn!(wasm_import_memory))), + // Crate level attributes ("crate_name", CrateLevel, Ungated), ("crate_type", CrateLevel, Ungated), @@ -976,6 +1027,14 @@ impl<'a> Context<'a> { if name == n { if let Gated(_, name, desc, ref has_feature) = *gateage { gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard); + } else if name == "doc" { + if let Some(content) = attr.meta_item_list() { + if content.iter().any(|c| c.check_name("include")) { + gate_feature!(self, external_doc, attr.span, + "#[doc(include = \"...\")] is experimental" + ); + } + } } debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); return; @@ -1253,6 +1312,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, doc_masked, attr.span, "#[doc(masked)] is experimental" ); + } else if content.iter().any(|c| c.check_name("spotlight")) { + gate_feature_post!(&self, doc_spotlight, attr.span, + "#[doc(spotlight)] is experimental" + ); } } } @@ -1338,10 +1401,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::DefaultImpl(..) => { + ast::ItemKind::AutoImpl(..) => { gate_feature_post!(&self, optin_builtin_traits, i.span, - "default trait implementations are experimental \ + "auto trait implementations are experimental \ and possibly buggy"); } @@ -1370,6 +1433,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } + ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => { + gate_feature_post!(&self, optin_builtin_traits, + i.span, + "auto traits are experimental and possibly buggy"); + } + ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => { let msg = "`macro` is experimental"; gate_feature_post!(&self, decl_macro, i.span, msg); @@ -1382,13 +1451,23 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) { - let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs, "link_name") { - Some(val) => val.as_str().starts_with("llvm."), - _ => false - }; - if links_to_llvm { - gate_feature_post!(&self, link_llvm_intrinsics, i.span, - "linking to LLVM intrinsics is experimental"); + match i.node { + ast::ForeignItemKind::Fn(..) | + ast::ForeignItemKind::Static(..) => { + let link_name = attr::first_attr_value_str_by_name(&i.attrs, "link_name"); + let links_to_llvm = match link_name { + Some(val) => val.as_str().starts_with("llvm."), + _ => false + }; + if links_to_llvm { + gate_feature_post!(&self, link_llvm_intrinsics, i.span, + "linking to LLVM intrinsics is experimental"); + } + } + ast::ForeignItemKind::Ty => { + gate_feature_post!(&self, extern_types, i.span, + "extern types are experimental"); + } } visit::walk_foreign_item(self, i) @@ -1399,14 +1478,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::TyKind::BareFn(ref bare_fn_ty) => { self.check_abi(bare_fn_ty.abi, ty.span); } - ast::TyKind::ImplTrait(..) => { - gate_feature_post!(&self, conservative_impl_trait, ty.span, - "`impl Trait` is experimental"); - } ast::TyKind::Never => { gate_feature_post!(&self, never_type, ty.span, "The `!` type is experimental"); }, + ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::Dyn) => { + gate_feature_post!(&self, dyn_trait, ty.span, + "`dyn Trait` syntax is unstable"); + } _ => {} } visit::walk_ty(self, ty) @@ -1509,7 +1588,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { span: Span, _node_id: NodeId) { // check for const fn declarations - if let FnKind::ItemFn(_, _, _, Spanned { node: ast::Constness::Const, .. }, _, _, _) = + if let FnKind::ItemFn(_, _, Spanned { node: ast::Constness::Const, .. }, _, _, _) = fn_kind { gate_feature_post!(&self, const_fn, span, "const fn is unstable"); } @@ -1519,7 +1598,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { // point. match fn_kind { - FnKind::ItemFn(_, _, _, _, abi, _, _) | + FnKind::ItemFn(_, _, _, abi, _, _) | FnKind::Method(_, &ast::MethodSig { abi, .. }, _, _) => { self.check_abi(abi, span); } @@ -1538,9 +1617,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); } } - ast::TraitItemKind::Type(_, Some(_)) => { - gate_feature_post!(&self, associated_type_defaults, ti.span, - "associated type defaults are unstable"); + ast::TraitItemKind::Type(_, ref default) => { + // We use two if statements instead of something like match guards so that both + // of these errors can be emitted if both cases apply. + if default.is_some() { + gate_feature_post!(&self, associated_type_defaults, ti.span, + "associated type defaults are unstable"); + } + if ti.generics.is_parameterized() { + gate_feature_post!(&self, generic_associated_types, ti.span, + "generic associated types are unstable"); + } } _ => {} } @@ -1560,11 +1647,57 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, const_fn, ii.span, "const fn is unstable"); } } + ast::ImplItemKind::Type(_) if ii.generics.is_parameterized() => { + gate_feature_post!(&self, generic_associated_types, ii.span, + "generic associated types are unstable"); + } _ => {} } visit::walk_impl_item(self, ii); } + fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) { + for segment in &path.segments { + if segment.identifier.name == keywords::Crate.name() { + gate_feature_post!(&self, crate_in_paths, segment.span, + "`crate` in paths is experimental"); + } + } + + visit::walk_path(self, path); + } + + fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: NodeId, nested: bool) { + if nested { + match use_tree.kind { + ast::UseTreeKind::Simple(_) => { + if use_tree.prefix.segments.len() != 1 { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "paths in `use` groups are experimental"); + } + } + ast::UseTreeKind::Glob => { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "glob imports in `use` groups are experimental"); + } + ast::UseTreeKind::Nested(_) => { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "nested groups in `use` are experimental"); + } + } + } + + visit::walk_use_tree(self, use_tree, id); + } + + fn visit_vis(&mut self, vis: &'a ast::Visibility) { + if let ast::Visibility::Crate(span, ast::CrateSugar::JustCrate) = *vis { + gate_feature_post!(&self, crate_visibility_modifier, span, + "`crate` visibility modifier is experimental"); + } + visit::walk_vis(self, vis); + } + fn visit_generics(&mut self, g: &'a ast::Generics) { for t in &g.ty_params { if !t.attrs.is_empty() { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 03c47b71d02d7..1a92f057e5e87 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -56,8 +56,8 @@ pub trait Folder : Sized { noop_fold_meta_item(meta_item, self) } - fn fold_view_path(&mut self, view_path: P) -> P { - noop_fold_view_path(view_path, self) + fn fold_use_tree(&mut self, use_tree: UseTree) -> UseTree { + noop_fold_use_tree(use_tree, self) } fn fold_foreign_item(&mut self, ni: ForeignItem) -> ForeignItem { @@ -310,30 +310,18 @@ pub fn noop_fold_meta_items(meta_items: Vec, fld: &mut T) - meta_items.move_map(|x| fld.fold_meta_item(x)) } -pub fn noop_fold_view_path(view_path: P, fld: &mut T) -> P { - view_path.map(|Spanned {node, span}| Spanned { - node: match node { - ViewPathSimple(ident, path) => { - ViewPathSimple(fld.fold_ident(ident), fld.fold_path(path)) - } - ViewPathGlob(path) => { - ViewPathGlob(fld.fold_path(path)) - } - ViewPathList(path, path_list_idents) => { - let path = fld.fold_path(path); - let path_list_idents = path_list_idents.move_map(|path_list_ident| Spanned { - node: PathListItem_ { - id: fld.new_id(path_list_ident.node.id), - rename: path_list_ident.node.rename.map(|ident| fld.fold_ident(ident)), - name: fld.fold_ident(path_list_ident.node.name), - }, - span: fld.new_span(path_list_ident.span) - }); - ViewPathList(path, path_list_idents) - } +pub fn noop_fold_use_tree(use_tree: UseTree, fld: &mut T) -> UseTree { + UseTree { + span: fld.new_span(use_tree.span), + prefix: fld.fold_path(use_tree.prefix), + kind: match use_tree.kind { + UseTreeKind::Simple(ident) => UseTreeKind::Simple(fld.fold_ident(ident)), + UseTreeKind::Glob => UseTreeKind::Glob, + UseTreeKind::Nested(items) => UseTreeKind::Nested(items.move_map(|(tree, id)| { + (fld.fold_use_tree(tree), fld.new_id(id)) + })), }, - span: fld.new_span(span) - }) + } } pub fn fold_attrs(attrs: Vec, fld: &mut T) -> Vec { @@ -400,8 +388,8 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { TyKind::Typeof(expr) => { TyKind::Typeof(fld.fold_expr(expr)) } - TyKind::TraitObject(bounds) => { - TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b))) + TyKind::TraitObject(bounds, syntax) => { + TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)), syntax) } TyKind::ImplTrait(bounds) => { TyKind::ImplTrait(bounds.move_map(|b| fld.fold_ty_param_bound(b))) @@ -874,8 +862,8 @@ pub fn noop_fold_block(b: P, folder: &mut T) -> P { pub fn noop_fold_item_kind(i: ItemKind, folder: &mut T) -> ItemKind { match i { ItemKind::ExternCrate(string) => ItemKind::ExternCrate(string), - ItemKind::Use(view_path) => { - ItemKind::Use(folder.fold_view_path(view_path)) + ItemKind::Use(use_tree) => { + ItemKind::Use(use_tree.map(|tree| folder.fold_use_tree(tree))) } ItemKind::Static(t, m, e) => { ItemKind::Static(folder.fold_ty(t), m, folder.fold_expr(e)) @@ -908,8 +896,8 @@ pub fn noop_fold_item_kind(i: ItemKind, folder: &mut T) -> ItemKind { let generics = folder.fold_generics(generics); ItemKind::Union(folder.fold_variant_data(struct_def), generics) } - ItemKind::DefaultImpl(unsafety, ref trait_ref) => { - ItemKind::DefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone())) + ItemKind::AutoImpl(unsafety, ref trait_ref) => { + ItemKind::AutoImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone())) } ItemKind::Impl(unsafety, polarity, @@ -926,7 +914,8 @@ pub fn noop_fold_item_kind(i: ItemKind, folder: &mut T) -> ItemKind { folder.fold_ty(ty), impl_items.move_flat_map(|item| folder.fold_impl_item(item)), ), - ItemKind::Trait(unsafety, generics, bounds, items) => ItemKind::Trait( + ItemKind::Trait(is_auto, unsafety, generics, bounds, items) => ItemKind::Trait( + is_auto, unsafety, folder.fold_generics(generics), folder.fold_bounds(bounds), @@ -943,6 +932,7 @@ pub fn noop_fold_trait_item(i: TraitItem, folder: &mut T) id: folder.new_id(i.id), ident: folder.fold_ident(i.ident), attrs: fold_attrs(i.attrs, folder), + generics: folder.fold_generics(i.generics), node: match i.node { TraitItemKind::Const(ty, default) => { TraitItemKind::Const(folder.fold_ty(ty), @@ -972,6 +962,7 @@ pub fn noop_fold_impl_item(i: ImplItem, folder: &mut T) vis: folder.fold_vis(i.vis), ident: folder.fold_ident(i.ident), attrs: fold_attrs(i.attrs, folder), + generics: folder.fold_generics(i.generics), defaultness: i.defaultness, node: match i.node { ast::ImplItemKind::Const(ty, expr) => { @@ -1067,6 +1058,7 @@ pub fn noop_fold_foreign_item(ni: ForeignItem, folder: &mut T) -> For ForeignItemKind::Static(t, m) => { ForeignItemKind::Static(folder.fold_ty(t), m) } + ForeignItemKind::Ty => ForeignItemKind::Ty, }, span: folder.new_span(ni.span) } @@ -1074,7 +1066,6 @@ pub fn noop_fold_foreign_item(ni: ForeignItem, folder: &mut T) -> For pub fn noop_fold_method_sig(sig: MethodSig, folder: &mut T) -> MethodSig { MethodSig { - generics: folder.fold_generics(sig.generics), abi: sig.abi, unsafety: sig.unsafety, constness: sig.constness, diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs index db49ab1034358..80ac0cb4faf7d 100644 --- a/src/libsyntax/json.rs +++ b/src/libsyntax/json.rs @@ -22,43 +22,50 @@ use codemap::{CodeMap, FilePathMapping}; use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan}; use errors::registry::Registry; -use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper}; -use errors::emitter::Emitter; +use errors::{DiagnosticBuilder, SubDiagnostic, CodeSuggestion, CodeMapper}; +use errors::DiagnosticId; +use errors::emitter::{Emitter, EmitterWriter}; use std::rc::Rc; use std::io::{self, Write}; use std::vec; +use std::sync::{Arc, Mutex}; -use rustc_serialize::json::as_json; +use rustc_serialize::json::{as_json, as_pretty_json}; pub struct JsonEmitter { dst: Box, registry: Option, cm: Rc, + pretty: bool, } impl JsonEmitter { pub fn stderr(registry: Option, - code_map: Rc) -> JsonEmitter { + code_map: Rc, + pretty: bool) -> JsonEmitter { JsonEmitter { dst: Box::new(io::stderr()), registry, cm: code_map, + pretty, } } - pub fn basic() -> JsonEmitter { + pub fn basic(pretty: bool) -> JsonEmitter { let file_path_mapping = FilePathMapping::empty(); - JsonEmitter::stderr(None, Rc::new(CodeMap::new(file_path_mapping))) + JsonEmitter::stderr(None, Rc::new(CodeMap::new(file_path_mapping)), pretty) } pub fn new(dst: Box, registry: Option, - code_map: Rc) -> JsonEmitter { + code_map: Rc, + pretty: bool) -> JsonEmitter { JsonEmitter { dst, registry, cm: code_map, + pretty, } } } @@ -66,7 +73,12 @@ impl JsonEmitter { impl Emitter for JsonEmitter { fn emit(&mut self, db: &DiagnosticBuilder) { let data = Diagnostic::from_diagnostic_builder(db, self); - if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + }; + if let Err(e) = result { panic!("failed to print diagnostics: {:?}", e); } } @@ -84,9 +96,7 @@ struct Diagnostic { spans: Vec, /// Associated diagnostic messages. children: Vec, - /// The message as rustc would render it. Currently this is only - /// `Some` for "suggestions", but eventually it will include all - /// snippets. + /// The message as rustc would render it. rendered: Option, } @@ -109,9 +119,7 @@ struct DiagnosticSpan { /// Label that should be placed at this location (if any) label: Option, /// If we are suggesting a replacement, this will contain text - /// that should be sliced in atop this span. You may prefer to - /// load the fully rendered version from the parent `Diagnostic`, - /// however. + /// that should be sliced in atop this span. suggested_replacement: Option, /// Macro invocations that created the code at this span, if any. expansion: Option>, @@ -153,18 +161,37 @@ impl Diagnostic { fn from_diagnostic_builder(db: &DiagnosticBuilder, je: &JsonEmitter) -> Diagnostic { - let sugg = db.suggestions.iter().flat_map(|sugg| { - je.render(sugg).into_iter().map(move |rendered| { - Diagnostic { - message: sugg.msg.clone(), - code: None, - level: "help", - spans: DiagnosticSpan::from_suggestion(sugg, je), - children: vec![], - rendered: Some(rendered), - } - }) + let sugg = db.suggestions.iter().map(|sugg| { + Diagnostic { + message: sugg.msg.clone(), + code: None, + level: "help", + spans: DiagnosticSpan::from_suggestion(sugg, je), + children: vec![], + rendered: None, + } }); + + // generate regular command line output and store it in the json + + // A threadsafe buffer for writing. + #[derive(Default, Clone)] + struct BufWriter(Arc>>); + + impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.0.lock().unwrap().flush() + } + } + let buf = BufWriter::default(); + let output = buf.clone(); + EmitterWriter::new(Box::new(buf), Some(je.cm.clone()), false).emit(db); + let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); + let output = String::from_utf8(output).unwrap(); + Diagnostic { message: db.message(), code: DiagnosticCode::map_opt_string(db.code.clone(), je), @@ -173,7 +200,7 @@ impl Diagnostic { children: db.children.iter().map(|c| { Diagnostic::from_sub_diagnostic(c, je) }).chain(sugg).collect(), - rendered: None, + rendered: Some(output), } } @@ -183,7 +210,7 @@ impl Diagnostic { code: None, level: db.level.to_str(), spans: db.render_span.as_ref() - .map(|sp| DiagnosticSpan::from_render_span(sp, je)) + .map(|sp| DiagnosticSpan::from_multispan(sp, je)) .unwrap_or_else(|| DiagnosticSpan::from_multispan(&db.span, je)), children: vec![], rendered: None, @@ -279,32 +306,22 @@ impl DiagnosticSpan { fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec { - suggestion.substitution_parts + suggestion.substitutions .iter() .flat_map(|substitution| { - substitution.substitutions.iter().map(move |suggestion| { + substitution.parts.iter().map(move |suggestion| { let span_label = SpanLabel { - span: substitution.span, + span: suggestion.span, is_primary: true, label: None, }; DiagnosticSpan::from_span_label(span_label, - Some(suggestion), + Some(&suggestion.snippet), je) }) }) .collect() } - - fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec { - match *rsp { - RenderSpan::FullSpan(ref msp) => - DiagnosticSpan::from_multispan(msp, je), - // regular diagnostics don't produce this anymore - // FIXME(oli_obk): remove it entirely - RenderSpan::Suggestion(_) => unreachable!(), - } - } } impl DiagnosticSpanLine { @@ -342,9 +359,12 @@ impl DiagnosticSpanLine { } impl DiagnosticCode { - fn map_opt_string(s: Option, je: &JsonEmitter) -> Option { + fn map_opt_string(s: Option, je: &JsonEmitter) -> Option { s.map(|s| { - + let s = match s { + DiagnosticId::Error(s) => s, + DiagnosticId::Lint(s) => s, + }; let explanation = je.registry .as_ref() .and_then(|registry| registry.find_description(&s)); @@ -356,9 +376,3 @@ impl DiagnosticCode { }) } } - -impl JsonEmitter { - fn render(&self, suggestion: &CodeSuggestion) -> Vec { - suggestion.splice_lines(&*self.cm).iter().map(|line| line.0.to_owned()).collect() - } -} diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 48c92873e146d..053746b579dcb 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -105,11 +105,10 @@ impl<'a> Parser<'a> { let span = self.span; self.diagnostic() .struct_span_err(span, reason) - .note("inner attributes and doc comments, like `#![no_std]` or \ - `//! My crate`, annotate the item enclosing them, and are \ - usually found at the beginning of source files. Outer \ - attributes and doc comments, like `#[test]` and - `/// My function`, annotate the item following them.") + .note("inner attributes, like `#![no_std]`, annotate the item \ + enclosing them, and are usually found at the beginning of \ + source files. Outer attributes, like `#[test]`, annotate the \ + item following them.") .emit() } ast::AttrStyle::Inner diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 1cb7b0eca58d0..6f20104dda5d7 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -73,6 +73,13 @@ impl<'a> StringReader<'a> { fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { unwrap_or!(self.override_span, Span::new(lo, hi, NO_EXPANSION)) } + fn mk_ident(&self, string: &str) -> Ident { + let mut ident = Ident::from_str(string); + if let Some(span) = self.override_span { + ident.ctxt = span.ctxt(); + } + ident + } fn next_token(&mut self) -> TokenAndSpan where Self: Sized { let res = self.try_next_token(); @@ -433,6 +440,7 @@ impl<'a> StringReader<'a> { self.filemap.record_multibyte_char(self.pos, new_ch_len); } } + self.filemap.record_width(self.pos, new_ch); } else { self.ch = None; self.pos = new_pos; @@ -1102,7 +1110,7 @@ impl<'a> StringReader<'a> { token::Underscore } else { // FIXME: perform NFKC normalization here. (Issue #2253) - token::Ident(Ident::from_str(string)) + token::Ident(self.mk_ident(string)) } })); } @@ -1285,13 +1293,13 @@ impl<'a> StringReader<'a> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let ident = self.with_str_from(start, |lifetime_name| { - Ident::from_str(&format!("'{}", lifetime_name)) + self.mk_ident(&format!("'{}", lifetime_name)) }); // Conjure up a "keyword checking ident" to make sure that // the lifetime name is not a keyword. let keyword_checking_ident = self.with_str_from(start, |lifetime_name| { - Ident::from_str(lifetime_name) + self.mk_ident(lifetime_name) }); let keyword_checking_token = &token::Ident(keyword_checking_ident); let last_bpos = self.pos; @@ -1721,7 +1729,9 @@ mod tests { use std::rc::Rc; fn mk_sess(cm: Rc) -> ParseSess { - let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), Some(cm.clone())); + let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), + Some(cm.clone()), + false); ParseSess { span_diagnostic: errors::Handler::with_emitter(true, false, Box::new(emitter)), unstable_features: UnstableFeatures::from_environment(), diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs index 39b5482a066d4..35afe8dd56d93 100644 --- a/src/libsyntax/parse/lexer/unicode_chars.rs +++ b/src/libsyntax/parse/lexer/unicode_chars.rs @@ -144,7 +144,7 @@ const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[ ('‵', "Reversed Prime", '\''), ('՚', "Armenian Apostrophe", '\''), ('׳', "Hebrew Punctuation Geresh", '\''), - ('`', "Greek Accent", '\''), + ('`', "Grave Accent", '\''), ('`', "Greek Varia", '\''), ('`', "Fullwidth Grave Accent", '\''), ('´', "Acute Accent", '\''), diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index 078e86aa2941f..49a697edf4164 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -58,7 +58,7 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> { }; if !self.obsolete_set.contains(&kind) && - (error || self.sess.span_diagnostic.can_emit_warnings) { + (error || self.sess.span_diagnostic.flags.can_emit_warnings) { err.note(desc); self.obsolete_set.insert(kind); } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d5ba4b54d9014..2461e65585f5a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -21,7 +21,7 @@ use ast::EnumDef; use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; -use ast::{Ident, ImplItem, Item, ItemKind}; +use ast::{Ident, ImplItem, IsAuto, Item, ItemKind}; use ast::{Lifetime, LifetimeDef, Lit, LitKind, UintTy}; use ast::Local; use ast::MacStmtStyle; @@ -33,10 +33,10 @@ use ast::{Stmt, StmtKind}; use ast::{VariantData, StructField}; use ast::StrStyle; use ast::SelfKind; -use ast::{TraitItem, TraitRef}; +use ast::{TraitItem, TraitRef, TraitObjectSyntax}; use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds}; -use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; -use ast::{Visibility, WhereClause}; +use ast::{Visibility, WhereClause, CrateSugar}; +use ast::{UseTree, UseTreeKind}; use ast::{BinOpKind, UnOp}; use ast::{RangeEnd, RangeSyntax}; use {ast, attr}; @@ -360,8 +360,11 @@ impl TokenType { } } -fn is_ident_or_underscore(t: &token::Token) -> bool { - t.is_ident() || *t == token::Underscore +// Returns true if `IDENT t` can start a type - `IDENT::a::b`, `IDENT`, +// `IDENT<::AssocTy>`, `IDENT(u8, u8) -> u8`. +fn can_continue_type_after_ident(t: &token::Token) -> bool { + t == &token::ModSep || t == &token::Lt || + t == &token::BinOp(token::Shl) || t == &token::OpenDelim(token::Paren) } /// Information about the path to a module. @@ -525,7 +528,7 @@ impl<'a> Parser<'a> { if let Some(directory) = directory { parser.directory = directory; } else if parser.span != syntax_pos::DUMMY_SP { - parser.directory.path = PathBuf::from(sess.codemap().span_to_filename(parser.span)); + parser.directory.path = sess.codemap().span_to_unmapped_path(parser.span); parser.directory.path.pop(); } @@ -657,11 +660,28 @@ impl<'a> Parser<'a> { } else { label_sp }; - if self.span.contains(sp) { - err.span_label(self.span, label_exp); - } else { - err.span_label(sp, label_exp); - err.span_label(self.span, "unexpected token"); + + let cm = self.sess.codemap(); + match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) { + (Ok(ref a), Ok(ref b)) if a.line == b.line => { + // When the spans are in the same line, it means that the only content between + // them is whitespace, point at the found token in that case: + // + // X | () => { syntax error }; + // | ^^^^^ expected one of 8 possible tokens here + // + // instead of having: + // + // X | () => { syntax error }; + // | -^^^^^ unexpected token + // | | + // | expected one of 8 possible tokens here + err.span_label(self.span, label_exp); + } + _ => { + err.span_label(sp, label_exp); + err.span_label(self.span, "unexpected token"); + } } Err(err) } @@ -967,11 +987,12 @@ impl<'a> Parser<'a> { pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) { let handler = self.diagnostic(); - self.parse_seq_to_before_tokens(kets, - SeqSep::none(), - TokenExpectType::Expect, - |p| Ok(p.parse_token_tree()), - |mut e| handler.cancel(&mut e)); + if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets, + SeqSep::none(), + TokenExpectType::Expect, + |p| Ok(p.parse_token_tree())) { + handler.cancel(err); + } } /// Parse a sequence, including the closing delimiter. The function @@ -984,7 +1005,7 @@ impl<'a> Parser<'a> { -> PResult<'a, Vec> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { - let val = self.parse_seq_to_before_end(ket, sep, f); + let val = self.parse_seq_to_before_end(ket, sep, f)?; self.bump(); Ok(val) } @@ -996,22 +1017,19 @@ impl<'a> Parser<'a> { ket: &token::Token, sep: SeqSep, f: F) - -> Vec - where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> + -> PResult<'a, Vec> + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> { - self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f, |mut e| e.emit()) + self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) } - // `fe` is an error handler. - fn parse_seq_to_before_tokens(&mut self, + fn parse_seq_to_before_tokens(&mut self, kets: &[&token::Token], sep: SeqSep, expect: TokenExpectType, - mut f: F, - mut fe: Fe) - -> Vec - where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, - Fe: FnMut(DiagnosticBuilder) + mut f: F) + -> PResult<'a, Vec> + where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> { let mut first: bool = true; let mut v = vec![]; @@ -1024,9 +1042,25 @@ impl<'a> Parser<'a> { if first { first = false; } else { - if let Err(e) = self.expect(t) { - fe(e); - break; + if let Err(mut e) = self.expect(t) { + // Attempt to keep parsing if it was a similar separator + if let Some(ref tokens) = t.similar_tokens() { + if tokens.contains(&self.token) { + self.bump(); + } + } + e.emit(); + // Attempt to keep parsing if it was an omitted separator + match f(self) { + Ok(t) => { + v.push(t); + continue; + }, + Err(mut e) => { + e.cancel(); + break; + } + } } } } @@ -1039,16 +1073,11 @@ impl<'a> Parser<'a> { break; } - match f(self) { - Ok(t) => v.push(t), - Err(e) => { - fe(e); - break; - } - } + let t = f(self)?; + v.push(t); } - v + Ok(v) } /// Parse a sequence, including the closing delimiter. The function @@ -1063,7 +1092,7 @@ impl<'a> Parser<'a> { F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, { self.expect(bra)?; - let result = self.parse_seq_to_before_end(ket, sep, f); + let result = self.parse_seq_to_before_end(ket, sep, f)?; if self.token == *ket { self.bump(); } @@ -1082,7 +1111,7 @@ impl<'a> Parser<'a> { { let lo = self.span; self.expect(bra)?; - let result = self.parse_seq_to_before_end(ket, sep, f); + let result = self.parse_seq_to_before_end(ket, sep, f)?; let hi = self.span; self.bump(); Ok(respan(lo.to(hi), result)) @@ -1144,6 +1173,7 @@ impl<'a> Parser<'a> { None => token::CloseDelim(self.token_cursor.frame.delim), }) } + fn look_ahead_span(&self, dist: usize) -> Span { if dist == 0 { return self.span @@ -1280,10 +1310,10 @@ impl<'a> Parser<'a> { mut attrs: Vec) -> PResult<'a, TraitItem> { let lo = self.span; - let (name, node) = if self.eat_keyword(keywords::Type) { - let TyParam {ident, bounds, default, ..} = self.parse_ty_param(vec![])?; - self.expect(&token::Semi)?; - (ident, TraitItemKind::Type(bounds, default)) + let (name, node, generics) = if self.eat_keyword(keywords::Type) { + let (generics, TyParam {ident, bounds, default, ..}) = + self.parse_trait_item_assoc_ty(vec![])?; + (ident, TraitItemKind::Type(bounds, default), generics) } else if self.is_const_item() { self.expect_keyword(keywords::Const)?; let ident = self.parse_ident()?; @@ -1298,7 +1328,7 @@ impl<'a> Parser<'a> { self.expect(&token::Semi)?; None }; - (ident, TraitItemKind::Const(ty, default)) + (ident, TraitItemKind::Const(ty, default), ast::Generics::default()) } else if self.token.is_path_start() { // trait item macro. // code copied from parse_macro_use_or_failure... abstraction! @@ -1321,7 +1351,7 @@ impl<'a> Parser<'a> { } let mac = respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts }); - (keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac)) + (keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default()) } else { let (constness, unsafety, abi) = self.parse_fn_front_matter()?; @@ -1334,13 +1364,12 @@ impl<'a> Parser<'a> { // definition... p.parse_arg_general(false) })?; - generics.where_clause = self.parse_where_clause()?; + let sig = ast::MethodSig { unsafety, constness, decl: d, - generics, abi, }; @@ -1363,13 +1392,14 @@ impl<'a> Parser<'a> { return Err(self.fatal(&format!("expected `;` or `{{`, found `{}`", token_str))); } }; - (ident, ast::TraitItemKind::Method(sig, body)) + (ident, ast::TraitItemKind::Method(sig, body), generics) }; Ok(TraitItem { id: ast::DUMMY_NODE_ID, ident: name, attrs, + generics, node, span: lo.to(self.prev_span), tokens: None, @@ -1428,7 +1458,7 @@ impl<'a> Parser<'a> { TyKind::Path(None, ref path) if maybe_bounds => { self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)? } - TyKind::TraitObject(ref bounds) + TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) if maybe_bounds && bounds.len() == 1 && !trailing_plus => { let path = match bounds[0] { TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(), @@ -1472,27 +1502,6 @@ impl<'a> Parser<'a> { } else if self.eat(&token::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.eat_lt() { - // Qualified path - let (qself, path) = self.parse_qpath(PathStyle::Type)?; - TyKind::Path(Some(qself), path) - } else if self.token.is_path_start() { - // Simple path - let path = self.parse_path(PathStyle::Type)?; - if self.eat(&token::Not) { - // Macro invocation in type position - let (_, tts) = self.expect_delimited_token_tree()?; - TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts })) - } else { - // Just a type path or bound list (trait object type) starting with a trait. - // `Type` - // `Trait1 + Trait2 + 'a` - if allow_plus && self.check(&token::BinOp(token::Plus)) { - self.parse_remaining_bounds(Vec::new(), path, lo, true)? - } else { - TyKind::Path(None, path) - } - } } else if self.token_is_bare_fn_keyword() { // Function pointer type self.parse_ty_bare_fn(Vec::new())? @@ -1512,17 +1521,44 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(keywords::Impl) { // FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511). TyKind::ImplTrait(self.parse_ty_param_bounds()?) + } else if self.check_keyword(keywords::Dyn) && + self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) { + // FIXME: figure out priority of `+` in `dyn Trait1 + Trait2` (#34511). + self.bump(); // `dyn` + TyKind::TraitObject(self.parse_ty_param_bounds()?, TraitObjectSyntax::Dyn) } else if self.check(&token::Question) || - self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)){ + self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) { // Bound list (trait object type) - TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?) + TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?, + TraitObjectSyntax::None) + } else if self.eat_lt() { + // Qualified path + let (qself, path) = self.parse_qpath(PathStyle::Type)?; + TyKind::Path(Some(qself), path) + } else if self.token.is_path_start() { + // Simple path + let path = self.parse_path(PathStyle::Type)?; + if self.eat(&token::Not) { + // Macro invocation in type position + let (_, tts) = self.expect_delimited_token_tree()?; + TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts })) + } else { + // Just a type path or bound list (trait object type) starting with a trait. + // `Type` + // `Trait1 + Trait2 + 'a` + if allow_plus && self.check(&token::BinOp(token::Plus)) { + self.parse_remaining_bounds(Vec::new(), path, lo, true)? + } else { + TyKind::Path(None, path) + } + } } else { let msg = format!("expected type, found {}", self.this_token_descr()); return Err(self.fatal(&msg)); }; let span = lo.to(self.prev_span); - let ty = Ty { node: node, span: span, id: ast::DUMMY_NODE_ID }; + let ty = Ty { node, span, id: ast::DUMMY_NODE_ID }; // Try to recover from use of `+` with incorrect priority. self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?; @@ -1538,7 +1574,7 @@ impl<'a> Parser<'a> { self.bump(); // `+` bounds.append(&mut self.parse_ty_param_bounds()?); } - Ok(TyKind::TraitObject(bounds)) + Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> { @@ -1603,23 +1639,19 @@ impl<'a> Parser<'a> { Ok(MutTy { ty: t, mutbl: mutbl }) } - pub fn is_named_argument(&mut self) -> bool { + fn is_named_argument(&mut self) -> bool { let offset = match self.token { - token::BinOp(token::And) | - token::AndAnd => 1, + token::Interpolated(ref nt) => match nt.0 { + token::NtPat(..) => return self.look_ahead(1, |t| t == &token::Colon), + _ => 0, + } + token::BinOp(token::And) | token::AndAnd => 1, _ if self.token.is_keyword(keywords::Mut) => 1, - _ => 0 + _ => 0, }; - debug!("parser is_named_argument offset:{}", offset); - - if offset == 0 { - is_ident_or_underscore(&self.token) - && self.look_ahead(1, |t| *t == token::Colon) - } else { - self.look_ahead(offset, |t| is_ident_or_underscore(t)) - && self.look_ahead(offset + 1, |t| *t == token::Colon) - } + self.look_ahead(offset, |t| t.is_ident() || t == &token::Underscore) && + self.look_ahead(offset + 1, |t| t == &token::Colon) } /// This version of parse arg doesn't necessarily require @@ -1839,12 +1871,15 @@ impl<'a> Parser<'a> { self.parse_path(style) } - fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle, - enable_warning: bool) -> PResult<'a, ()> { + fn parse_path_segments(&mut self, + segments: &mut Vec, + style: PathStyle, + enable_warning: bool) + -> PResult<'a, ()> { loop { segments.push(self.parse_path_segment(style, enable_warning)?); - if self.is_import_coupler() || !self.eat(&token::ModSep) { + if self.is_import_coupler(false) || !self.eat(&token::ModSep) { return Ok(()); } } @@ -1885,9 +1920,12 @@ impl<'a> Parser<'a> { } else { // `(T, U) -> R` self.bump(); // `(` - let inputs = self.parse_seq_to_end(&token::CloseDelim(token::Paren), - SeqSep::trailing_allowed(token::Comma), - |p| p.parse_ty())?; + let inputs = self.parse_seq_to_before_tokens( + &[&token::CloseDelim(token::Paren)], + SeqSep::trailing_allowed(token::Comma), + TokenExpectType::Expect, + |p| p.parse_ty())?; + self.bump(); // `)` let output = if self.eat(&token::RArrow) { Some(self.parse_ty_no_plus()?) } else { @@ -2314,6 +2352,7 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { + let exp_span = self.prev_span; match self.parse_expr() { Ok(e) => { base = Some(e); @@ -2323,6 +2362,16 @@ impl<'a> Parser<'a> { self.recover_stmt(); } } + if self.token == token::Comma { + let mut err = self.sess.span_diagnostic.mut_span_err( + exp_span.to(self.prev_span), + "cannot use a comma after the base struct", + ); + err.span_suggestion_short(self.span, "remove this comma", "".to_owned()); + err.note("the base struct must always be the last field"); + err.emit(); + self.recover_stmt(); + } break; } @@ -2744,10 +2793,11 @@ impl<'a> Parser<'a> { if op.precedence() < min_prec { break; } - // Warn about deprecated ... syntax (until SNAP) - if self.token == token::DotDotDot { - self.warn_dotdoteq(self.span); + // Check for deprecated `...` syntax + if self.token == token::DotDotDot && op == AssocOp::DotDotEq { + self.err_dotdotdot_syntax(self.span); } + self.bump(); if op.is_comparison() { self.check_no_chained_comparison(&lhs, &op); @@ -2780,7 +2830,6 @@ impl<'a> Parser<'a> { // // We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other // two variants are handled with `parse_prefix_range_expr` call above. - // (and `x...y`/`x...` until SNAP) let rhs = if self.is_at_start_of_range_notation_rhs() { Some(self.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)?) @@ -2890,17 +2939,30 @@ impl<'a> Parser<'a> { match self.parse_path(PathStyle::Expr) { Ok(path) => { + let (op_noun, op_verb) = match self.token { + token::Lt => ("comparison", "comparing"), + token::BinOp(token::Shl) => ("shift", "shifting"), + _ => { + // We can end up here even without `<` being the next token, for + // example because `parse_ty_no_plus` returns `Err` on keywords, + // but `parse_path` returns `Ok` on them due to error recovery. + // Return original error and parser state. + mem::replace(self, parser_snapshot_after_type); + return Err(type_err); + } + }; + // Successfully parsed the type path leaving a `<` yet to parse. type_err.cancel(); // Report non-fatal diagnostics, keep `x as usize` as an expression // in AST and continue parsing. let msg = format!("`<` is interpreted as a start of generic \ - arguments for `{}`, not a comparison", path); + arguments for `{}`, not a {}", path, op_noun); let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg); err.span_label(self.look_ahead_span(1).to(parser_snapshot_after_type.span), "interpreted as generic arguments"); - err.span_label(self.span, "not interpreted as comparison"); + err.span_label(self.span, format!("not interpreted as {}", op_noun)); let expr = mk_expr(self, P(Ty { span: path.span, @@ -2911,7 +2973,7 @@ impl<'a> Parser<'a> { let expr_str = self.sess.codemap().span_to_snippet(expr.span) .unwrap_or(pprust::expr_to_string(&expr)); err.span_suggestion(expr.span, - "try comparing the casted value", + &format!("try {} the casted value", op_verb), format!("({})", expr_str)); err.emit(); @@ -2947,6 +3009,7 @@ impl<'a> Parser<'a> { { // Foo>> err.help( "use `::<...>` instead of `<...>` if you meant to specify type arguments"); + err.help("or use `(...)` if you meant to specify fn arguments"); } err.emit(); } @@ -2954,22 +3017,22 @@ impl<'a> Parser<'a> { } } - /// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr` (and `...expr` until SNAP) + /// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr` fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option>) -> PResult<'a, P> { - // SNAP remove DotDotDot + // Check for deprecated `...` syntax + if self.token == token::DotDotDot { + self.err_dotdotdot_syntax(self.span); + } + debug_assert!([token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token), - "parse_prefix_range_expr: token {:?} is not DotDot/DotDotDot/DotDotEq", + "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", self.token); let tok = self.token.clone(); let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let lo = self.span; let mut hi = self.span; - // Warn about deprecated ... syntax (until SNAP) - if tok == token::DotDotDot { - self.warn_dotdoteq(self.span); - } self.bump(); let opt_end = if self.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. @@ -3100,7 +3163,13 @@ impl<'a> Parser<'a> { // Parse: `for in ` let pat = self.parse_pat()?; - self.expect_keyword(keywords::In)?; + if !self.eat_keyword(keywords::In) { + let in_span = self.prev_span.between(self.span); + let mut err = self.sess.span_diagnostic + .struct_span_err(in_span, "missing `in` in `for` loop"); + err.span_suggestion_short(in_span, "try adding `in` here", " in ".into()); + err.emit(); + } let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); @@ -3255,10 +3324,12 @@ impl<'a> Parser<'a> { } /// Parse the RHS of a local variable declaration (e.g. '= 14;') - fn parse_initializer(&mut self) -> PResult<'a, Option>> { + fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option>> { if self.check(&token::Eq) { self.bump(); Ok(Some(self.parse_expr()?)) + } else if skip_eq { + Ok(Some(self.parse_expr()?)) } else { Ok(None) } @@ -3665,18 +3736,67 @@ impl<'a> Parser<'a> { let lo = self.prev_span; let pat = self.parse_pat()?; - let ty = if self.eat(&token::Colon) { - Some(self.parse_ty()?) + let (err, ty) = if self.eat(&token::Colon) { + // Save the state of the parser before parsing type normally, in case there is a `:` + // instead of an `=` typo. + let parser_snapshot_before_type = self.clone(); + let colon_sp = self.prev_span; + match self.parse_ty() { + Ok(ty) => (None, Some(ty)), + Err(mut err) => { + // Rewind to before attempting to parse the type and continue parsing + let parser_snapshot_after_type = self.clone(); + mem::replace(self, parser_snapshot_before_type); + + let snippet = self.sess.codemap().span_to_snippet(pat.span).unwrap(); + err.span_label(pat.span, format!("while parsing the type for `{}`", snippet)); + (Some((parser_snapshot_after_type, colon_sp, err)), None) + } + } } else { - None + (None, None) + }; + let init = match (self.parse_initializer(err.is_some()), err) { + (Ok(init), None) => { // init parsed, ty parsed + init + } + (Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error + // Could parse the type as if it were the initializer, it is likely there was a + // typo in the code: `:` instead of `=`. Add suggestion and emit the error. + err.span_suggestion_short(colon_sp, + "use `=` if you meant to assign", + "=".to_string()); + err.emit(); + // As this was parsed successfuly, continue as if the code has been fixed for the + // rest of the file. It will still fail due to the emitted error, but we avoid + // extra noise. + init + } + (Err(mut init_err), Some((snapshot, _, ty_err))) => { // init error, ty error + init_err.cancel(); + // Couldn't parse the type nor the initializer, only raise the type error and + // return to the parser state before parsing the type as the initializer. + // let x: ; + mem::replace(self, snapshot); + return Err(ty_err); + } + (Err(err), None) => { // init error, ty parsed + // Couldn't parse the initializer and we're not attempting to recover a failed + // parse of the type, return the error. + return Err(err); + } + }; + let hi = if self.token == token::Semi { + self.span + } else { + self.prev_span }; - let init = self.parse_initializer()?; Ok(P(ast::Local { ty, pat, init, id: ast::DUMMY_NODE_ID, - span: lo.to(self.prev_span), + span: lo.to(hi), attrs, })) } @@ -3814,6 +3934,20 @@ impl<'a> Parser<'a> { self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } + fn is_crate_vis(&self) -> bool { + self.token.is_keyword(keywords::Crate) && self.look_ahead(1, |t| t != &token::ModSep) + } + + fn eat_auto_trait(&mut self) -> bool { + if self.token.is_keyword(keywords::Auto) + && self.look_ahead(1, |t| t.is_keyword(keywords::Trait)) + { + self.eat_keyword(keywords::Auto) && self.eat_keyword(keywords::Trait) + } else { + false + } + } + fn is_defaultness(&self) -> bool { // `pub` is included for better error messages self.token.is_keyword(keywords::Default) && @@ -3914,10 +4048,15 @@ impl<'a> Parser<'a> { node: StmtKind::Item(macro_def), span: lo.to(self.prev_span), } - // Starts like a simple path, but not a union item. + // Starts like a simple path, but not a union item or item with `crate` visibility. + // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts + // like a path (1 token), but it fact not a path. + // `union::b::c` - path, `union U { ... }` - not a path. + // `crate::b::c` - path, `crate struct S;` - not a path. } else if self.token.is_path_start() && !self.token.is_qpath_start() && - !self.is_union_item() { + !self.is_union_item() && + !self.is_crate_vis() { let pth = self.parse_path(PathStyle::Expr)?; if !self.eat(&token::Not) { @@ -4045,11 +4184,11 @@ impl<'a> Parser<'a> { node: StmtKind::Item(i), }, None => { - let unused_attrs = |attrs: &[_], s: &mut Self| { + let unused_attrs = |attrs: &[Attribute], s: &mut Self| { if !attrs.is_empty() { if s.prev_token_kind == PrevTokenKind::DocComment { s.span_fatal_err(s.prev_span, Error::UselessDocComment).emit(); - } else { + } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { s.span_err(s.span, "expected statement after outer attribute"); } } @@ -4147,7 +4286,16 @@ impl<'a> Parser<'a> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - if let Some(stmt) = self.parse_full_stmt(false)? { + let stmt = match self.parse_full_stmt(false) { + Err(mut err) => { + err.emit(); + self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Break); + self.eat(&token::CloseDelim(token::Brace)); + break; + } + Ok(stmt) => stmt, + }; + if let Some(stmt) = stmt { stmts.push(stmt); } else if self.token == token::Eof { break; @@ -4156,7 +4304,6 @@ impl<'a> Parser<'a> { continue; }; } - Ok(P(ast::Block { stmts, id: ast::DUMMY_NODE_ID, @@ -4212,9 +4359,13 @@ impl<'a> Parser<'a> { }).emit(); } - fn warn_dotdoteq(&self, span: Span) { - self.diagnostic().struct_span_warn(span, { - "`...` is being replaced by `..=`" + fn err_dotdotdot_syntax(&self, span: Span) { + self.diagnostic().struct_span_err(span, { + "`...` syntax cannot be used in expressions" + }).help({ + "Use `..` if you need an exclusive range (a < b)" + }).help({ + "or `..=` if you need an inclusive range (a <= b)" }).emit(); } @@ -4226,6 +4377,7 @@ impl<'a> Parser<'a> { fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> { let mut bounds = Vec::new(); loop { + // This needs to be syncronized with `Token::can_begin_bound`. let is_bound_start = self.check_path() || self.check_lifetime() || self.check(&token::Question) || self.check_keyword(keywords::For) || @@ -4316,6 +4468,39 @@ impl<'a> Parser<'a> { }) } + /// Parses the following grammar: + /// TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty] + fn parse_trait_item_assoc_ty(&mut self, preceding_attrs: Vec) + -> PResult<'a, (ast::Generics, TyParam)> { + let span = self.span; + let ident = self.parse_ident()?; + let mut generics = self.parse_generics()?; + + // Parse optional colon and param bounds. + let bounds = if self.eat(&token::Colon) { + self.parse_ty_param_bounds()? + } else { + Vec::new() + }; + generics.where_clause = self.parse_where_clause()?; + + let default = if self.eat(&token::Eq) { + Some(self.parse_ty()?) + } else { + None + }; + self.expect(&token::Semi)?; + + Ok((generics, TyParam { + attrs: preceding_attrs.into(), + ident, + id: ast::DUMMY_NODE_ID, + bounds, + default, + span, + })) + } + /// Parses (possibly empty) list of lifetime and type parameters, possibly including /// trailing comma and erroneous trailing attributes. pub fn parse_generic_params(&mut self) -> PResult<'a, (Vec, Vec)> { @@ -4721,14 +4906,14 @@ impl<'a> Parser<'a> { } else if self.eat(&token::Comma) { let mut fn_inputs = vec![self_arg]; fn_inputs.append(&mut self.parse_seq_to_before_end( - &token::CloseDelim(token::Paren), sep, parse_arg_fn) + &token::CloseDelim(token::Paren), sep, parse_arg_fn)? ); fn_inputs } else { return self.unexpected(); } } else { - self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn) + self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)? }; // Parse closing paren and return type. @@ -4751,9 +4936,8 @@ impl<'a> Parser<'a> { &[&token::BinOp(token::Or), &token::OrOr], SeqSep::trailing_allowed(token::Comma), TokenExpectType::NoExpect, - |p| p.parse_fn_block_arg(), - |mut e| e.emit() - ); + |p| p.parse_fn_block_arg() + )?; self.expect_or()?; args } @@ -4857,13 +5041,19 @@ impl<'a> Parser<'a> { let lo = self.span; let vis = self.parse_visibility(false)?; let defaultness = self.parse_defaultness()?; - let (name, node) = if self.eat_keyword(keywords::Type) { + let (name, node, generics) = if self.eat_keyword(keywords::Type) { + // This parses the grammar: + // ImplItemAssocTy = Ident ["<"...">"] ["where" ...] "=" Ty ";" let name = self.parse_ident()?; + let mut generics = self.parse_generics()?; + generics.where_clause = self.parse_where_clause()?; self.expect(&token::Eq)?; let typ = self.parse_ty()?; self.expect(&token::Semi)?; - (name, ast::ImplItemKind::Type(typ)) + (name, ast::ImplItemKind::Type(typ), generics) } else if self.is_const_item() { + // This parses the grammar: + // ImplItemConst = "const" Ident ":" Ty "=" Expr ";" self.expect_keyword(keywords::Const)?; let name = self.parse_ident()?; self.expect(&token::Colon)?; @@ -4871,11 +5061,11 @@ impl<'a> Parser<'a> { self.expect(&token::Eq)?; let expr = self.parse_expr()?; self.expect(&token::Semi)?; - (name, ast::ImplItemKind::Const(typ, expr)) + (name, ast::ImplItemKind::Const(typ, expr), ast::Generics::default()) } else { - let (name, inner_attrs, node) = self.parse_impl_method(&vis, at_end)?; + let (name, inner_attrs, generics, node) = self.parse_impl_method(&vis, at_end)?; attrs.extend(inner_attrs); - (name, node) + (name, node, generics) }; Ok(ImplItem { @@ -4885,6 +5075,7 @@ impl<'a> Parser<'a> { vis, defaultness, attrs, + generics, node, tokens: None, }) @@ -4942,7 +5133,8 @@ impl<'a> Parser<'a> { /// Parse a method or a macro invocation in a trait impl. fn parse_impl_method(&mut self, vis: &Visibility, at_end: &mut bool) - -> PResult<'a, (Ident, Vec, ast::ImplItemKind)> { + -> PResult<'a, (Ident, Vec, ast::Generics, + ast::ImplItemKind)> { // code copied from parse_macro_use_or_failure... abstraction! if self.token.is_path_start() { // Method macro. @@ -4969,7 +5161,8 @@ impl<'a> Parser<'a> { } let mac = respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts }); - Ok((keywords::Invalid.ident(), vec![], ast::ImplItemKind::Macro(mac))) + Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(), + ast::ImplItemKind::Macro(mac))) } else { let (constness, unsafety, abi) = self.parse_fn_front_matter()?; let ident = self.parse_ident()?; @@ -4978,8 +5171,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; *at_end = true; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - Ok((ident, inner_attrs, ast::ImplItemKind::Method(ast::MethodSig { - generics, + Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method(ast::MethodSig { abi, unsafety, constness, @@ -4989,7 +5181,7 @@ impl<'a> Parser<'a> { } /// Parse trait Foo { ... } - fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> { + fn parse_item_trait(&mut self, is_auto: IsAuto, unsafety: Unsafety) -> PResult<'a, ItemInfo> { let ident = self.parse_ident()?; let mut tps = self.parse_generics()?; @@ -5016,7 +5208,7 @@ impl<'a> Parser<'a> { } } } - Ok((ident, ItemKind::Trait(unsafety, tps, bounds, trait_items), None)) + Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, trait_items), None)) } /// Parses items implementations variants @@ -5071,19 +5263,19 @@ impl<'a> Parser<'a> { if opt_trait.is_some() && self.eat(&token::DotDot) { if generics.is_parameterized() { - self.span_err(impl_span, "default trait implementations are not \ + self.span_err(impl_span, "auto trait implementations are not \ allowed to have generics"); } if let ast::Defaultness::Default = defaultness { self.span_err(impl_span, "`default impl` is not allowed for \ - default trait implementations"); + auto trait implementations"); } self.expect(&token::OpenDelim(token::Brace))?; self.expect(&token::CloseDelim(token::Brace))?; Ok((keywords::Invalid.ident(), - ItemKind::DefaultImpl(unsafety, opt_trait.unwrap()), None)) + ItemKind::AutoImpl(unsafety, opt_trait.unwrap()), None)) } else { if opt_trait.is_some() { ty = self.parse_ty()?; @@ -5198,18 +5390,45 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics), None)) } + fn consume_block(&mut self, delim: token::DelimToken) { + let mut brace_depth = 0; + if !self.eat(&token::OpenDelim(delim)) { + return; + } + loop { + if self.eat(&token::OpenDelim(delim)) { + brace_depth += 1; + } else if self.eat(&token::CloseDelim(delim)) { + if brace_depth == 0 { + return; + } else { + brace_depth -= 1; + continue; + } + } else if self.eat(&token::Eof) || self.eat(&token::CloseDelim(token::NoDelim)) { + return; + } else { + self.bump(); + } + } + } + pub fn parse_record_struct_body(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); if self.eat(&token::OpenDelim(token::Brace)) { while self.token != token::CloseDelim(token::Brace) { - fields.push(self.parse_struct_decl_field().map_err(|e| { + let field = self.parse_struct_decl_field().map_err(|e| { self.recover_stmt(); - self.eat(&token::CloseDelim(token::Brace)); e - })?); + }); + match field { + Ok(field) => fields.push(field), + Err(mut err) => { + err.emit(); + } + } } - - self.bump(); + self.eat(&token::CloseDelim(token::Brace)); } else { let token_str = self.this_token_to_string(); return Err(self.fatal(&format!("expected `where`, or `{{` after struct \ @@ -5257,8 +5476,15 @@ impl<'a> Parser<'a> { self.bump(); } token::CloseDelim(token::Brace) => {} - token::DocComment(_) => return Err(self.span_fatal_err(self.span, - Error::UselessDocComment)), + token::DocComment(_) => { + let mut err = self.span_fatal_err(self.span, Error::UselessDocComment); + self.bump(); // consume the doc comment + if self.eat(&token::Comma) || self.token == token::CloseDelim(token::Brace) { + err.emit(); + } else { + return Err(err); + } + } _ => return Err(self.span_fatal_help(self.span, &format!("expected `,`, or `}}`, found `{}`", self.this_token_to_string()), "struct fields should be separated by commas")), @@ -5281,6 +5507,12 @@ impl<'a> Parser<'a> { pub fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> { maybe_whole!(self, NtVis, |x| x); + self.expected_tokens.push(TokenType::Keyword(keywords::Crate)); + if self.is_crate_vis() { + self.bump(); // `crate` + return Ok(Visibility::Crate(self.prev_span, CrateSugar::JustCrate)); + } + if !self.eat_keyword(keywords::Pub) { return Ok(Visibility::Inherited) } @@ -5294,7 +5526,7 @@ impl<'a> Parser<'a> { // `pub(crate)` self.bump(); // `(` self.bump(); // `crate` - let vis = Visibility::Crate(self.prev_span); + let vis = Visibility::Crate(self.prev_span, CrateSugar::PubCrate); self.expect(&token::CloseDelim(token::Paren))?; // `)` return Ok(vis) } else if self.look_ahead(1, |t| t.is_keyword(keywords::In)) { @@ -5353,7 +5585,12 @@ impl<'a> Parser<'a> { if !self.eat(term) { let token_str = self.this_token_to_string(); - return Err(self.fatal(&format!("expected item, found `{}`", token_str))); + let mut err = self.fatal(&format!("expected item, found `{}`", token_str)); + let msg = "consider removing this semicolon"; + if token_str == ";" { + err.span_suggestion_short(self.span, msg, "".to_string()); + } + return Err(err); } let hi = if self.span == syntax_pos::DUMMY_SP { @@ -5621,6 +5858,24 @@ impl<'a> Parser<'a> { }) } + /// Parse a type from a foreign module + fn parse_item_foreign_type(&mut self, vis: ast::Visibility, lo: Span, attrs: Vec) + -> PResult<'a, ForeignItem> { + self.expect_keyword(keywords::Type)?; + + let ident = self.parse_ident()?; + let hi = self.span; + self.expect(&token::Semi)?; + Ok(ast::ForeignItem { + ident: ident, + attrs: attrs, + node: ForeignItemKind::Ty, + id: ast::DUMMY_NODE_ID, + span: lo.to(hi), + vis: vis + }) + } + /// Parse extern crate links /// /// # Examples @@ -5808,7 +6063,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(keywords::Use) { // USE ITEM - let item_ = ItemKind::Use(self.parse_view_path()?); + let item_ = ItemKind::Use(P(self.parse_use_tree(false)?)); self.expect(&token::Semi)?; let prev_span = self.prev_span; @@ -5904,13 +6159,19 @@ impl<'a> Parser<'a> { return Ok(Some(item)); } if self.check_keyword(keywords::Unsafe) && - self.look_ahead(1, |t| t.is_keyword(keywords::Trait)) + (self.look_ahead(1, |t| t.is_keyword(keywords::Trait)) || + self.look_ahead(1, |t| t.is_keyword(keywords::Auto))) { // UNSAFE TRAIT ITEM self.expect_keyword(keywords::Unsafe)?; - self.expect_keyword(keywords::Trait)?; + let is_auto = if self.eat_keyword(keywords::Trait) { + IsAuto::No + } else { + self.eat_auto_trait(); + IsAuto::Yes + }; let (ident, item_, extra_attrs) = - self.parse_item_trait(ast::Unsafety::Unsafe)?; + self.parse_item_trait(is_auto, ast::Unsafety::Unsafe)?; let prev_span = self.prev_span; let item = self.mk_item(lo.to(prev_span), ident, @@ -6013,10 +6274,19 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if self.eat_keyword(keywords::Trait) { + if self.check_keyword(keywords::Trait) + || (self.check_keyword(keywords::Auto) + && self.look_ahead(1, |t| t.is_keyword(keywords::Trait))) + { + let is_auto = if self.eat_keyword(keywords::Trait) { + IsAuto::No + } else { + self.eat_auto_trait(); + IsAuto::Yes + }; // TRAIT ITEM let (ident, item_, extra_attrs) = - self.parse_item_trait(ast::Unsafety::Normal)?; + self.parse_item_trait(is_auto, ast::Unsafety::Normal)?; let prev_span = self.prev_span; let item = self.mk_item(lo.to(prev_span), ident, @@ -6070,7 +6340,65 @@ impl<'a> Parser<'a> { return Ok(Some(macro_def)); } - self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility) + // Verify wether we have encountered a struct or method definition where the user forgot to + // add the `struct` or `fn` keyword after writing `pub`: `pub S {}` + if visibility == Visibility::Public && + self.check_ident() && + self.look_ahead(1, |t| *t != token::Not) + { + // Space between `pub` keyword and the identifier + // + // pub S {} + // ^^^ `sp` points here + let sp = self.prev_span.between(self.span); + let full_sp = self.prev_span.to(self.span); + let ident_sp = self.span; + if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) { + // possible public struct definition where `struct` was forgotten + let ident = self.parse_ident().unwrap(); + let msg = format!("add `struct` here to parse `{}` as a public struct", + ident); + let mut err = self.diagnostic() + .struct_span_err(sp, "missing `struct` for struct definition"); + err.span_suggestion_short(sp, &msg, " struct ".into()); + return Err(err); + } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) { + let ident = self.parse_ident().unwrap(); + self.consume_block(token::Paren); + let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) || + self.check(&token::OpenDelim(token::Brace)) + { + ("fn", "method", false) + } else if self.check(&token::Colon) { + let kw = "struct"; + (kw, kw, false) + } else { + ("fn` or `struct", "method or struct", true) + }; + + let msg = format!("missing `{}` for {} definition", kw, kw_name); + let mut err = self.diagnostic().struct_span_err(sp, &msg); + if !ambiguous { + let suggestion = format!("add `{}` here to parse `{}` as a public {}", + kw, + ident, + kw_name); + err.span_suggestion_short(sp, &suggestion, format!(" {} ", kw)); + } else { + if let Ok(snippet) = self.sess.codemap().span_to_snippet(ident_sp) { + err.span_suggestion( + full_sp, + "if you meant to call a macro, write instead", + format!("{}!", snippet)); + } else { + err.help("if you meant to call a macro, remove the `pub` \ + and add a trailing `!` after the identifier"); + } + } + return Err(err); + } + } + self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, visibility) } /// Parse a foreign item. @@ -6095,6 +6423,10 @@ impl<'a> Parser<'a> { if self.check_keyword(keywords::Fn) { return Ok(Some(self.parse_item_foreign_fn(visibility, lo, attrs)?)); } + // FOREIGN TYPE ITEM + if self.check_keyword(keywords::Type) { + return Ok(Some(self.parse_item_foreign_type(visibility, lo, attrs)?)); + } // FIXME #5668: this will occur for a macro invocation: match self.parse_macro_use_or_failure(attrs, true, false, lo, visibility)? { @@ -6232,74 +6564,101 @@ impl<'a> Parser<'a> { })) } - fn parse_path_list_items(&mut self) -> PResult<'a, Vec> { - self.parse_unspanned_seq(&token::OpenDelim(token::Brace), - &token::CloseDelim(token::Brace), - SeqSep::trailing_allowed(token::Comma), |this| { - let lo = this.span; - let ident = if this.eat_keyword(keywords::SelfValue) { - keywords::SelfValue.ident() - } else { - this.parse_ident()? - }; - let rename = this.parse_rename()?; - let node = ast::PathListItem_ { - name: ident, - rename, - id: ast::DUMMY_NODE_ID - }; - Ok(respan(lo.to(this.prev_span), node)) - }) + /// `{` or `::{` or `*` or `::*` + /// `::{` or `::*` (also `{` or `*` if unprefixed is true) + fn is_import_coupler(&mut self, unprefixed: bool) -> bool { + self.is_import_coupler_inner(&token::OpenDelim(token::Brace), unprefixed) || + self.is_import_coupler_inner(&token::BinOp(token::Star), unprefixed) } - /// `::{` or `::*` - fn is_import_coupler(&mut self) -> bool { - self.check(&token::ModSep) && - self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace) || - *t == token::BinOp(token::Star)) + fn is_import_coupler_inner(&mut self, token: &token::Token, unprefixed: bool) -> bool { + if self.check(&token::ModSep) { + self.look_ahead(1, |t| t == token) + } else if unprefixed { + self.check(token) + } else { + false + } } - /// Matches ViewPath: - /// MOD_SEP? non_global_path - /// MOD_SEP? non_global_path as IDENT - /// MOD_SEP? non_global_path MOD_SEP STAR - /// MOD_SEP? non_global_path MOD_SEP LBRACE item_seq RBRACE - /// MOD_SEP? LBRACE item_seq RBRACE - fn parse_view_path(&mut self) -> PResult<'a, P> { + /// Parse UseTree + /// + /// USE_TREE = `*` | + /// `{` USE_TREE_LIST `}` | + /// PATH `::` `*` | + /// PATH `::` `{` USE_TREE_LIST `}` | + /// PATH [`as` IDENT] + fn parse_use_tree(&mut self, nested: bool) -> PResult<'a, UseTree> { let lo = self.span; - if self.check(&token::OpenDelim(token::Brace)) || self.check(&token::BinOp(token::Star)) || - self.is_import_coupler() { - // `{foo, bar}`, `::{foo, bar}`, `*`, or `::*`. - self.eat(&token::ModSep); - let prefix = ast::Path { - segments: vec![PathSegment::crate_root(lo)], - span: lo.to(self.span), - }; - let view_path_kind = if self.eat(&token::BinOp(token::Star)) { - ViewPathGlob(prefix) + + let mut prefix = ast::Path { + segments: vec![], + span: lo.to(self.span), + }; + + let kind = if self.is_import_coupler(true) { + // `use *;` or `use ::*;` or `use {...};` `use ::{...};` + + // Remove the first `::` + if self.eat(&token::ModSep) { + prefix.segments.push(PathSegment::crate_root(self.prev_span)); + } else if !nested { + prefix.segments.push(PathSegment::crate_root(self.span)); + } + + if self.eat(&token::BinOp(token::Star)) { + // `use *;` + UseTreeKind::Glob + } else if self.check(&token::OpenDelim(token::Brace)) { + // `use {...};` + UseTreeKind::Nested(self.parse_use_tree_list()?) } else { - ViewPathList(prefix, self.parse_path_list_items()?) - }; - Ok(P(respan(lo.to(self.span), view_path_kind))) + return self.unexpected(); + } } else { - let prefix = self.parse_path(PathStyle::Mod)?.default_to_global(); - if self.is_import_coupler() { - // `foo::bar::{a, b}` or `foo::bar::*` - self.bump(); - if self.check(&token::BinOp(token::Star)) { - self.bump(); - Ok(P(respan(lo.to(self.span), ViewPathGlob(prefix)))) + // `use path::...;` + let mut parsed = self.parse_path(PathStyle::Mod)?; + if !nested { + parsed = parsed.default_to_global(); + } + + prefix.segments.append(&mut parsed.segments); + prefix.span = prefix.span.to(parsed.span); + + if self.eat(&token::ModSep) { + if self.eat(&token::BinOp(token::Star)) { + // `use path::*;` + UseTreeKind::Glob + } else if self.check(&token::OpenDelim(token::Brace)) { + // `use path::{...};` + UseTreeKind::Nested(self.parse_use_tree_list()?) } else { - let items = self.parse_path_list_items()?; - Ok(P(respan(lo.to(self.span), ViewPathList(prefix, items)))) + return self.unexpected(); } } else { - // `foo::bar` or `foo::bar as baz` + // `use path::foo;` or `use path::foo as bar;` let rename = self.parse_rename()?. unwrap_or(prefix.segments.last().unwrap().identifier); - Ok(P(respan(lo.to(self.prev_span), ViewPathSimple(rename, prefix)))) + UseTreeKind::Simple(rename) } - } + }; + + Ok(UseTree { + span: lo.to(self.prev_span), + kind, + prefix, + }) + } + + /// Parse UseTreeKind::Nested(list) + /// + /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`] + fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> { + self.parse_unspanned_seq(&token::OpenDelim(token::Brace), + &token::CloseDelim(token::Brace), + SeqSep::trailing_allowed(token::Comma), |this| { + Ok((this.parse_use_tree(true)?, ast::DUMMY_NODE_ID)) + }) } fn parse_rename(&mut self) -> PResult<'a, Option> { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 4888654fac9d0..ff87f146c0a71 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -222,8 +222,8 @@ impl Token { BinOp(Or) | OrOr | // closure BinOp(And) | // reference AndAnd | // double reference + // DotDotDot is no longer supported, but we need some way to display the error DotDot | DotDotDot | DotDotEq | // range notation - // SNAP remove DotDotDot Lt | BinOp(Shl) | // associated path ModSep | // global path Pound => true, // expression attributes @@ -258,6 +258,12 @@ impl Token { } } + /// Returns `true` if the token can appear at the start of a generic bound. + pub fn can_begin_bound(&self) -> bool { + self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) || + self == &Question || self == &OpenDelim(Paren) + } + /// Returns `true` if the token is any literal pub fn is_lit(&self) -> bool { match *self { @@ -341,6 +347,7 @@ impl Token { Some(id) => id.name == keywords::Super.name() || id.name == keywords::SelfValue.name() || id.name == keywords::SelfType.name() || + id.name == keywords::Crate.name() || id.name == keywords::DollarCrate.name(), None => false, } @@ -427,6 +434,16 @@ impl Token { }) } + /// Returns tokens that are likely to be typed accidentally instead of the current token. + /// Enables better error recovery when the wrong token is found. + pub fn similar_tokens(&self) -> Option> { + match *self { + Comma => Some(vec![Dot, Lt]), + Semi => Some(vec![Colon]), + _ => None + } + } + /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { self.is_special_ident() || self.is_used_keyword() || self.is_unused_keyword() diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 959dd4ef30f29..a2d3ed4deb652 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1049,8 +1049,9 @@ impl<'a> State<'a> { ast::TyKind::Path(Some(ref qself), ref path) => { self.print_qpath(path, qself, false)? } - ast::TyKind::TraitObject(ref bounds) => { - self.print_bounds("", &bounds[..])?; + ast::TyKind::TraitObject(ref bounds, syntax) => { + let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn " } else { "" }; + self.print_bounds(prefix, &bounds[..])?; } ast::TyKind::ImplTrait(ref bounds) => { self.print_bounds("impl ", &bounds[..])?; @@ -1111,6 +1112,13 @@ impl<'a> State<'a> { self.end()?; // end the head-ibox self.end() // end the outer cbox } + ast::ForeignItemKind::Ty => { + self.head(&visibility_qualified(&item.vis, "type"))?; + self.print_ident(item.ident)?; + self.s.word(";")?; + self.end()?; // end the head-ibox + self.end() // end the outer cbox + } } } @@ -1177,9 +1185,9 @@ impl<'a> State<'a> { self.end()?; // end inner head-block self.end()?; // end outer head-block } - ast::ItemKind::Use(ref vp) => { + ast::ItemKind::Use(ref tree) => { self.head(&visibility_qualified(&item.vis, "use"))?; - self.print_view_path(vp)?; + self.print_use_tree(tree)?; self.s.word(";")?; self.end()?; // end inner head-block self.end()?; // end outer head-block @@ -1279,7 +1287,7 @@ impl<'a> State<'a> { self.head(&visibility_qualified(&item.vis, "union"))?; self.print_struct(struct_def, generics, item.ident, item.span, true)?; } - ast::ItemKind::DefaultImpl(unsafety, ref trait_ref) => { + ast::ItemKind::AutoImpl(unsafety, ref trait_ref) => { self.head("")?; self.print_visibility(&item.vis)?; self.print_unsafety(unsafety)?; @@ -1330,10 +1338,11 @@ impl<'a> State<'a> { } self.bclose(item.span)?; } - ast::ItemKind::Trait(unsafety, ref generics, ref bounds, ref trait_items) => { + ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => { self.head("")?; self.print_visibility(&item.vis)?; self.print_unsafety(unsafety)?; + self.print_is_auto(is_auto)?; self.word_nbsp("trait")?; self.print_ident(item.ident)?; self.print_generics(generics)?; @@ -1439,7 +1448,10 @@ impl<'a> State<'a> { pub fn print_visibility(&mut self, vis: &ast::Visibility) -> io::Result<()> { match *vis { ast::Visibility::Public => self.word_nbsp("pub"), - ast::Visibility::Crate(_) => self.word_nbsp("pub(crate)"), + ast::Visibility::Crate(_, sugar) => match sugar { + ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), + ast::CrateSugar::JustCrate => self.word_nbsp("crate") + } ast::Visibility::Restricted { ref path, .. } => { let path = to_string(|s| s.print_path(path, false, 0, true)); if path == "self" || path == "super" { @@ -1524,6 +1536,7 @@ impl<'a> State<'a> { pub fn print_method_sig(&mut self, ident: ast::Ident, + generics: &ast::Generics, m: &ast::MethodSig, vis: &ast::Visibility) -> io::Result<()> { @@ -1532,7 +1545,7 @@ impl<'a> State<'a> { m.constness.node, m.abi, Some(ident), - &m.generics, + &generics, vis) } @@ -1552,7 +1565,7 @@ impl<'a> State<'a> { if body.is_some() { self.head("")?; } - self.print_method_sig(ti.ident, sig, &ast::Visibility::Inherited)?; + self.print_method_sig(ti.ident, &ti.generics, sig, &ast::Visibility::Inherited)?; if let Some(ref body) = *body { self.nbsp()?; self.print_block_with_attrs(body, &ti.attrs)?; @@ -1591,7 +1604,7 @@ impl<'a> State<'a> { } ast::ImplItemKind::Method(ref sig, ref body) => { self.head("")?; - self.print_method_sig(ii.ident, sig, &ii.vis)?; + self.print_method_sig(ii.ident, &ii.generics, sig, &ii.vis)?; self.nbsp()?; self.print_block_with_attrs(body, &ii.attrs)?; } @@ -1974,6 +1987,15 @@ impl<'a> State<'a> { Fixity::None => (prec + 1, prec + 1), }; + let left_prec = match (&lhs.node, op.node) { + // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is + // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead + // of `(x as i32) < ...`. We need to convince it _not_ to do that. + (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt) | + (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN, + _ => left_prec, + }; + self.print_expr_maybe_paren(lhs, left_prec)?; self.s.space()?; self.word_space(op.node.to_string())?; @@ -2191,7 +2213,7 @@ impl<'a> State<'a> { if limits == ast::RangeLimits::HalfOpen { self.s.word("..")?; } else { - self.s.word("...")?; + self.s.word("..=")?; } if let Some(ref e) = *end { self.print_expr_maybe_paren(e, fake_prec)?; @@ -2896,45 +2918,39 @@ impl<'a> State<'a> { Ok(()) } - pub fn print_view_path(&mut self, vp: &ast::ViewPath) -> io::Result<()> { - match vp.node { - ast::ViewPathSimple(ident, ref path) => { - self.print_path(path, false, 0, true)?; + pub fn print_use_tree(&mut self, tree: &ast::UseTree) -> io::Result<()> { + match tree.kind { + ast::UseTreeKind::Simple(ref ident) => { + self.print_path(&tree.prefix, false, 0, true)?; - if path.segments.last().unwrap().identifier.name != - ident.name { + if tree.prefix.segments.last().unwrap().identifier.name != ident.name { self.s.space()?; self.word_space("as")?; - self.print_ident(ident)?; + self.print_ident(*ident)?; } - - Ok(()) } - - ast::ViewPathGlob(ref path) => { - self.print_path(path, false, 0, true)?; - self.s.word("::*") + ast::UseTreeKind::Glob => { + if !tree.prefix.segments.is_empty() { + self.print_path(&tree.prefix, false, 0, true)?; + self.s.word("::")?; + } + self.s.word("*")?; } - - ast::ViewPathList(ref path, ref idents) => { - if path.segments.is_empty() { + ast::UseTreeKind::Nested(ref items) => { + if tree.prefix.segments.is_empty() { self.s.word("{")?; } else { - self.print_path(path, false, 0, true)?; + self.print_path(&tree.prefix, false, 0, true)?; self.s.word("::{")?; } - self.commasep(Inconsistent, &idents[..], |s, w| { - s.print_ident(w.node.name)?; - if let Some(ident) = w.node.rename { - s.s.space()?; - s.word_space("as")?; - s.print_ident(ident)?; - } - Ok(()) + self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| { + this.print_use_tree(tree) })?; - self.s.word("}") + self.s.word("}")?; } } + + Ok(()) } pub fn print_mutability(&mut self, @@ -3111,6 +3127,13 @@ impl<'a> State<'a> { ast::Unsafety::Unsafe => self.word_nbsp("unsafe"), } } + + pub fn print_is_auto(&mut self, s: ast::IsAuto) -> io::Result<()> { + match s { + ast::IsAuto::Yes => self.word_nbsp("auto"), + ast::IsAuto::No => Ok(()), + } + } } fn repeat(s: &str, n: usize) -> String { iter::repeat(s).take(n).collect() } diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 7aa94de9d3d5b..ae22230198f57 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -13,7 +13,7 @@ use attr; use ext::hygiene::{Mark, SyntaxContext}; use symbol::{Symbol, keywords}; use syntax_pos::{DUMMY_SP, Span}; -use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute}; +use codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; use ptr::P; use tokenstream::TokenStream; @@ -75,12 +75,16 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option P { let id_test = Ident::from_str("test"); let sp = ignored_span(cx, DUMMY_SP); let (vi, vis, ident) = if cx.is_libtest { - (ast::ItemKind::Use( - P(nospan(ast::ViewPathSimple(id_test, - path_node(vec![id_test]))))), + (ast::ItemKind::Use(P(ast::UseTree { + span: DUMMY_SP, + prefix: path_node(vec![id_test]), + kind: ast::UseTreeKind::Simple(id_test), + })), ast::Visibility::Public, keywords::Invalid.ident()) } else { (ast::ItemKind::ExternCrate(None), ast::Visibility::Inherited, id_test) @@ -547,9 +549,11 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { // building `use = __test::main` let reexport_ident = Ident::with_empty_ctxt(s); - let use_path = - nospan(ast::ViewPathSimple(reexport_ident, - path_node(vec![mod_ident, Ident::from_str("main")]))); + let use_path = ast::UseTree { + span: DUMMY_SP, + prefix: path_node(vec![mod_ident, Ident::from_str("main")]), + kind: ast::UseTreeKind::Simple(reexport_ident), + }; expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax/test_snippet.rs b/src/libsyntax/test_snippet.rs index e9b1976ea472b..a29250ea5f19f 100644 --- a/src/libsyntax/test_snippet.rs +++ b/src/libsyntax/test_snippet.rs @@ -60,7 +60,8 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & } let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), - Some(code_map.clone())); + Some(code_map.clone()), + false); let handler = Handler::with_emitter(true, false, Box::new(emitter)); handler.span_err(msp, "foo"); diff --git a/src/libsyntax/util/lev_distance.rs b/src/libsyntax/util/lev_distance.rs index 9307f3c58d4b0..e429791f2bdd4 100644 --- a/src/libsyntax/util/lev_distance.rs +++ b/src/libsyntax/util/lev_distance.rs @@ -44,23 +44,45 @@ pub fn lev_distance(a: &str, b: &str) -> usize { /// To find the best match for a given string from an iterator of names /// As a loose rule to avoid the obviously incorrect suggestions, it takes /// an optional limit for the maximum allowable edit distance, which defaults -/// to one-third of the given word +/// to one-third of the given word. +/// Besides Levenshtein, we use case insensitive comparison to improve accuracy on an edge case with +/// a lower(upper)case letters mismatch. pub fn find_best_match_for_name<'a, T>(iter_names: T, lookup: &str, dist: Option) -> Option where T: Iterator { let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d); - iter_names + + let (case_insensitive_match, levenstein_match) = iter_names .filter_map(|&name| { let dist = lev_distance(lookup, &name.as_str()); - if dist <= max_dist { // filter the unwanted cases + if dist <= max_dist { Some((name, dist)) } else { None } }) - .min_by_key(|&(_, val)| val) // extract the tuple containing the minimum edit distance - .map(|(s, _)| s) // and return only the string + // Here we are collecting the next structure: + // (case_insensitive_match, (levenstein_match, levenstein_distance)) + .fold((None, None), |result, (candidate, dist)| { + ( + if candidate.as_str().to_uppercase() == lookup.to_uppercase() { + Some(candidate) + } else { + result.0 + }, + match result.1 { + None => Some((candidate, dist)), + Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }) + } + ) + }); + + if let Some(candidate) = case_insensitive_match { + Some(candidate) // exact case insensitive match has a higher priority + } else { + if let Some((candidate, _)) = levenstein_match { Some(candidate) } else { None } + } } #[test] diff --git a/src/libsyntax/util/node_count.rs b/src/libsyntax/util/node_count.rs index 0a5d0c2e7fe01..ac5642e53cf67 100644 --- a/src/libsyntax/util/node_count.rs +++ b/src/libsyntax/util/node_count.rs @@ -133,9 +133,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_path(self, path) } - fn visit_path_list_item(&mut self, prefix: &Path, item: &PathListItem) { + fn visit_use_tree(&mut self, use_tree: &UseTree, id: NodeId, _nested: bool) { self.count += 1; - walk_path_list_item(self, prefix, item) + walk_use_tree(self, use_tree, id) } fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &PathParameters) { self.count += 1; diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 590874806d7b5..6014ec5aa92a5 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -106,7 +106,8 @@ impl AssocOp { Token::OrOr => Some(LOr), Token::DotDot => Some(DotDot), Token::DotDotEq => Some(DotDotEq), - Token::DotDotDot => Some(DotDotEq), // remove this after SNAP + // DotDotDot is no longer supported, but we need some way to display the error + Token::DotDotDot => Some(DotDotEq), Token::Colon => Some(Colon), _ if t.is_keyword(keywords::As) => Some(As), _ => None diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 05077d42a0bed..9a06ed0ba0297 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -27,11 +27,13 @@ use abi::Abi; use ast::*; use syntax_pos::Span; use codemap::Spanned; +use parse::token::Token; +use tokenstream::{TokenTree, TokenStream}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FnKind<'a> { /// fn foo() or extern "Abi" fn foo() - ItemFn(Ident, &'a Generics, Unsafety, Spanned, Abi, &'a Visibility, &'a Block), + ItemFn(Ident, Unsafety, Spanned, Abi, &'a Visibility, &'a Block), /// fn foo(&self) Method(Ident, &'a MethodSig, Option<&'a Visibility>, &'a Block), @@ -118,8 +120,8 @@ pub trait Visitor<'ast>: Sized { fn visit_path(&mut self, path: &'ast Path, _id: NodeId) { walk_path(self, path) } - fn visit_path_list_item(&mut self, prefix: &'ast Path, item: &'ast PathListItem) { - walk_path_list_item(self, prefix, item) + fn visit_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId, _nested: bool) { + walk_use_tree(self, use_tree, id) } fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) { walk_path_segment(self, path_span, path_segment) @@ -130,7 +132,17 @@ pub trait Visitor<'ast>: Sized { fn visit_assoc_type_binding(&mut self, type_binding: &'ast TypeBinding) { walk_assoc_type_binding(self, type_binding) } - fn visit_attribute(&mut self, _attr: &'ast Attribute) {} + fn visit_attribute(&mut self, attr: &'ast Attribute) { + walk_attribute(self, attr) + } + fn visit_tt(&mut self, tt: TokenTree) { + walk_tt(self, tt) + } + fn visit_tts(&mut self, tts: TokenStream) { + walk_tts(self, tts) + } + fn visit_token(&mut self, _t: Token) {} + // FIXME: add `visit_interpolated` and `walk_interpolated` fn visit_vis(&mut self, vis: &'ast Visibility) { walk_vis(self, vis) } @@ -224,22 +236,8 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { ItemKind::ExternCrate(opt_name) => { walk_opt_name(visitor, item.span, opt_name) } - ItemKind::Use(ref vp) => { - match vp.node { - ViewPathSimple(ident, ref path) => { - visitor.visit_ident(vp.span, ident); - visitor.visit_path(path, item.id); - } - ViewPathGlob(ref path) => { - visitor.visit_path(path, item.id); - } - ViewPathList(ref prefix, ref list) => { - visitor.visit_path(prefix, item.id); - for item in list { - visitor.visit_path_list_item(prefix, item) - } - } - } + ItemKind::Use(ref use_tree) => { + visitor.visit_use_tree(use_tree, item.id, false) } ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(ref typ, ref expr) => { @@ -247,7 +245,8 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_expr(expr); } ItemKind::Fn(ref declaration, unsafety, constness, abi, ref generics, ref body) => { - visitor.visit_fn(FnKind::ItemFn(item.ident, generics, unsafety, + visitor.visit_generics(generics); + visitor.visit_fn(FnKind::ItemFn(item.ident, unsafety, constness, abi, &item.vis, body), declaration, item.span, @@ -268,7 +267,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(type_parameters); visitor.visit_enum_def(enum_definition, type_parameters, item.id, item.span) } - ItemKind::DefaultImpl(_, ref trait_ref) => { + ItemKind::AutoImpl(_, ref trait_ref) => { visitor.visit_trait_ref(trait_ref) } ItemKind::Impl(_, _, _, @@ -287,7 +286,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_variant_data(struct_definition, item.ident, generics, item.id, item.span); } - ItemKind::Trait(_, ref generics, ref bounds, ref methods) => { + ItemKind::Trait(.., ref generics, ref bounds, ref methods) => { visitor.visit_generics(generics); walk_list!(visitor, visit_ty_param_bound, bounds); walk_list!(visitor, visit_trait_item, methods); @@ -348,7 +347,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { visitor.visit_ty(ty); visitor.visit_expr(expression) } - TyKind::TraitObject(ref bounds) | + TyKind::TraitObject(ref bounds, ..) | TyKind::ImplTrait(ref bounds) => { walk_list!(visitor, visit_ty_param_bound, bounds); } @@ -368,11 +367,22 @@ pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) { } } -pub fn walk_path_list_item<'a, V: Visitor<'a>>(visitor: &mut V, - _prefix: &Path, - item: &'a PathListItem) { - visitor.visit_ident(item.span, item.node.name); - walk_opt_ident(visitor, item.span, item.node.rename); +pub fn walk_use_tree<'a, V: Visitor<'a>>( + visitor: &mut V, use_tree: &'a UseTree, id: NodeId, +) { + visitor.visit_path(&use_tree.prefix, id); + + match use_tree.kind { + UseTreeKind::Simple(ident) => { + visitor.visit_ident(use_tree.span, ident); + } + UseTreeKind::Glob => {}, + UseTreeKind::Nested(ref use_trees) => { + for &(ref nested_tree, nested_id) in use_trees { + visitor.visit_use_tree(nested_tree, nested_id, true); + } + } + } } pub fn walk_path_segment<'a, V: Visitor<'a>>(visitor: &mut V, @@ -464,6 +474,7 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, foreign_item: &'a visitor.visit_generics(generics) } ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ), + ForeignItemKind::Ty => (), } walk_list!(visitor, visit_attribute, &foreign_item.attrs); @@ -538,13 +549,11 @@ pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl where V: Visitor<'a>, { match kind { - FnKind::ItemFn(_, generics, _, _, _, _, body) => { - visitor.visit_generics(generics); + FnKind::ItemFn(_, _, _, _, _, body) => { walk_fn_decl(visitor, declaration); visitor.visit_block(body); } - FnKind::Method(_, sig, _, body) => { - visitor.visit_generics(&sig.generics); + FnKind::Method(_, _, _, body) => { walk_fn_decl(visitor, declaration); visitor.visit_block(body); } @@ -558,13 +567,13 @@ pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl pub fn walk_trait_item<'a, V: Visitor<'a>>(visitor: &mut V, trait_item: &'a TraitItem) { visitor.visit_ident(trait_item.span, trait_item.ident); walk_list!(visitor, visit_attribute, &trait_item.attrs); + visitor.visit_generics(&trait_item.generics); match trait_item.node { TraitItemKind::Const(ref ty, ref default) => { visitor.visit_ty(ty); walk_list!(visitor, visit_expr, default); } TraitItemKind::Method(ref sig, None) => { - visitor.visit_generics(&sig.generics); walk_fn_decl(visitor, &sig.decl); } TraitItemKind::Method(ref sig, Some(ref body)) => { @@ -585,6 +594,7 @@ pub fn walk_impl_item<'a, V: Visitor<'a>>(visitor: &mut V, impl_item: &'a ImplIt visitor.visit_vis(&impl_item.vis); visitor.visit_ident(impl_item.span, impl_item.ident); walk_list!(visitor, visit_attribute, &impl_item.attrs); + visitor.visit_generics(&impl_item.generics); match impl_item.node { ImplItemKind::Const(ref ty, ref expr) => { visitor.visit_ty(ty); @@ -810,3 +820,20 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { visitor.visit_path(path, id); } } + +pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) { + visitor.visit_tts(attr.tokens.clone()); +} + +pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) { + match tt { + TokenTree::Token(_, tok) => visitor.visit_token(tok), + TokenTree::Delimited(_, delimed) => visitor.visit_tts(delimed.stream()), + } +} + +pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) { + for tt in tts.trees() { + visitor.visit_tt(tt); + } +} diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index fa5537b5d8fe3..f375847e705bc 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -96,12 +96,18 @@ impl MultiItemModifier for ProcMacroDerive { } }; + let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); __internal::set_sess(ecx, || { + let msg = "proc-macro derive produced unparseable tokens"; match __internal::token_stream_parse_items(stream) { + // fail if there have been errors emitted + Ok(_) if ecx.parse_sess.span_diagnostic.err_count() > error_count_before => { + ecx.struct_span_fatal(span, msg).emit(); + panic!(FatalError); + } Ok(new_items) => new_items.into_iter().map(Annotatable::Item).collect(), Err(_) => { // FIXME: handle this better - let msg = "proc-macro derive produced unparseable tokens"; ecx.struct_span_fatal(span, msg).emit(); panic!(FatalError); } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 5c1ca19d635f7..2b565ca51e9ec 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -393,7 +393,7 @@ fn find_type_parameters(ty: &ast::Ty, } impl<'a> TraitDef<'a> { - pub fn expand(&self, + pub fn expand(self, cx: &mut ExtCtxt, mitem: &ast::MetaItem, item: &'a Annotatable, @@ -401,7 +401,7 @@ impl<'a> TraitDef<'a> { self.expand_ext(cx, mitem, item, push, false); } - pub fn expand_ext(&self, + pub fn expand_ext(self, cx: &mut ExtCtxt, mitem: &ast::MetaItem, item: &'a Annotatable, @@ -409,30 +409,55 @@ impl<'a> TraitDef<'a> { from_scratch: bool) { match *item { Annotatable::Item(ref item) => { + let is_packed = item.attrs.iter().any(|attr| { + attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr) + .contains(&attr::ReprPacked) + }); + let has_no_type_params = match item.node { + ast::ItemKind::Struct(_, ref generics) | + ast::ItemKind::Enum(_, ref generics) | + ast::ItemKind::Union(_, ref generics) => { + generics.ty_params.is_empty() + } + _ => { + // Non-ADT derive is an error, but it should have been + // set earlier; see + // libsyntax/ext/expand.rs:MacroExpander::expand() + return; + } + }; + let is_always_copy = + attr::contains_name(&item.attrs, "rustc_copy_clone_marker") && + has_no_type_params; + let use_temporaries = is_packed && is_always_copy; + let newitem = match item.node { ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch) + self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch, + use_temporaries) } ast::ItemKind::Enum(ref enum_def, ref generics) => { + // We ignore `use_temporaries` here, because + // `repr(packed)` enums cause an error later on. + // + // This can only cause further compilation errors + // downstream in blatantly illegal code, so it + // is fine. self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { self.expand_struct_def(cx, &struct_def, item.ident, - generics, from_scratch) + generics, from_scratch, + use_temporaries) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); return; } } - _ => { - // Non-ADT derive is an error, but it should have been - // set earlier; see - // libsyntax/ext/expand.rs:MacroExpander::expand() - return; - } + _ => unreachable!(), }; // Keep the lint attributes of the previous item to control how the // generated implementations are linted @@ -506,6 +531,7 @@ impl<'a> TraitDef<'a> { vis: ast::Visibility::Inherited, defaultness: ast::Defaultness::Final, attrs: Vec::new(), + generics: Generics::default(), node: ast::ImplItemKind::Type(type_def.to_ty(cx, self.span, type_ident, generics)), tokens: None, } @@ -674,7 +700,8 @@ impl<'a> TraitDef<'a> { struct_def: &'a VariantData, type_ident: Ident, generics: &Generics, - from_scratch: bool) + from_scratch: bool, + use_temporaries: bool) -> P { let field_tys: Vec> = struct_def.fields() .iter() @@ -700,7 +727,8 @@ impl<'a> TraitDef<'a> { struct_def, type_ident, &self_args[..], - &nonself_args[..]) + &nonself_args[..], + use_temporaries) }; method_def.create_method(cx, @@ -921,12 +949,12 @@ impl<'a> MethodDef<'a> { ast::ImplItem { id: ast::DUMMY_NODE_ID, attrs: self.attributes.clone(), + generics: fn_generics, span: trait_.span, vis: ast::Visibility::Inherited, defaultness: ast::Defaultness::Final, ident: method_ident, node: ast::ImplItemKind::Method(ast::MethodSig { - generics: fn_generics, abi, unsafety, constness: @@ -957,6 +985,22 @@ impl<'a> MethodDef<'a> { /// } /// } /// } + /// + /// // or if A is repr(packed) - note fields are matched by-value + /// // instead of by-reference. + /// impl PartialEq for A { + /// fn eq(&self, __arg_1: &A) -> bool { + /// match *self { + /// A {x: __self_0_0, y: __self_0_1} => { + /// match __arg_1 { + /// A {x: __self_1_0, y: __self_1_1} => { + /// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1) + /// } + /// } + /// } + /// } + /// } + /// } /// ``` fn expand_struct_method_body<'b>(&self, cx: &mut ExtCtxt, @@ -964,7 +1008,8 @@ impl<'a> MethodDef<'a> { struct_def: &'b VariantData, type_ident: Ident, self_args: &[P], - nonself_args: &[P]) + nonself_args: &[P], + use_temporaries: bool) -> P { let mut raw_fields = Vec::new(); // Vec<[fields of self], @@ -976,7 +1021,8 @@ impl<'a> MethodDef<'a> { struct_path, struct_def, &format!("__self_{}", i), - ast::Mutability::Immutable); + ast::Mutability::Immutable, + use_temporaries); patterns.push(pat); raw_fields.push(ident_expr); } @@ -1139,7 +1185,6 @@ impl<'a> MethodDef<'a> { self_args: Vec>, nonself_args: &[P]) -> P { - let sp = trait_.span; let variants = &enum_def.variants; @@ -1511,12 +1556,18 @@ impl<'a> TraitDef<'a> { fn create_subpatterns(&self, cx: &mut ExtCtxt, field_paths: Vec, - mutbl: ast::Mutability) + mutbl: ast::Mutability, + use_temporaries: bool) -> Vec> { field_paths.iter() .map(|path| { + let binding_mode = if use_temporaries { + ast::BindingMode::ByValue(ast::Mutability::Immutable) + } else { + ast::BindingMode::ByRef(mutbl) + }; cx.pat(path.span, - PatKind::Ident(ast::BindingMode::ByRef(mutbl), (*path).clone(), None)) + PatKind::Ident(binding_mode, (*path).clone(), None)) }) .collect() } @@ -1527,8 +1578,10 @@ impl<'a> TraitDef<'a> { struct_path: ast::Path, struct_def: &'a VariantData, prefix: &str, - mutbl: ast::Mutability) - -> (P, Vec<(Span, Option, P, &'a [ast::Attribute])>) { + mutbl: ast::Mutability, + use_temporaries: bool) + -> (P, Vec<(Span, Option, P, &'a [ast::Attribute])>) + { let mut paths = Vec::new(); let mut ident_exprs = Vec::new(); for (i, struct_field) in struct_def.fields().iter().enumerate() { @@ -1538,12 +1591,18 @@ impl<'a> TraitDef<'a> { span: sp, node: ident, }); - let val = cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp, ident))); + let val = cx.expr_path(cx.path_ident(sp, ident)); + let val = if use_temporaries { + val + } else { + cx.expr_deref(sp, val) + }; let val = cx.expr(sp, ast::ExprKind::Paren(val)); + ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); } - let subpats = self.create_subpatterns(cx, paths, mutbl); + let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); let pattern = match *struct_def { VariantData::Struct(..) => { let field_pats = subpats.into_iter() @@ -1587,7 +1646,9 @@ impl<'a> TraitDef<'a> { let variant_ident = variant.node.name; let sp = variant.span.with_ctxt(self.span.ctxt()); let variant_path = cx.path(sp, vec![enum_ident, variant_ident]); - self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl) + let use_temporaries = false; // enums can't be repr(packed) + self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl, + use_temporaries) } } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 63c533df198d0..ad5bd39a45341 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -110,6 +110,8 @@ struct Context<'a, 'b: 'a> { /// still existed in this phase of processing. /// Used only for `all_pieces_simple` tracking in `trans_piece`. curarg: usize, + /// Keep track of invalid references to positional arguments + invalid_refs: Vec, } /// Parses the arguments from the given list of tokens, returning None @@ -226,7 +228,7 @@ impl<'a, 'b> Context<'a, 'b> { // argument second, if it's an implicit positional parameter // it's written second, so it should come after width/precision. let pos = match arg.position { - parse::ArgumentIs(i) => Exact(i), + parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), parse::ArgumentNamed(s) => Named(s.to_string()), }; @@ -251,23 +253,54 @@ impl<'a, 'b> Context<'a, 'b> { fn describe_num_args(&self) -> String { match self.args.len() { - 0 => "no arguments given".to_string(), + 0 => "no arguments were given".to_string(), 1 => "there is 1 argument".to_string(), x => format!("there are {} arguments", x), } } + /// Handle invalid references to positional arguments. Output different + /// errors for the case where all arguments are positional and for when + /// there are named arguments or numbered positional arguments in the + /// format string. + fn report_invalid_references(&self, numbered_position_args: bool) { + let mut e; + let mut refs: Vec = self.invalid_refs + .iter() + .map(|r| r.to_string()) + .collect(); + + if self.names.is_empty() && !numbered_position_args { + e = self.ecx.mut_span_err(self.fmtsp, + &format!("{} positional argument{} in format string, but {}", + self.pieces.len(), + if self.pieces.len() > 1 { "s" } else { "" }, + self.describe_num_args())); + } else { + let arg_list = match refs.len() { + 1 => format!("argument {}", refs.pop().unwrap()), + _ => format!("arguments {head} and {tail}", + tail=refs.pop().unwrap(), + head=refs.join(", ")) + }; + + e = self.ecx.mut_span_err(self.fmtsp, + &format!("invalid reference to positional {} ({})", + arg_list, + self.describe_num_args())); + e.note("positional arguments are zero-based"); + }; + + e.emit(); + } + /// Actually verifies and tracks a given format placeholder /// (a.k.a. argument). fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) { match arg { Exact(arg) => { if self.args.len() <= arg { - let msg = format!("invalid reference to argument `{}` ({})", - arg, - self.describe_num_args()); - - self.ecx.span_err(self.fmtsp, &msg[..]); + self.invalid_refs.push(arg); return; } match ty { @@ -403,7 +436,8 @@ impl<'a, 'b> Context<'a, 'b> { } }; match arg.position { - parse::ArgumentIs(i) => { + parse::ArgumentIs(i) + | parse::ArgumentImplicitlyIs(i) => { // Map to index in final generated argument array // in case of multiple types specified let arg_idx = match arg_index_consumed.get_mut(i) { @@ -691,6 +725,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, all_pieces_simple: true, macsp, fmtsp: fmt.span, + invalid_refs: Vec::new(), }; let fmt_str = &*fmt.node.0.as_str(); @@ -711,6 +746,18 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, } } + let numbered_position_args = pieces.iter().any(|arg: &parse::Piece| { + match *arg { + parse::String(_) => false, + parse::NextArgument(arg) => { + match arg.position { + parse::Position::ArgumentIs(_) => true, + _ => false, + } + } + } + }); + cx.build_index_map(); let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()]; @@ -736,6 +783,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, cx.str_pieces.push(s); } + if cx.invalid_refs.len() >= 1 { + cx.report_invalid_references(numbered_position_args); + } + // Make sure that all arguments were used and all arguments have types. let num_pos_args = cx.args.len() - cx.names.len(); let mut errs = vec![]; diff --git a/src/libsyntax_pos/Cargo.toml b/src/libsyntax_pos/Cargo.toml index dd8129bab510f..aad2155157d84 100644 --- a/src/libsyntax_pos/Cargo.toml +++ b/src/libsyntax_pos/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["dylib"] [dependencies] serialize = { path = "../libserialize" } rustc_data_structures = { path = "../librustc_data_structures" } +unicode-width = "0.1.4" diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 4790fa0a7edc2..9358e654a9fc8 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -140,6 +140,31 @@ impl SyntaxContext { SyntaxContext(0) } + // Allocate a new SyntaxContext with the given ExpnInfo. This is used when + // deserializing Spans from the incr. comp. cache. + // FIXME(mw): This method does not restore MarkData::parent or + // SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things + // don't seem to be used after HIR lowering, so everything should be fine + // as long as incremental compilation does not kick in before that. + pub fn allocate_directly(expansion_info: ExpnInfo) -> Self { + HygieneData::with(|data| { + data.marks.push(MarkData { + parent: Mark::root(), + modern: false, + expn_info: Some(expansion_info) + }); + + let mark = Mark(data.marks.len() as u32 - 1); + + data.syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + prev_ctxt: SyntaxContext::empty(), + modern: SyntaxContext::empty(), + }); + SyntaxContext(data.syntax_contexts.len() as u32 - 1) + }) + } + /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { HygieneData::with(|data| { @@ -286,7 +311,7 @@ impl fmt::Debug for SyntaxContext { } /// Extra information for tracking spans of macro and syntax sugar expansion -#[derive(Clone, Hash, Debug)] +#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct ExpnInfo { /// The location of the actual macro invocation or syntax sugar , e.g. /// `let x = foo!();` or `if let Some(y) = x {}` @@ -302,7 +327,7 @@ pub struct ExpnInfo { pub callee: NameAndSpan } -#[derive(Clone, Hash, Debug)] +#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct NameAndSpan { /// The format with which the macro was invoked. pub format: ExpnFormat, @@ -330,7 +355,7 @@ impl NameAndSpan { } /// The source of expansion. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum ExpnFormat { /// e.g. #[derive(...)] MacroAttribute(Symbol), @@ -341,7 +366,7 @@ pub enum ExpnFormat { } /// The kind of compiler desugaring. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum CompilerDesugaringKind { BackArrow, DotFill, diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 582f279818134..bf059cac89152 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -32,6 +32,7 @@ use std::cmp::{self, Ordering}; use std::fmt; use std::hash::Hasher; use std::ops::{Add, Sub}; +use std::path::PathBuf; use std::rc::Rc; use rustc_data_structures::stable_hasher::StableHasher; @@ -43,6 +44,8 @@ use serialize::{Encodable, Decodable, Encoder, Decoder}; extern crate serialize; extern crate serialize as rustc_serialize; // used by deriving +extern crate unicode_width; + pub mod hygiene; pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind}; @@ -74,6 +77,21 @@ pub struct SpanData { pub ctxt: SyntaxContext, } +impl SpanData { + #[inline] + pub fn with_lo(&self, lo: BytePos) -> Span { + Span::new(lo, self.hi, self.ctxt) + } + #[inline] + pub fn with_hi(&self, hi: BytePos) -> Span { + Span::new(self.lo, hi, self.ctxt) + } + #[inline] + pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { + Span::new(self.lo, self.hi, ctxt) + } +} + // The interner in thread-local, so `Span` shouldn't move between threads. impl !Send for Span {} impl !Sync for Span {} @@ -108,8 +126,7 @@ impl Span { } #[inline] pub fn with_lo(self, lo: BytePos) -> Span { - let base = self.data(); - Span::new(lo, base.hi, base.ctxt) + self.data().with_lo(lo) } #[inline] pub fn hi(self) -> BytePos { @@ -117,8 +134,7 @@ impl Span { } #[inline] pub fn with_hi(self, hi: BytePos) -> Span { - let base = self.data(); - Span::new(base.lo, hi, base.ctxt) + self.data().with_hi(hi) } #[inline] pub fn ctxt(self) -> SyntaxContext { @@ -126,20 +142,21 @@ impl Span { } #[inline] pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { - let base = self.data(); - Span::new(base.lo, base.hi, ctxt) + self.data().with_ctxt(ctxt) } /// Returns a new span representing just the end-point of this span pub fn end_point(self) -> Span { - let lo = cmp::max(self.hi().0 - 1, self.lo().0); - self.with_lo(BytePos(lo)) + let span = self.data(); + let lo = cmp::max(span.hi.0 - 1, span.lo.0); + span.with_lo(BytePos(lo)) } /// Returns a new span representing the next character after the end-point of this span pub fn next_point(self) -> Span { - let lo = cmp::max(self.hi().0, self.lo().0 + 1); - Span::new(BytePos(lo), BytePos(lo), self.ctxt()) + let span = self.data(); + let lo = cmp::max(span.hi.0, span.lo.0 + 1); + Span::new(BytePos(lo), BytePos(lo), span.ctxt) } /// Returns `self` if `self` is not the dummy span, and `other` otherwise. @@ -149,7 +166,9 @@ impl Span { /// Return true if `self` fully encloses `other`. pub fn contains(self, other: Span) -> bool { - self.lo() <= other.lo() && other.hi() <= self.hi() + let span = self.data(); + let other = other.data(); + span.lo <= other.lo && other.hi <= span.hi } /// Return true if the spans are equal with regards to the source text. @@ -157,13 +176,17 @@ impl Span { /// Use this instead of `==` when either span could be generated code, /// and you only care that they point to the same bytes of source text. pub fn source_equal(&self, other: &Span) -> bool { - self.lo() == other.lo() && self.hi() == other.hi() + let span = self.data(); + let other = other.data(); + span.lo == other.lo && span.hi == other.hi } /// Returns `Some(span)`, where the start is trimmed by the end of `other` pub fn trim_start(self, other: Span) -> Option { - if self.hi() > other.hi() { - Some(self.with_lo(cmp::max(self.lo(), other.hi()))) + let span = self.data(); + let other = other.data(); + if span.hi > other.hi { + Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None } @@ -267,29 +290,35 @@ impl Span { /// Return a `Span` that would enclose both `self` and `end`. pub fn to(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); Span::new( - cmp::min(self.lo(), end.lo()), - cmp::max(self.hi(), end.hi()), + cmp::min(span.lo, end.lo), + cmp::max(span.hi, end.hi), // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480) - if self.ctxt() == SyntaxContext::empty() { end.ctxt() } else { self.ctxt() }, + if span.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, ) } /// Return a `Span` between the end of `self` to the beginning of `end`. pub fn between(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); Span::new( - self.hi(), - end.lo(), - if end.ctxt() == SyntaxContext::empty() { end.ctxt() } else { self.ctxt() }, + span.hi, + end.lo, + if end.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, ) } /// Return a `Span` between the beginning of `self` to the beginning of `end`. pub fn until(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); Span::new( - self.lo(), - end.lo(), - if end.ctxt() == SyntaxContext::empty() { end.ctxt() } else { self.ctxt() }, + span.lo, + end.lo, + if end.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt }, ) } } @@ -315,13 +344,14 @@ impl Default for Span { impl serialize::UseSpecializedEncodable for Span { fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + let span = self.data(); s.emit_struct("Span", 2, |s| { s.emit_struct_field("lo", 0, |s| { - self.lo().encode(s) + span.lo.encode(s) })?; s.emit_struct_field("hi", 1, |s| { - self.hi().encode(s) + span.hi.encode(s) }) }) } @@ -338,8 +368,11 @@ impl serialize::UseSpecializedDecodable for Span { } fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Span {{ lo: {:?}, hi: {:?}, ctxt: {:?} }}", - span.lo(), span.hi(), span.ctxt()) + f.debug_struct("Span") + .field("lo", &span.lo()) + .field("hi", &span.hi()) + .field("ctxt", &span.ctxt()) + .finish() } impl fmt::Debug for Span { @@ -463,6 +496,63 @@ pub struct MultiByteChar { pub bytes: usize, } +/// Identifies an offset of a non-narrow character in a FileMap +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)] +pub enum NonNarrowChar { + /// Represents a zero-width character + ZeroWidth(BytePos), + /// Represents a wide (fullwidth) character + Wide(BytePos), +} + +impl NonNarrowChar { + fn new(pos: BytePos, width: usize) -> Self { + match width { + 0 => NonNarrowChar::ZeroWidth(pos), + 2 => NonNarrowChar::Wide(pos), + _ => panic!("width {} given for non-narrow character", width), + } + } + + /// Returns the absolute offset of the character in the CodeMap + pub fn pos(&self) -> BytePos { + match *self { + NonNarrowChar::ZeroWidth(p) | + NonNarrowChar::Wide(p) => p, + } + } + + /// Returns the width of the character, 0 (zero-width) or 2 (wide) + pub fn width(&self) -> usize { + match *self { + NonNarrowChar::ZeroWidth(_) => 0, + NonNarrowChar::Wide(_) => 2, + } + } +} + +impl Add for NonNarrowChar { + type Output = Self; + + fn add(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), + } + } +} + +impl Sub for NonNarrowChar { + type Output = Self; + + fn sub(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), + } + } +} + /// The state of the lazy external source loading mechanism of a FileMap. #[derive(PartialEq, Eq, Clone)] pub enum ExternalSource { @@ -501,6 +591,9 @@ pub struct FileMap { pub name: FileName, /// True if the `name` field above has been modified by -Zremap-path-prefix pub name_was_remapped: bool, + /// The unmapped path of the file that the source came from. + /// Set to `None` if the FileMap was imported from an external crate. + pub unmapped_path: Option, /// Indicates which crate this FileMap was imported from. pub crate_of_origin: u32, /// The complete source code @@ -518,11 +611,13 @@ pub struct FileMap { pub lines: RefCell>, /// Locations of multi-byte characters in the source code pub multibyte_chars: RefCell>, + /// Width of characters that are not narrow in the source code + pub non_narrow_chars: RefCell>, } impl Encodable for FileMap { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("FileMap", 7, |s| { + s.emit_struct("FileMap", 8, |s| { s.emit_struct_field("name", 0, |s| self.name.encode(s))?; s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?; s.emit_struct_field("src_hash", 6, |s| self.src_hash.encode(s))?; @@ -576,6 +671,9 @@ impl Encodable for FileMap { })?; s.emit_struct_field("multibyte_chars", 5, |s| { (*self.multibyte_chars.borrow()).encode(s) + })?; + s.emit_struct_field("non_narrow_chars", 7, |s| { + (*self.non_narrow_chars.borrow()).encode(s) }) }) } @@ -584,7 +682,7 @@ impl Encodable for FileMap { impl Decodable for FileMap { fn decode(d: &mut D) -> Result { - d.read_struct("FileMap", 6, |d| { + d.read_struct("FileMap", 8, |d| { let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; let name_was_remapped: bool = d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?; @@ -623,9 +721,12 @@ impl Decodable for FileMap { })?; let multibyte_chars: Vec = d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?; + let non_narrow_chars: Vec = + d.read_struct_field("non_narrow_chars", 7, |d| Decodable::decode(d))?; Ok(FileMap { name, name_was_remapped, + unmapped_path: None, // `crate_of_origin` has to be set by the importer. // This value matches up with rustc::hir::def_id::INVALID_CRATE. // That constant is not available here unfortunately :( @@ -636,7 +737,8 @@ impl Decodable for FileMap { src_hash, external_src: RefCell::new(ExternalSource::AbsentOk), lines: RefCell::new(lines), - multibyte_chars: RefCell::new(multibyte_chars) + multibyte_chars: RefCell::new(multibyte_chars), + non_narrow_chars: RefCell::new(non_narrow_chars) }) }) } @@ -651,6 +753,7 @@ impl fmt::Debug for FileMap { impl FileMap { pub fn new(name: FileName, name_was_remapped: bool, + unmapped_path: PathBuf, mut src: String, start_pos: BytePos) -> FileMap { remove_bom(&mut src); @@ -664,6 +767,7 @@ impl FileMap { FileMap { name, name_was_remapped, + unmapped_path: Some(unmapped_path), crate_of_origin: 0, src: Some(Rc::new(src)), src_hash, @@ -672,6 +776,7 @@ impl FileMap { end_pos: Pos::from_usize(end_pos), lines: RefCell::new(Vec::new()), multibyte_chars: RefCell::new(Vec::new()), + non_narrow_chars: RefCell::new(Vec::new()), } } @@ -761,6 +866,23 @@ impl FileMap { self.multibyte_chars.borrow_mut().push(mbc); } + pub fn record_width(&self, pos: BytePos, ch: char) { + let width = match ch { + '\t' | '\n' => + // Tabs will consume one column. + // Make newlines take one column so that displayed spans can point them. + 1, + ch => + // Assume control characters are zero width. + // FIXME: How can we decide between `width` and `width_cjk`? + unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0), + }; + // Only record non-narrow characters. + if width != 1 { + self.non_narrow_chars.borrow_mut().push(NonNarrowChar::new(pos, width)); + } + } + pub fn is_real_file(&self) -> bool { !(self.name.starts_with("<") && self.name.ends_with(">")) @@ -809,6 +931,11 @@ impl FileMap { (lines[line_index], lines[line_index + 1]) } } + + #[inline] + pub fn contains(&self, byte_pos: BytePos) -> bool { + byte_pos >= self.start_pos && byte_pos <= self.end_pos + } } /// Remove utf-8 BOM if any. @@ -907,7 +1034,9 @@ pub struct Loc { /// The (1-based) line number pub line: usize, /// The (0-based) column offset - pub col: CharPos + pub col: CharPos, + /// The (0-based) column offset when displayed + pub col_display: usize, } /// A source code location used as the result of lookup_char_pos_adj diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index c2b32171a9a98..b23e40ce7a932 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -59,9 +59,11 @@ const LEN_INDEX: usize = 1; const CTXT_INDEX: usize = 2; // Tag = 0, inline format. -// ----------------------------------- -// | base 31:8 | len 7:1 | tag 0:0 | -// ----------------------------------- +// ------------------------------------------------------------- +// | base 31:8 | len 7:1 | ctxt (currently 0 bits) | tag 0:0 | +// ------------------------------------------------------------- +// Since there are zero bits for ctxt, only SpanData with a 0 SyntaxContext +// can be inline. const INLINE_SIZES: [u32; 3] = [24, 7, 0]; const INLINE_OFFSETS: [u32; 3] = [8, 1, 1]; diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 4d3db15ef29db..69ddd56021377 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -309,10 +309,12 @@ declare_keywords! { (54, Yield, "yield") // Weak keywords, have special meaning only in specific contexts. - (55, Default, "default") - (56, StaticLifetime, "'static") - (57, Union, "union") - (58, Catch, "catch") + (55, Auto, "auto") + (56, Catch, "catch") + (57, Default, "default") + (58, Dyn, "dyn") + (59, StaticLifetime, "'static") + (60, Union, "union") } // If an interner exists in TLS, return it. Otherwise, prepare a fresh one. @@ -414,7 +416,7 @@ mod tests { // first one is zero: assert_eq!(i.intern("dog"), Symbol(0)); // re-use gets the same entry: - assert_eq!(i.intern ("dog"), Symbol(0)); + assert_eq!(i.intern("dog"), Symbol(0)); // different string gets a different #: assert_eq!(i.intern("cat"), Symbol(1)); assert_eq!(i.intern("cat"), Symbol(1)); diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 642eb28556408..ef08b877262fc 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -71,6 +71,7 @@ use std::thread; use std::time::{Instant, Duration}; const TEST_WARN_TIMEOUT_S: u64 = 60; +const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode // to be used by rustc to compile tests in libtest pub mod test { @@ -433,7 +434,8 @@ Test Attributes: // Parses command line arguments into test options pub fn parse_opts(args: &[String]) -> Option { let opts = optgroups(); - let matches = match opts.parse(&args[1..]) { + let args = args.get(1..).unwrap_or(args); + let matches = match opts.parse(args) { Ok(m) => m, Err(f) => return Some(Err(f.to_string())), }; @@ -614,7 +616,14 @@ impl ConsoleTestState { pub fn write_short_result(&mut self, verbose: &str, quiet: &str, color: term::color::Color) -> io::Result<()> { if self.quiet { - self.write_pretty(quiet, color) + self.write_pretty(quiet, color)?; + if self.current_test_count() % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 { + // we insert a new line every 100 dots in order to flush the + // screen when dealing with line-buffered output (e.g. piping to + // `stamp` in the rust CI). + self.write_plain("\n")?; + } + Ok(()) } else { self.write_pretty(verbose, color)?; self.write_plain("\n") @@ -771,9 +780,12 @@ impl ConsoleTestState { Ok(()) } + fn current_test_count(&self) -> usize { + self.passed + self.failed + self.ignored + self.measured + self.allowed_fail + } + pub fn write_run_finish(&mut self) -> io::Result { - assert!(self.passed + self.failed + self.ignored + self.measured + - self.allowed_fail == self.total); + assert!(self.current_test_count() == self.total); if self.options.display_output { self.write_outputs()?; @@ -1023,6 +1035,10 @@ fn stdout_isatty() -> bool { // FIXME: Implement isatty on Redox false } +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +fn stdout_isatty() -> bool { + false +} #[cfg(unix)] fn stdout_isatty() -> bool { unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } @@ -1121,45 +1137,47 @@ pub fn run_tests(opts: &TestOpts, tests: Vec, mut callback: F) }}) }; - while pending > 0 || !remaining.is_empty() { - while pending < concurrency && !remaining.is_empty() { + if concurrency == 1 { + while !remaining.is_empty() { let test = remaining.pop().unwrap(); - if concurrency == 1 { - // We are doing one test at a time so we can print the name - // of the test before we run it. Useful for debugging tests - // that hang forever. - callback(TeWait(test.desc.clone(), test.testfn.padding()))?; - } - let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); - running_tests.insert(test.desc.clone(), timeout); + callback(TeWait(test.desc.clone(), test.testfn.padding()))?; run_test(opts, !opts.run_tests, test, tx.clone()); - pending += 1; + let (test, result, stdout) = rx.recv().unwrap(); + callback(TeResult(test, result, stdout))?; } + } else { + while pending > 0 || !remaining.is_empty() { + while pending < concurrency && !remaining.is_empty() { + let test = remaining.pop().unwrap(); + let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); + running_tests.insert(test.desc.clone(), timeout); + run_test(opts, !opts.run_tests, test, tx.clone()); + pending += 1; + } - let mut res; - loop { - if let Some(timeout) = calc_timeout(&running_tests) { - res = rx.recv_timeout(timeout); - for test in get_timed_out_tests(&mut running_tests) { - callback(TeTimeout(test))?; - } - if res != Err(RecvTimeoutError::Timeout) { + let mut res; + loop { + if let Some(timeout) = calc_timeout(&running_tests) { + res = rx.recv_timeout(timeout); + for test in get_timed_out_tests(&mut running_tests) { + callback(TeTimeout(test))?; + } + if res != Err(RecvTimeoutError::Timeout) { + break; + } + } else { + res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); break; } - } else { - res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); - break; } - } - let (desc, result, stdout) = res.unwrap(); - running_tests.remove(&desc); + let (desc, result, stdout) = res.unwrap(); + running_tests.remove(&desc); - if concurrency != 1 { callback(TeWait(desc.clone(), PadNone))?; + callback(TeResult(desc, result, stdout))?; + pending -= 1; } - callback(TeResult(desc, result, stdout))?; - pending -= 1; } if opts.bench_benchmarks { @@ -1224,6 +1242,11 @@ fn get_concurrency() -> usize { 1 } + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + fn num_cpus() -> usize { + 1 + } + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", @@ -1382,7 +1405,12 @@ pub fn run_test(opts: &TestOpts, let TestDescAndFn {desc, testfn} = test; - if force_ignore || desc.ignore { + let ignore_because_panic_abort = + cfg!(target_arch = "wasm32") && + !cfg!(target_os = "emscripten") && + desc.should_panic != ShouldPanic::No; + + if force_ignore || desc.ignore || ignore_because_panic_abort { monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap(); return; } @@ -1434,7 +1462,9 @@ pub fn run_test(opts: &TestOpts, // If the platform is single-threaded we're just going to run // the test synchronously, regardless of the concurrency // level. - let supports_threads = !cfg!(target_os = "emscripten"); + let supports_threads = + !cfg!(target_os = "emscripten") && + !cfg!(target_arch = "wasm32"); if supports_threads { let cfg = thread::Builder::new().name(match name { DynTestName(ref name) => name.clone(), @@ -1554,16 +1584,14 @@ impl MetricMap { /// elimination. /// /// This function is a no-op, and does not even read from `dummy`. -#[cfg(not(any(all(target_os = "nacl", target_arch = "le32"), - target_arch = "asmjs", target_arch = "wasm32")))] +#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))] pub fn black_box(dummy: T) -> T { // we need to "use" the argument in some way LLVM can't // introspect. unsafe { asm!("" : : "r"(&dummy)) } dummy } -#[cfg(any(all(target_os = "nacl", target_arch = "le32"), - target_arch = "asmjs", target_arch = "wasm32"))] +#[cfg(any(target_arch = "asmjs", target_arch = "wasm32"))] #[inline(never)] pub fn black_box(dummy: T) -> T { dummy diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs index dc1464b905b0d..d8457dab51bba 100644 --- a/src/libunwind/build.rs +++ b/src/libunwind/build.rs @@ -27,7 +27,7 @@ fn main() { } else if target.contains("netbsd") { println!("cargo:rustc-link-lib=gcc_s"); } else if target.contains("openbsd") { - println!("cargo:rustc-link-lib=gcc"); + println!("cargo:rustc-link-lib=c++abi"); } else if target.contains("solaris") { println!("cargo:rustc-link-lib=gcc_s"); } else if target.contains("bitrig") { diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 461b49aa363b4..5bb1eb96dcfad 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -20,13 +20,20 @@ #![cfg_attr(not(target_env = "msvc"), feature(libc))] -#[cfg(not(target_env = "msvc"))] -extern crate libc; +#[macro_use] +mod macros; -#[cfg(not(target_env = "msvc"))] -mod libunwind; -#[cfg(not(target_env = "msvc"))] -pub use libunwind::*; +cfg_if! { + if #[cfg(target_env = "msvc")] { + // no extra unwinder support needed + } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + // no unwinder on the system! + } else { + extern crate libc; + mod libunwind; + pub use libunwind::*; + } +} #[cfg(all(target_env = "musl", not(target_arch = "mips")))] #[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))] diff --git a/src/libunwind/macros.rs b/src/libunwind/macros.rs new file mode 100644 index 0000000000000..26376a3733f4f --- /dev/null +++ b/src/libunwind/macros.rs @@ -0,0 +1,45 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// A macro for defining #[cfg] if-else statements. +/// +/// This is similar to the `if/elif` C preprocessor macro by allowing definition +/// of a cascade of `#[cfg]` cases, emitting the implementation which matches +/// first. +/// +/// This allows you to conveniently provide a long list #[cfg]'d blocks of code +/// without having to rewrite each clause multiple times. +macro_rules! cfg_if { + ($( + if #[cfg($($meta:meta),*)] { $($it:item)* } + ) else * else { + $($it2:item)* + }) => { + __cfg_if_items! { + () ; + $( ( ($($meta),*) ($($it)*) ), )* + ( () ($($it2)*) ), + } + } +} + +macro_rules! __cfg_if_items { + (($($not:meta,)*) ; ) => {}; + (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { + __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } + __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } + } +} + +macro_rules! __cfg_if_apply { + ($m:meta, $($it:item)*) => { + $(#[$m] $it)* + } +} diff --git a/src/llvm b/src/llvm index d9e7d2696e419..6d08185a5cf48 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit d9e7d2696e41983b6b5a0b4fac604b4e548a84d3 +Subproject commit 6d08185a5cf488d0a853b065a8a3a6d7a29f084f diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 335817fddbbe2..d33b52486296f 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -14,7 +14,7 @@ // When an executable or dylib image is linked, all user code and libraries are // "sandwiched" between these two object files, so code or data from rsbegin.o // become first in the respective sections of the image, whereas code and data -// from rsend.o become the last ones. This effect can be used to place symbols +// from rsend.o become the last ones. This effect can be used to place symbols // at the beginning or at the end of a section, as well as to insert any required // headers or footers. // @@ -31,14 +31,18 @@ trait Sized {} #[lang = "sync"] trait Sync {} +#[allow(unknown_lints)] +#[allow(auto_impl)] impl Sync for .. {} #[lang = "copy"] trait Copy {} #[lang = "freeze"] trait Freeze {} +#[allow(unknown_lints)] +#[allow(auto_impl)] impl Freeze for .. {} -#[lang="drop_in_place"] +#[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] pub unsafe fn drop_in_place(to_drop: *mut T) { diff --git a/src/rtstartup/rsend.rs b/src/rtstartup/rsend.rs index 9229b4e31289f..410366d0d7ff5 100644 --- a/src/rtstartup/rsend.rs +++ b/src/rtstartup/rsend.rs @@ -23,9 +23,11 @@ impl Sync for T {} trait Copy {} #[lang = "freeze"] trait Freeze {} +#[allow(unknown_lints)] +#[allow(auto_impl)] impl Freeze for .. {} -#[lang="drop_in_place"] +#[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] pub unsafe fn drop_in_place(to_drop: *mut T) { diff --git a/src/rustc/compiler_builtins_shim/Cargo.toml b/src/rustc/compiler_builtins_shim/Cargo.toml index e0026078a5d72..608e5f5f36d02 100644 --- a/src/rustc/compiler_builtins_shim/Cargo.toml +++ b/src/rustc/compiler_builtins_shim/Cargo.toml @@ -1,5 +1,3 @@ -# See libc_shim/Cargo.toml for why this exists - [package] name = "compiler_builtins" authors = ["The Rust Project Developers"] @@ -12,10 +10,27 @@ test = false doctest = false [dependencies] +# Specify the path to libcore; at the time of writing, removing this shim in +# favor of using compiler-builtins from git results in a compilation failure: +# +# Building stage0 std artifacts (x86_64-apple-darwin -> x86_64-apple-darwin) +# Compiling compiler_builtins v0.1.0 (https://github.com/rust-lang-nursery/compiler-builtins.git#23f14d3f) +# error[E0463]: can't find crate for `core` +# +# error: aborting due to previous error +# +# error: Could not compile `compiler_builtins`. +# +# Caused by: +# process didn't exit successfully: `/Users/tamird/src/rust/build/bootstrap/debug/rustc --crate-name compiler_builtins /Users/tamird/.cargo/git/checkouts/compiler-builtins-ec094dc45a0179c8/23f14d3/src/lib.rs --error-format json --crate-type lib --emit=dep-info,link -C opt-level=2 --cfg feature="c" --cfg feature="compiler-builtins" --cfg feature="default" --cfg feature="gcc" -C metadata=876d429e8d7eae1f -C extra-filename=-876d429e8d7eae1f --out-dir /Users/tamird/src/rust/build/x86_64-apple-darwin/stage0-std/x86_64-apple-darwin/release/deps --target x86_64-apple-darwin -L dependency=/Users/tamird/src/rust/build/x86_64-apple-darwin/stage0-std/x86_64-apple-darwin/release/deps -L dependency=/Users/tamird/src/rust/build/x86_64-apple-darwin/stage0-std/release/deps --cap-lints allow -L native=/Users/tamird/src/rust/build/x86_64-apple-darwin/stage0-std/x86_64-apple-darwin/release/build/compiler_builtins-f18fab55928102ad/out -l static=compiler-rt` (exit code: 101) +# thread 'main' panicked at 'command did not execute successfully: "/Users/tamird/src/rust/build/x86_64-apple-darwin/stage0/bin/cargo" "build" "-j" "4" "--target" "x86_64-apple-darwin" "--release" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/Users/tamird/src/rust/src/libstd/Cargo.toml" "--message-format" "json" +# expected success, got: exit code: 101', src/bootstrap/compile.rs:883:8 +# +# See https://github.com/rust-lang/rfcs/pull/1133. core = { path = "../../libcore" } [build-dependencies] -gcc = "0.3" +cc = "1.0.1" [features] c = [] diff --git a/src/rustc/compiler_builtins_shim/build.rs b/src/rustc/compiler_builtins_shim/build.rs index 546f60482e7bc..b37543e5f679d 100644 --- a/src/rustc/compiler_builtins_shim/build.rs +++ b/src/rustc/compiler_builtins_shim/build.rs @@ -8,11 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - -// See comments in Cargo.toml for why this exists - -fn main() { - println!("cargo:rustc-cfg=stdbuild"); - println!("cargo:rerun-if-changed=build.rs"); -} +// This file is left intentionally empty (and not removed) to avoid an issue +// where this crate is always considered dirty due to compiler-builtins' +// `cargo:rerun-if-changed=build.rs` directive; since the path is relative, it +// refers to this file when this shim crate is being built, and the absence of +// this file is considered by cargo to be equivalent to it having changed. diff --git a/src/rustc/dlmalloc_shim/Cargo.toml b/src/rustc/dlmalloc_shim/Cargo.toml new file mode 100644 index 0000000000000..cf8440c40da1a --- /dev/null +++ b/src/rustc/dlmalloc_shim/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "dlmalloc" +version = "0.0.0" +authors = ["The Rust Project Developers"] + +[lib] +path = "../../dlmalloc/src/lib.rs" +test = false +bench = false +doc = false + +[dependencies] +core = { path = "../../libcore" } +alloc = { path = "../../liballoc" } diff --git a/src/rustllvm/ArchiveWrapper.cpp b/src/rustllvm/ArchiveWrapper.cpp index 7f76861c0777d..b110013ceaed3 100644 --- a/src/rustllvm/ArchiveWrapper.cpp +++ b/src/rustllvm/ArchiveWrapper.cpp @@ -24,11 +24,7 @@ struct RustArchiveMember { RustArchiveMember() : Filename(nullptr), Name(nullptr), -#if LLVM_VERSION_GE(3, 8) Child(nullptr, nullptr, nullptr) -#else - Child(nullptr, nullptr) -#endif { } ~RustArchiveMember() {} @@ -38,13 +34,9 @@ struct RustArchiveIterator { bool First; Archive::child_iterator Cur; Archive::child_iterator End; -#if LLVM_VERSION_GE(3, 9) Error Err; RustArchiveIterator() : First(true), Err(Error::success()) {} -#else - RustArchiveIterator() : First(true) {} -#endif }; enum class LLVMRustArchiveKind { @@ -66,7 +58,7 @@ static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { case LLVMRustArchiveKind::COFF: return Archive::K_COFF; default: - llvm_unreachable("Bad ArchiveKind."); + report_fatal_error("Bad ArchiveKind."); } } @@ -84,19 +76,11 @@ extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { return nullptr; } -#if LLVM_VERSION_LE(3, 8) - ErrorOr> ArchiveOr = -#else Expected> ArchiveOr = -#endif Archive::create(BufOr.get()->getMemBufferRef()); if (!ArchiveOr) { -#if LLVM_VERSION_LE(3, 8) - LLVMRustSetLastError(ArchiveOr.getError().message().c_str()); -#else LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); -#endif return nullptr; } @@ -114,16 +98,12 @@ extern "C" LLVMRustArchiveIteratorRef LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { Archive *Archive = RustArchive->getBinary(); RustArchiveIterator *RAI = new RustArchiveIterator(); -#if LLVM_VERSION_LE(3, 8) - RAI->Cur = Archive->child_begin(); -#else RAI->Cur = Archive->child_begin(RAI->Err); if (RAI->Err) { LLVMRustSetLastError(toString(std::move(RAI->Err)).c_str()); delete RAI; return nullptr; } -#endif RAI->End = Archive->child_end(); return RAI; } @@ -141,12 +121,10 @@ LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { // but instead advance it *before* fetching the child in all later calls. if (!RAI->First) { ++RAI->Cur; -#if LLVM_VERSION_GE(3, 9) if (RAI->Err) { LLVMRustSetLastError(toString(std::move(RAI->Err)).c_str()); return nullptr; } -#endif } else { RAI->First = false; } @@ -154,16 +132,7 @@ LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { if (RAI->Cur == RAI->End) return nullptr; -#if LLVM_VERSION_EQ(3, 8) - const ErrorOr *Cur = RAI->Cur.operator->(); - if (!*Cur) { - LLVMRustSetLastError(Cur->getError().message().c_str()); - return nullptr; - } - const Archive::Child &Child = Cur->get(); -#else const Archive::Child &Child = *RAI->Cur.operator->(); -#endif Archive::Child *Ret = new Archive::Child(Child); return Ret; @@ -239,18 +208,13 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers, bool WriteSymbtab, LLVMRustArchiveKind RustKind) { -#if LLVM_VERSION_LE(3, 8) - std::vector Members; -#else std::vector Members; -#endif auto Kind = fromRust(RustKind); for (size_t I = 0; I < NumMembers; I++) { auto Member = NewMembers[I]; assert(Member->Name); if (Member->Filename) { -#if LLVM_VERSION_GE(3, 9) Expected MOrErr = NewArchiveMember::getFile(Member->Filename, true); if (!MOrErr) { @@ -261,15 +225,7 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers, MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); #endif Members.push_back(std::move(*MOrErr)); -#elif LLVM_VERSION_EQ(3, 8) - Members.push_back(NewArchiveIterator(Member->Filename)); -#else - Members.push_back(NewArchiveIterator(Member->Filename, Member->Name)); -#endif } else { -#if LLVM_VERSION_LE(3, 8) - Members.push_back(NewArchiveIterator(Member->Child, Member->Name)); -#else Expected MOrErr = NewArchiveMember::getOldMember(Member->Child, true); if (!MOrErr) { @@ -277,14 +233,9 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers, return LLVMRustResult::Failure; } Members.push_back(std::move(*MOrErr)); -#endif } } -#if LLVM_VERSION_GE(3, 8) auto Pair = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); -#else - auto Pair = writeArchive(Dst, Members, WriteSymbtab, Kind, true); -#endif if (!Pair.second) return LLVMRustResult::Success; LLVMRustSetLastError(Pair.second.message().c_str()); diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 2f966e5a1c5e2..1797e19c549cf 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "rustllvm.h" @@ -27,6 +28,12 @@ #if LLVM_VERSION_GE(4, 0) #include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" +#include "llvm/LTO/LTO.h" +#if LLVM_VERSION_LE(4, 0) +#include "llvm/Object/ModuleSummaryIndexObjectFile.h" +#endif #endif #include "llvm-c/Transforms/PassManagerBuilder.h" @@ -52,9 +59,6 @@ extern "C" void LLVMInitializePasses() { initializeVectorization(Registry); initializeIPO(Registry); initializeAnalysis(Registry); -#if LLVM_VERSION_EQ(3, 7) - initializeIPA(Registry); -#endif initializeTransformUtils(Registry); initializeInstCombine(Registry); initializeInstrumentation(Registry); @@ -102,6 +106,19 @@ extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { PMB->add(Pass); } +extern "C" +bool LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + LLVMPassManagerBuilderRef PMBR, + LLVMPassManagerRef PMR +) { +#if LLVM_VERSION_GE(4, 0) + unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); + return true; +#else + return false; +#endif +} + #ifdef LLVM_COMPONENT_X86 #define SUBTARGET_X86 SUBTARGET(X86) #else @@ -216,7 +233,7 @@ static CodeModel::Model fromRust(LLVMRustCodeModel Model) { case LLVMRustCodeModel::Large: return CodeModel::Large; default: - llvm_unreachable("Bad CodeModel."); + report_fatal_error("Bad CodeModel."); } } @@ -239,7 +256,7 @@ static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { case LLVMRustCodeGenOptLevel::Aggressive: return CodeGenOpt::Aggressive; default: - llvm_unreachable("Bad CodeGenOptLevel."); + report_fatal_error("Bad CodeGenOptLevel."); } } @@ -253,18 +270,10 @@ enum class LLVMRustRelocMode { ROPIRWPI, }; -#if LLVM_VERSION_LE(3, 8) -static Reloc::Model fromRust(LLVMRustRelocMode RustReloc) { -#else static Optional fromRust(LLVMRustRelocMode RustReloc) { -#endif switch (RustReloc) { case LLVMRustRelocMode::Default: -#if LLVM_VERSION_LE(3, 8) - return Reloc::Default; -#else return None; -#endif case LLVMRustRelocMode::Static: return Reloc::Static; case LLVMRustRelocMode::PIC: @@ -283,7 +292,7 @@ static Optional fromRust(LLVMRustRelocMode RustReloc) { break; #endif } - llvm_unreachable("Bad RelocModel."); + report_fatal_error("Bad RelocModel."); } #if LLVM_RUSTLLVM @@ -347,7 +356,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( LLVMRustCodeModel RustCM, LLVMRustRelocMode RustReloc, LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, bool PositionIndependentExecutable, bool FunctionSections, - bool DataSections) { + bool DataSections, + bool TrapUnreachable, + bool Singlethread) { auto CM = fromRust(RustCM); auto OptLevel = fromRust(RustOptLevel); @@ -368,9 +379,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } TargetOptions Options; -#if LLVM_VERSION_LE(3, 8) - Options.PositionIndependentExecutable = PositionIndependentExecutable; -#endif Options.FloatABIType = FloatABI::Default; if (UseSoftFloat) { @@ -379,6 +387,18 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.DataSections = DataSections; Options.FunctionSections = FunctionSections; + if (TrapUnreachable) { + // Tell LLVM to translate `unreachable` into an explicit trap instruction. + // This limits the extent of possible undefined behavior in some cases, as + // it prevents control flow from "falling through" into whatever code + // happens to be laid out next in memory. + Options.TrapUnreachable = true; + } + + if (Singlethread) { + Options.ThreadModel = ThreadModel::Single; + } + TargetMachine *TM = TheTarget->createTargetMachine( Trip.getTriple(), RealCPU, Feature, Options, RM, CM, OptLevel); return wrap(TM); @@ -478,7 +498,7 @@ static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { case LLVMRustFileType::ObjectFile: return TargetMachine::CGFT_ObjectFile; default: - llvm_unreachable("Bad FileType."); + report_fatal_error("Bad FileType."); } } @@ -686,10 +706,6 @@ extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, size_t Len) { llvm::legacy::PassManager passes; -#if LLVM_VERSION_LE(3, 8) - ArrayRef Ref(Symbols, Len); - passes.add(llvm::createInternalizePass(Ref)); -#else auto PreserveFunctions = [=](const GlobalValue &GV) { for (size_t I = 0; I < Len; I++) { if (GV.getName() == Symbols[I]) { @@ -700,7 +716,6 @@ extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, }; passes.add(llvm::createInternalizePass(PreserveFunctions)); -#endif passes.run(*unwrap(M)); } @@ -736,7 +751,437 @@ extern "C" LLVMTargetDataRef LLVMRustGetModuleDataLayout(LLVMModuleRef M) { } extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { -#if LLVM_VERSION_GE(3, 9) unwrap(M)->setPIELevel(PIELevel::Level::Large); +} + +extern "C" bool +LLVMRustThinLTOAvailable() { +#if LLVM_VERSION_GE(4, 0) + return true; +#else + return false; #endif } + +#if LLVM_VERSION_GE(4, 0) + +// Here you'll find an implementation of ThinLTO as used by the Rust compiler +// right now. This ThinLTO support is only enabled on "recent ish" versions of +// LLVM, and otherwise it's just blanket rejected from other compilers. +// +// Most of this implementation is straight copied from LLVM. At the time of +// this writing it wasn't *quite* suitable to reuse more code from upstream +// for our purposes, but we should strive to upstream this support once it's +// ready to go! I figure we may want a bit of testing locally first before +// sending this upstream to LLVM. I hear though they're quite eager to receive +// feedback like this! +// +// If you're reading this code and wondering "what in the world" or you're +// working "good lord by LLVM upgrade is *still* failing due to these bindings" +// then fear not! (ok maybe fear a little). All code here is mostly based +// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. +// +// You'll find that the general layout here roughly corresponds to the `run` +// method in that file as well as `ProcessThinLTOModule`. Functions are +// specifically commented below as well, but if you're updating this code +// or otherwise trying to understand it, the LLVM source will be useful in +// interpreting the mysteries within. +// +// Otherwise I'll apologize in advance, it probably requires a relatively +// significant investment on your part to "truly understand" what's going on +// here. Not saying I do myself, but it took me awhile staring at LLVM's source +// and various online resources about ThinLTO to make heads or tails of all +// this. + +extern "C" bool +LLVMRustWriteThinBitcodeToFile(LLVMPassManagerRef PMR, + LLVMModuleRef M, + const char *BcFile) { + llvm::legacy::PassManager *PM = unwrap(PMR); + std::error_code EC; + llvm::raw_fd_ostream bc(BcFile, EC, llvm::sys::fs::F_None); + if (EC) { + LLVMRustSetLastError(EC.message().c_str()); + return false; + } + PM->add(createWriteThinLTOBitcodePass(bc)); + PM->run(*unwrap(M)); + delete PM; + return true; +} + +// This is a shared data structure which *must* be threadsafe to share +// read-only amongst threads. This also corresponds basically to the arguments +// of the `ProcessThinLTOModule` function in the LLVM source. +struct LLVMRustThinLTOData { + // The combined index that is the global analysis over all modules we're + // performing ThinLTO for. This is mostly managed by LLVM. + ModuleSummaryIndex Index; + + // All modules we may look at, stored as in-memory serialized versions. This + // is later used when inlining to ensure we can extract any module to inline + // from. + StringMap ModuleMap; + + // A set that we manage of everything we *don't* want internalized. Note that + // this includes all transitive references right now as well, but it may not + // always! + DenseSet GUIDPreservedSymbols; + + // Not 100% sure what these are, but they impact what's internalized and + // what's inlined across modules, I believe. + StringMap ImportLists; + StringMap ExportLists; + StringMap ModuleToDefinedGVSummaries; +}; + +// Just an argument to the `LLVMRustCreateThinLTOData` function below. +struct LLVMRustThinLTOModule { + const char *identifier; + const char *data; + size_t len; +}; + +// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it +// does. +static const GlobalValueSummary * +getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { + auto StrongDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage) && + !GlobalValue::isWeakForLinker(Linkage); + }); + if (StrongDefForLinker != GVSummaryList.end()) + return StrongDefForLinker->get(); + + auto FirstDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + if (FirstDefForLinker == GVSummaryList.end()) + return nullptr; + return FirstDefForLinker->get(); +} + +// The main entry point for creating the global ThinLTO analysis. The structure +// here is basically the same as before threads are spawned in the `run` +// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { + auto Ret = llvm::make_unique(); + + // Load each module's summary and merge it into one combined index + for (int i = 0; i < num_modules; i++) { + auto module = &modules[i]; + StringRef buffer(module->data, module->len); + MemoryBufferRef mem_buffer(buffer, module->identifier); + + Ret->ModuleMap[module->identifier] = mem_buffer; + +#if LLVM_VERSION_GE(5, 0) + if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { + LLVMRustSetLastError(toString(std::move(Err)).c_str()); + return nullptr; + } +#else + Expected> ObjOrErr = + object::ModuleSummaryIndexObjectFile::create(mem_buffer); + if (!ObjOrErr) { + LLVMRustSetLastError(toString(ObjOrErr.takeError()).c_str()); + return nullptr; + } + auto Index = (*ObjOrErr)->takeIndex(); + Ret->Index.mergeFrom(std::move(Index), i); +#endif + } + + // Collect for each module the list of function it defines (GUID -> Summary) + Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is then needed + // for internalization. + for (int i = 0; i < num_symbols; i++) { + auto GUID = GlobalValue::getGUID(preserved_symbols[i]); + Ret->GUIDPreservedSymbols.insert(GUID); + } + + // Collect the import/export lists for all modules from the call-graph in the + // combined index + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` +#if LLVM_VERSION_GE(5, 0) + computeDeadSymbols(Ret->Index, Ret->GUIDPreservedSymbols); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists + ); +#else + auto DeadSymbols = computeDeadSymbols(Ret->Index, Ret->GUIDPreservedSymbols); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists, + &DeadSymbols + ); +#endif + + // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it + // impacts the caching. + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this + // being lifted from `lib/LTO/LTO.cpp` as well + StringMap> ResolvedODR; + DenseMap PrevailingCopy; + for (auto &I : Ret->Index) { +#if LLVM_VERSION_GE(5, 0) + if (I.second.SummaryList.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList); +#else + if (I.second.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second); +#endif + } + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + auto recordNewLinkage = [&](StringRef ModuleIdentifier, + GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) { + ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + }; + thinLTOResolveWeakForLinkerInIndex(Ret->Index, isPrevailing, recordNewLinkage); + + // Here we calculate an `ExportedGUIDs` set for use in the `isExported` + // callback below. This callback below will dictate the linkage for all + // summaries in the index, and we basically just only want to ensure that dead + // symbols are internalized. Otherwise everything that's already external + // linkage will stay as external, and internal will stay as internal. + std::set ExportedGUIDs; + for (auto &List : Ret->Index) { + for (auto &GVS: List.second) { + if (!GlobalValue::isExternalLinkage(GVS->linkage())) + continue; + auto GUID = GVS->getOriginalName(); + if (!DeadSymbols.count(GUID)) + ExportedGUIDs.insert(GUID); + } + } + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(GUID)) || + ExportedGUIDs.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); + + return Ret.release(); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + delete Data; +} + +// Below are the various passes that happen *per module* when doing ThinLTO. +// +// In other words, these are the functions that are all run concurrently +// with one another, one per module. The passes here correspond to the analysis +// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the +// `ProcessThinLTOModule` function. Here they're split up into separate steps +// so rustc can save off the intermediate bytecode between each step. + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + if (renameModuleForThinLTO(Mod, Data->Index)) { + LLVMRustSetLastError("renameModuleForThinLTO failed"); + return false; + } + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOInternalizeModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + auto Loader = [&](StringRef Identifier) { + const auto &Memory = Data->ModuleMap.lookup(Identifier); + auto &Context = Mod.getContext(); + return getLazyBitcodeModule(Memory, Context, true, true); + }; + FunctionImporter Importer(Data->Index, Loader); + Expected Result = Importer.importFunctions(Mod, ImportList); + if (!Result) { + LLVMRustSetLastError(toString(Result.takeError()).c_str()); + return false; + } + return true; +} + +// This struct and various functions are sort of a hack right now, but the +// problem is that we've got in-memory LLVM modules after we generate and +// optimize all codegen-units for one compilation in rustc. To be compatible +// with the LTO support above we need to serialize the modules plus their +// ThinLTO summary into memory. +// +// This structure is basically an owned version of a serialize module, with +// a ThinLTO summary attached. +struct LLVMRustThinLTOBuffer { + std::string data; +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { + auto Ret = llvm::make_unique(); + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createWriteThinLTOBitcodePass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.length(); +} + +// This is what we used to parse upstream bitcode for actual ThinLTO +// processing. We'll call this once per module optimized through ThinLTO, and +// it'll be called concurrently on many threads. +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, identifier); + unwrap(Context)->enableDebugTypeODRUniquing(); + Expected> SrcOrError = + parseBitcodeFile(Buffer, *unwrap(Context)); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return nullptr; + } + return wrap(std::move(*SrcOrError).release()); +} + +#else + +extern "C" bool +LLVMRustWriteThinBitcodeToFile(LLVMPassManagerRef PMR, + LLVMModuleRef M, + const char *BcFile) { + report_fatal_error("ThinLTO not available"); +} + +struct LLVMRustThinLTOData { +}; + +struct LLVMRustThinLTOModule { +}; + +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + report_fatal_error("ThinLTO not available"); +} + +struct LLVMRustThinLTOBuffer { +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + report_fatal_error("ThinLTO not available"); +} +#endif // LLVM_VERSION_GE(4, 0) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 15a04ba00e2a5..ee48d49da4691 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -15,6 +15,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/IR/CallSite.h" @@ -53,10 +54,10 @@ static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { return AtomicOrdering::SequentiallyConsistent; } - llvm_unreachable("Invalid LLVMAtomicOrdering value!"); + report_fatal_error("Invalid LLVMAtomicOrdering value!"); } -static char *LastError; +static LLVM_THREAD_LOCAL char *LastError; extern "C" LLVMMemoryBufferRef LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) { @@ -160,7 +161,7 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { case SanitizeMemory: return Attribute::SanitizeMemory; } - llvm_unreachable("bad AttributeKind"); + report_fatal_error("bad AttributeKind"); } extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, @@ -177,6 +178,22 @@ extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, #endif } +extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint32_t Bytes) { + CallSite Call = CallSite(unwrap(Instr)); + AttrBuilder B; + B.addAlignmentAttr(Bytes); +#if LLVM_VERSION_GE(5, 0) + Call.setAttributes(Call.getAttributes().addAttributes( + Call->getContext(), Index, B)); +#else + Call.setAttributes(Call.getAttributes().addAttributes( + Call->getContext(), Index, + AttributeSet::get(Call->getContext(), Index, B))); +#endif +} + extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, unsigned Index, uint64_t Bytes) { @@ -193,6 +210,22 @@ extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, #endif } +extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallSite Call = CallSite(unwrap(Instr)); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); +#if LLVM_VERSION_GE(5, 0) + Call.setAttributes(Call.getAttributes().addAttributes( + Call->getContext(), Index, B)); +#else + Call.setAttributes(Call.getAttributes().addAttributes( + Call->getContext(), Index, + AttributeSet::get(Call->getContext(), Index, B))); +#endif +} + extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, LLVMRustAttribute RustAttr) { Function *A = unwrap(Fn); @@ -205,6 +238,19 @@ extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, #endif } +extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn, + unsigned Index, + uint32_t Bytes) { + Function *A = unwrap(Fn); + AttrBuilder B; + B.addAlignmentAttr(Bytes); +#if LLVM_VERSION_GE(5, 0) + A->addAttributes(Index, B); +#else + A->addAttributes(Index, AttributeSet::get(A->getContext(), Index, B)); +#endif +} + extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, uint64_t Bytes) { Function *A = unwrap(Fn); @@ -217,6 +263,19 @@ extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, #endif } +extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, + unsigned Index, + uint64_t Bytes) { + Function *A = unwrap(Fn); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); +#if LLVM_VERSION_GE(5, 0) + A->addAttributes(Index, B); +#else + A->addAttributes(Index, AttributeSet::get(A->getContext(), Index, B)); +#endif +} + extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, unsigned Index, const char *Name, @@ -256,21 +315,18 @@ extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { extern "C" LLVMValueRef LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name, - LLVMAtomicOrdering Order, unsigned Alignment) { + LLVMAtomicOrdering Order) { LoadInst *LI = new LoadInst(unwrap(Source), 0); LI->setAtomic(fromRust(Order)); - LI->setAlignment(Alignment); return wrap(unwrap(B)->Insert(LI, Name)); } extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, LLVMValueRef V, LLVMValueRef Target, - LLVMAtomicOrdering Order, - unsigned Alignment) { + LLVMAtomicOrdering Order) { StoreInst *SI = new StoreInst(unwrap(V), unwrap(Target)); SI->setAtomic(fromRust(Order)); - SI->setAlignment(Alignment); return wrap(unwrap(B)->Insert(SI)); } @@ -300,7 +356,7 @@ static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) { case LLVMRustSynchronizationScope::CrossThread: return SyncScope::System; default: - llvm_unreachable("bad SynchronizationScope."); + report_fatal_error("bad SynchronizationScope."); } } #else @@ -311,7 +367,7 @@ static SynchronizationScope fromRust(LLVMRustSynchronizationScope Scope) { case LLVMRustSynchronizationScope::CrossThread: return CrossThread; default: - llvm_unreachable("bad SynchronizationScope."); + report_fatal_error("bad SynchronizationScope."); } } #endif @@ -341,7 +397,7 @@ static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { case LLVMRustAsmDialect::Intel: return InlineAsm::AD_Intel; default: - llvm_unreachable("bad AsmDialect."); + report_fatal_error("bad AsmDialect."); } } @@ -553,9 +609,6 @@ LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, LLVMMetadataRef File, LLVMMetadataRef ParameterTypes) { return wrap(Builder->createSubroutineType( -#if LLVM_VERSION_EQ(3, 7) - unwrapDI(File), -#endif DITypeRefArray(unwrap(ParameterTypes)))); } @@ -565,7 +618,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( LLVMMetadataRef Ty, bool IsLocalToUnit, bool IsDefinition, unsigned ScopeLine, LLVMRustDIFlags Flags, bool IsOptimized, LLVMValueRef Fn, LLVMMetadataRef TParam, LLVMMetadataRef Decl) { -#if LLVM_VERSION_GE(3, 8) DITemplateParameterArray TParams = DITemplateParameterArray(unwrap(TParam)); DISubprogram *Sub = Builder->createFunction( @@ -575,13 +627,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( unwrapDIPtr(Decl)); unwrap(Fn)->setSubprogram(Sub); return wrap(Sub); -#else - return wrap(Builder->createFunction( - unwrapDI(Scope), Name, LinkageName, unwrapDI(File), - LineNo, unwrapDI(Ty), IsLocalToUnit, IsDefinition, - ScopeLine, fromRust(Flags), IsOptimized, unwrap(Fn), - unwrapDIPtr(TParam), unwrapDIPtr(Decl))); -#endif } extern "C" LLVMMetadataRef @@ -685,14 +730,13 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( const char *Name, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, unsigned ArgNo, uint32_t AlignInBits) { -#if LLVM_VERSION_GE(3, 8) if (Tag == 0x100) { // DW_TAG_auto_variable return wrap(Builder->createAutoVariable( unwrapDI(Scope), Name, unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags) #if LLVM_VERSION_GE(4, 0) , - AlignInBits + AlignInBits #endif )); } else { @@ -700,11 +744,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( unwrapDI(Scope), Name, ArgNo, unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); } -#else - return wrap(Builder->createLocalVariable( - Tag, unwrapDI(Scope), Name, unwrapDI(File), LineNo, - unwrapDI(Ty), AlwaysPreserve, fromRust(Flags), ArgNo)); -#endif } extern "C" LLVMMetadataRef @@ -879,11 +918,8 @@ extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef DstRef, char *BC, DiagnosticPrinterRawOStream DP(Stream); #if LLVM_VERSION_GE(4, 0) if (Linker::linkModules(*Dst, std::move(Src))) { -#elif LLVM_VERSION_GE(3, 8) - if (Linker::linkModules(*Dst, std::move(Src.get()))) { #else - if (Linker::LinkModules(Dst, Src->get(), - [&](const DiagnosticInfo &DI) { DI.print(DP); })) { + if (Linker::linkModules(*Dst, std::move(Src.get()))) { #endif LLVMRustSetLastError(Err.c_str()); return false; @@ -891,6 +927,23 @@ extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef DstRef, char *BC, return true; } +extern "C" bool LLVMRustLinkInParsedExternalBitcode( + LLVMModuleRef DstRef, LLVMModuleRef SrcRef) { +#if LLVM_VERSION_GE(4, 0) + Module *Dst = unwrap(DstRef); + std::unique_ptr Src(unwrap(SrcRef)); + + if (Linker::linkModules(*Dst, std::move(Src))) { + LLVMRustSetLastError("failed to link modules"); + return false; + } + return true; +#else + LLVMRustSetLastError("can't link parsed modules on this LLVM"); + return false; +#endif +} + // Note that the two following functions look quite similar to the // LLVMGetSectionName function. Sadly, it appears that this function only // returns a char* pointer, which isn't guaranteed to be null-terminated. The @@ -1013,20 +1066,14 @@ static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { return LLVMRustDiagnosticKind::OptimizationRemarkMissed; case DK_OptimizationRemarkAnalysis: return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis; -#if LLVM_VERSION_GE(3, 8) case DK_OptimizationRemarkAnalysisFPCommute: return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute; case DK_OptimizationRemarkAnalysisAliasing: return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing; -#endif default: -#if LLVM_VERSION_GE(3, 9) return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) ? LLVMRustDiagnosticKind::OptimizationRemarkOther : LLVMRustDiagnosticKind::Other; -#else - return LLVMRustDiagnosticKind::Other; -#endif } } @@ -1071,12 +1118,10 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { return LLVMVectorTypeKind; case Type::X86_MMXTyID: return LLVMX86_MMXTypeKind; -#if LLVM_VERSION_GE(3, 8) case Type::TokenTyID: return LLVMTokenTypeKind; -#endif } - llvm_unreachable("Unhandled TypeID."); + report_fatal_error("Unhandled TypeID."); } extern "C" void LLVMRustWriteDebugLocToString(LLVMContextRef C, @@ -1111,7 +1156,6 @@ extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { -#if LLVM_VERSION_GE(3, 8) Value **Args = unwrap(LLArgs); if (ParentPad == nullptr) { Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); @@ -1119,43 +1163,28 @@ extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, } return wrap(unwrap(B)->CreateCleanupPad( unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); -#else - return nullptr; -#endif } extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B, LLVMValueRef CleanupPad, LLVMBasicBlockRef UnwindBB) { -#if LLVM_VERSION_GE(3, 8) CleanupPadInst *Inst = cast(unwrap(CleanupPad)); return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB))); -#else - return nullptr; -#endif } extern "C" LLVMValueRef LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad, unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { -#if LLVM_VERSION_GE(3, 8) Value **Args = unwrap(LLArgs); return wrap(unwrap(B)->CreateCatchPad( unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); -#else - return nullptr; -#endif } extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B, LLVMValueRef Pad, LLVMBasicBlockRef BB) { -#if LLVM_VERSION_GE(3, 8) return wrap(unwrap(B)->CreateCatchRet(cast(unwrap(Pad)), unwrap(BB))); -#else - return nullptr; -#endif } extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, @@ -1163,27 +1192,20 @@ extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, LLVMBasicBlockRef BB, unsigned NumHandlers, const char *Name) { -#if LLVM_VERSION_GE(3, 8) if (ParentPad == nullptr) { Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); ParentPad = wrap(Constant::getNullValue(Ty)); } return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB), NumHandlers, Name)); -#else - return nullptr; -#endif } extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, LLVMBasicBlockRef Handler) { -#if LLVM_VERSION_GE(3, 8) Value *CatchSwitch = unwrap(CatchSwitchRef); cast(CatchSwitch)->addHandler(unwrap(Handler)); -#endif } -#if LLVM_VERSION_GE(3, 8) extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, LLVMValueRef *Inputs, unsigned NumInputs) { @@ -1215,28 +1237,6 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, makeArrayRef(unwrap(Args), NumArgs), Bundles, Name)); } -#else -extern "C" void *LLVMRustBuildOperandBundleDef(const char *Name, - LLVMValueRef *Inputs, - unsigned NumInputs) { - return nullptr; -} - -extern "C" void LLVMRustFreeOperandBundleDef(void *Bundle) {} - -extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, - LLVMValueRef *Args, unsigned NumArgs, - void *Bundle, const char *Name) { - return LLVMBuildCall(B, Fn, Args, NumArgs, Name); -} - -extern "C" LLVMValueRef -LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, LLVMBasicBlockRef Then, - LLVMBasicBlockRef Catch, void *Bundle, const char *Name) { - return LLVMBuildInvoke(B, Fn, Args, NumArgs, Then, Catch, Name); -} -#endif extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, LLVMBasicBlockRef BB) { @@ -1297,7 +1297,7 @@ static LLVMRustLinkage toRust(LLVMLinkage Linkage) { case LLVMCommonLinkage: return LLVMRustLinkage::CommonLinkage; default: - llvm_unreachable("Invalid LLVMRustLinkage value!"); + report_fatal_error("Invalid LLVMRustLinkage value!"); } } @@ -1326,7 +1326,7 @@ static LLVMLinkage fromRust(LLVMRustLinkage Linkage) { case LLVMRustLinkage::CommonLinkage: return LLVMCommonLinkage; } - llvm_unreachable("Invalid LLVMRustLinkage value!"); + report_fatal_error("Invalid LLVMRustLinkage value!"); } extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { @@ -1374,7 +1374,7 @@ static LLVMRustVisibility toRust(LLVMVisibility Vis) { case LLVMProtectedVisibility: return LLVMRustVisibility::Protected; } - llvm_unreachable("Invalid LLVMRustVisibility value!"); + report_fatal_error("Invalid LLVMRustVisibility value!"); } static LLVMVisibility fromRust(LLVMRustVisibility Vis) { @@ -1386,7 +1386,7 @@ static LLVMVisibility fromRust(LLVMRustVisibility Vis) { case LLVMRustVisibility::Protected: return LLVMProtectedVisibility; } - llvm_unreachable("Invalid LLVMRustVisibility value!"); + report_fatal_error("Invalid LLVMRustVisibility value!"); } extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { @@ -1403,3 +1403,47 @@ extern "C" void LLVMRustSetVisibility(LLVMValueRef V, LLVMRustVisibility RustVisibility) { LLVMSetVisibility(V, fromRust(RustVisibility)); } + +struct LLVMRustModuleBuffer { + std::string data; +}; + +extern "C" LLVMRustModuleBuffer* +LLVMRustModuleBufferCreate(LLVMModuleRef M) { + auto Ret = llvm::make_unique(); + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createBitcodeWriterPass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.length(); +} + +extern "C" uint64_t +LLVMRustModuleCost(LLVMModuleRef M) { + Module &Mod = *unwrap(M); + uint64_t cost = 0; + for (auto &F : Mod.functions()) { + (void)F; + cost += 1; + } + return cost; +} diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index c30290167d48e..feb6d98fe4614 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2017-07-18 +2017-11-07 diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 20816af2f1c20..8c2f855c226ba 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -57,11 +57,7 @@ #define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) -#if LLVM_VERSION_GE(3, 7) #include "llvm/IR/LegacyPassManager.h" -#else -#include "llvm/PassManager.h" -#endif #if LLVM_VERSION_GE(4, 0) #include "llvm/Bitcode/BitcodeReader.h" diff --git a/src/stage0.txt b/src/stage0.txt index 892679c192916..eb0bedf64b23c 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2017-08-29 +date: 2017-11-21 rustc: beta cargo: beta diff --git a/src/test/COMPILER_TESTS.md b/src/test/COMPILER_TESTS.md index 0380454b8278d..0bc29e8b5aa9e 100644 --- a/src/test/COMPILER_TESTS.md +++ b/src/test/COMPILER_TESTS.md @@ -106,7 +106,7 @@ result is then compared against reference files named those files doesn't exist, the output must be empty. If the test run fails, we will print out the current output, but it is also saved in `build//test/ui/hello_world/main.stdout` (this path is -printed as part of the test failure mesage), so you can run `diff` and +printed as part of the test failure message), so you can run `diff` and so forth. ### Editing and updating the reference files diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index d8e6028b799fb..b8033b88fb75e 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -10,8 +10,9 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { @@ -22,7 +23,7 @@ impl Drop for StructWithDtor { //~ TRANS_ITEM fn drop_in_place_intrinsic::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -34,7 +35,7 @@ fn drop_slice_in_place(x: &[StructWithDtor]) { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index 06e02b100152e..65936d12e316f 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -10,6 +10,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -45,7 +46,7 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue0[Internal] impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] @@ -54,11 +55,11 @@ impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; @@ -67,17 +68,17 @@ fn main() { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue.cgu-0[Internal] + //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index 9c6bdb6624eae..e32366d15c337 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -10,6 +10,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -31,13 +32,13 @@ impl Trait for Struct { fn main() { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index 5f70ff396ddd5..5765f230e8bd4 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -10,10 +10,11 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] struct StructWithDrop { x: i32 } @@ -27,7 +28,7 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] enum EnumWithDrop { A(i32) } diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index e41cb34eec6ab..be560690e5190 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -10,14 +10,15 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Leaf; impl Drop for Leaf { @@ -38,15 +39,15 @@ fn main() { let _ = Root(Intermediate(Leaf)); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0i16))); } diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index 39043cf87cbec..ad1475a73f7cc 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -10,10 +10,11 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue0[Internal] struct Dropped; impl Drop for Dropped { @@ -23,10 +24,10 @@ impl Drop for Dropped { //~ TRANS_ITEM fn tuple_drop_glue::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] let x = (0u32, Dropped); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] let x = (0i16, (Dropped, true)); } diff --git a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs new file mode 100644 index 0000000000000..59b25d8beca02 --- /dev/null +++ b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=lazy + +// NB: We do not expect *any* translation item to be generated here. + +#![feature(const_fn)] +#![deny(dead_code)] +#![crate_type = "rlib"] + +pub const fn foo(x: u32) -> u32 { + x + 0xf00 +} diff --git a/src/test/codegen-units/item-collection/unreferenced-inline-function.rs b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs new file mode 100644 index 0000000000000..75d41a38012cd --- /dev/null +++ b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=lazy + +// NB: We do not expect *any* translation item to be generated here. + +#![deny(dead_code)] +#![crate_type = "rlib"] + +#[inline] +pub fn foo() -> bool { + [1, 2] == [3, 4] +} + diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index de7613741b27b..d7e457cde8a90 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -10,6 +10,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(coerce_unsized)] @@ -57,13 +58,13 @@ fn main() { // simple case let bool_sized = &true; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; let char_sized = &'a'; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; @@ -73,13 +74,13 @@ fn main() _b: 2, _c: 3.0f64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper; } diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index 4e6ae167024e3..da96c5e183d79 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -13,9 +13,10 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue +// compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type="rlib"] // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; @@ -24,20 +25,20 @@ extern crate cgu_extern_drop_glue; struct LocalStruct(cgu_extern_drop_glue::Struct); -//~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[Internal] -fn user() +//~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] +pub fn user() { //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } -mod mod1 { +pub mod mod1 { use cgu_extern_drop_glue; struct LocalStruct(cgu_extern_drop_glue::Struct); - //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[Internal] - fn user() + //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] + pub fn user() { //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); diff --git a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs index 20920c9ebe432..01600c03ba2cd 100644 --- a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -12,6 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate +// compile-flags:-Zinline-in-all-cgus #![crate_type="lib"] @@ -34,10 +35,10 @@ pub fn user() cgu_explicit_inlining::never_inlined(); } -mod mod1 { +pub mod mod1 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[Internal] + //~ TRANS_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] pub fn user() { cgu_explicit_inlining::inlined(); @@ -47,10 +48,10 @@ mod mod1 { } } -mod mod2 { +pub mod mod2 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[Internal] + //~ TRANS_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] pub fn user() { cgu_explicit_inlining::always_inlined(); diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index d2ce847e108f0..f7c05285ed63c 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -12,9 +12,10 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue +// compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type="rlib"] //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] struct Struct { @@ -31,8 +32,8 @@ struct Outer { _a: Struct } -//~ TRANS_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[Internal] -fn user() +//~ TRANS_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] +pub fn user() { let _ = Outer { _a: Struct { @@ -41,7 +42,7 @@ fn user() }; } -mod mod1 +pub mod mod1 { use super::Struct; @@ -52,8 +53,8 @@ mod mod1 _b: (u32, Struct), } - //~ TRANS_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[Internal] - fn user() + //~ TRANS_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] + pub fn user() { let _ = Struct2 { _a: Struct { _a: 0 }, diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs new file mode 100644 index 0000000000000..cf197301eec16 --- /dev/null +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -0,0 +1,54 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// We specify -Z incremental here because we want to test the partitioning for +// incremental compilation +// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all +// compile-flags:-Zinline-in-all-cgus=no + +#![allow(dead_code)] +#![crate_type="lib"] + +mod inline { + + //~ TRANS_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] + #[inline] + pub fn inlined_function() + { + + } +} + +pub mod user1 { + use super::inline; + + //~ TRANS_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] + pub fn foo() { + inline::inlined_function(); + } +} + +pub mod user2 { + use super::inline; + + //~ TRANS_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] + pub fn bar() { + inline::inlined_function(); + } +} + +pub mod non_user { + + //~ TRANS_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] + pub fn baz() { + + } +} diff --git a/src/test/codegen-units/partitioning/local-inlining.rs b/src/test/codegen-units/partitioning/local-inlining.rs index a4d9e60d2280c..3502aa59fdccc 100644 --- a/src/test/codegen-units/partitioning/local-inlining.rs +++ b/src/test/codegen-units/partitioning/local-inlining.rs @@ -12,6 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining +// compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] #![crate_type="lib"] @@ -27,28 +28,28 @@ mod inline { } } -mod user1 { +pub mod user1 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[Internal] - fn foo() { + //~ TRANS_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] + pub fn foo() { inline::inlined_function(); } } -mod user2 { +pub mod user2 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[Internal] - fn bar() { + //~ TRANS_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] + pub fn bar() { inline::inlined_function(); } } -mod non_user { +pub mod non_user { - //~ TRANS_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[Internal] - fn baz() { + //~ TRANS_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] + pub fn baz() { } } diff --git a/src/test/codegen-units/partitioning/local-transitive-inlining.rs b/src/test/codegen-units/partitioning/local-transitive-inlining.rs index 1beaa186d9ee5..ed883954f3f40 100644 --- a/src/test/codegen-units/partitioning/local-transitive-inlining.rs +++ b/src/test/codegen-units/partitioning/local-transitive-inlining.rs @@ -12,9 +12,10 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining +// compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type="rlib"] mod inline { @@ -36,19 +37,19 @@ mod direct_user { } } -mod indirect_user { +pub mod indirect_user { use super::direct_user; - //~ TRANS_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[Internal] - fn bar() { + //~ TRANS_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] + pub fn bar() { direct_user::foo(); } } -mod non_user { +pub mod non_user { - //~ TRANS_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[Internal] - fn baz() { + //~ TRANS_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] + pub fn baz() { } } diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index 8cbce12b52cad..12ef34441ff39 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -13,7 +13,7 @@ // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/statics -#![crate_type="lib"] +#![crate_type="rlib"] //~ TRANS_ITEM static statics::FOO[0] @@ statics[Internal] static FOO: u32 = 0; @@ -21,8 +21,8 @@ static FOO: u32 = 0; //~ TRANS_ITEM static statics::BAR[0] @@ statics[Internal] static BAR: u32 = 0; -//~ TRANS_ITEM fn statics::function[0] @@ statics[Internal] -fn function() { +//~ TRANS_ITEM fn statics::function[0] @@ statics[External] +pub fn function() { //~ TRANS_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] static FOO: u32 = 0; @@ -30,15 +30,15 @@ fn function() { static BAR: u32 = 0; } -mod mod1 { +pub mod mod1 { //~ TRANS_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; //~ TRANS_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] static BAR: u32 = 0; - //~ TRANS_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[Internal] - fn function() { + //~ TRANS_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] + pub fn function() { //~ TRANS_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index 74f2f84356701..302f9312b5709 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -13,6 +13,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const +// compile-flags:-Zinline-in-all-cgus // This test case makes sure, that references made through constants are // recorded properly in the InliningMap. diff --git a/src/test/codegen/abi-main-signature-16bit-c-int.rs b/src/test/codegen/abi-main-signature-16bit-c-int.rs new file mode 100644 index 0000000000000..fbe2fd10e7a14 --- /dev/null +++ b/src/test/codegen/abi-main-signature-16bit-c-int.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checks the signature of the implicitly generated native main() +// entry point. It must match C's `int main(int, char **)`. + +// This test is for targets with 16bit c_int only. +// ignore-aarch64 +// ignore-arm +// ignore-asmjs +// ignore-hexagon +// ignore-mips +// ignore-powerpc +// ignore-powerpc64 +// ignore-s390x +// ignore-sparc +// ignore-wasm32 +// ignore-x86 +// ignore-x86_64 +// ignore-xcore + +fn main() { +} + +// CHECK: define i16 @main(i16, i8**) diff --git a/src/test/codegen/abi-main-signature-32bit-c-int.rs b/src/test/codegen/abi-main-signature-32bit-c-int.rs new file mode 100644 index 0000000000000..3139749dfcb06 --- /dev/null +++ b/src/test/codegen/abi-main-signature-32bit-c-int.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checks the signature of the implicitly generated native main() +// entry point. It must match C's `int main(int, char **)`. + +// This test is for targets with 32bit c_int only. +// ignore-msp430 + +fn main() { +} + +// CHECK: define i32 @main(i32, i8**) diff --git a/src/test/codegen/abi-x86-interrupt.rs b/src/test/codegen/abi-x86-interrupt.rs index 838cd4bf6d745..e0b37cb2f3224 100644 --- a/src/test/codegen/abi-x86-interrupt.rs +++ b/src/test/codegen/abi-x86-interrupt.rs @@ -14,7 +14,6 @@ // ignore-arm // ignore-aarch64 -// min-llvm-version 3.8 // compile-flags: -C no-prepopulate-passes diff --git a/src/test/codegen/adjustments.rs b/src/test/codegen/adjustments.rs index 40603845da2b0..2b35d4547395a 100644 --- a/src/test/codegen/adjustments.rs +++ b/src/test/codegen/adjustments.rs @@ -9,13 +9,14 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] // Hack to get the correct size for the length part in slices -// CHECK: @helper([[USIZE:i[0-9]+]]) +// CHECK: @helper([[USIZE:i[0-9]+]] %arg0) #[no_mangle] -fn helper(_: usize) { +pub fn helper(_: usize) { } // CHECK-LABEL: @no_op_slice_adjustment @@ -23,9 +24,9 @@ fn helper(_: usize) { pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] { // We used to generate an extra alloca and memcpy for the block's trailing expression value, so // check that we copy directly to the return value slot -// CHECK: %2 = insertvalue { i8*, [[USIZE]] } undef, i8* %0, 0 -// CHECK: %3 = insertvalue { i8*, [[USIZE]] } %2, [[USIZE]] %1, 1 -// CHECK: ret { i8*, [[USIZE]] } %3 +// CHECK: %0 = insertvalue { [0 x i8]*, [[USIZE]] } undef, [0 x i8]* %x.0, 0 +// CHECK: %1 = insertvalue { [0 x i8]*, [[USIZE]] } %0, [[USIZE]] %x.1, 1 +// CHECK: ret { [0 x i8]*, [[USIZE]] } %1 { x } } diff --git a/src/test/codegen/align-struct.rs b/src/test/codegen/align-struct.rs index d4828be037a49..ba81e2d6046e8 100644 --- a/src/test/codegen/align-struct.rs +++ b/src/test/codegen/align-struct.rs @@ -42,7 +42,6 @@ pub fn align64(i : i32) -> Align64 { #[no_mangle] pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { // CHECK: %n64 = alloca %Nested64, align 64 -// CHECK: %a = alloca %Align64, align 64 let n64 = Nested64 { a, b, c, d }; n64 } @@ -51,7 +50,6 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { #[no_mangle] pub fn enum64(a: Align64) -> Enum64 { // CHECK: %e64 = alloca %Enum64, align 64 -// CHECK: %a = alloca %Align64, align 64 let e64 = Enum64::A(a); e64 } diff --git a/src/test/codegen/auxiliary/nounwind.rs b/src/test/codegen/auxiliary/nounwind.rs new file mode 100644 index 0000000000000..5e40e8ede1557 --- /dev/null +++ b/src/test/codegen/auxiliary/nounwind.rs @@ -0,0 +1,13 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_mangle] +pub fn bar() { +} diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index 33b4221b73338..a75b8f3992d07 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E { #[no_mangle] pub fn low_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] *&E::A(0) } @@ -62,6 +62,6 @@ pub fn low_align_const() -> E { #[no_mangle] pub fn high_align_const() -> E { // Check that low_align_const and high_align_const use the same constant -// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]] +// CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] *&E::A(0) } diff --git a/src/test/codegen/fastcall-inreg.rs b/src/test/codegen/fastcall-inreg.rs index f02e7e9f0ddcf..346c5da8d1b8d 100644 --- a/src/test/codegen/fastcall-inreg.rs +++ b/src/test/codegen/fastcall-inreg.rs @@ -59,28 +59,28 @@ #![crate_type = "lib"] -mod tests { - // CHECK: @f1(i32 inreg, i32 inreg, i32) +pub mod tests { + // CHECK: @f1(i32 inreg %arg0, i32 inreg %arg1, i32 %arg2) #[no_mangle] - extern "fastcall" fn f1(_: i32, _: i32, _: i32) {} + pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {} - // CHECK: @f2(i32* inreg, i32* inreg, i32*) + // CHECK: @f2(i32* inreg %arg0, i32* inreg %arg1, i32* %arg2) #[no_mangle] - extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {} + pub extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {} - // CHECK: @f3(float, i32 inreg, i32 inreg, i32) + // CHECK: @f3(float %arg0, i32 inreg %arg1, i32 inreg %arg2, i32 %arg3) #[no_mangle] - extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {} + pub extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {} - // CHECK: @f4(i32 inreg, float, i32 inreg, i32) + // CHECK: @f4(i32 inreg %arg0, float %arg1, i32 inreg %arg2, i32 %arg3) #[no_mangle] - extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {} + pub extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {} - // CHECK: @f5(i64, i32) + // CHECK: @f5(i64 %arg0, i32 %arg1) #[no_mangle] - extern "fastcall" fn f5(_: i64, _: i32) {} + pub extern "fastcall" fn f5(_: i64, _: i32) {} - // CHECK: @f6(i1 inreg zeroext, i32 inreg, i32) + // CHECK: @f6(i1 inreg zeroext %arg0, i32 inreg %arg1, i32 %arg2) #[no_mangle] - extern "fastcall" fn f6(_: bool, _: i32, _: i32) {} + pub extern "fastcall" fn f6(_: bool, _: i32, _: i32) {} } diff --git a/src/test/codegen/float_math.rs b/src/test/codegen/float_math.rs index bc458d45446fe..6a6d6f90b2e69 100644 --- a/src/test/codegen/float_math.rs +++ b/src/test/codegen/float_math.rs @@ -19,7 +19,7 @@ use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast}; #[no_mangle] pub fn add(x: f32, y: f32) -> f32 { // CHECK: fadd float -// CHECK-NOT fast +// CHECK-NOT: fast x + y } diff --git a/src/test/codegen/function-arguments.rs b/src/test/codegen/function-arguments.rs index d8bbcd9b7328e..f8945a6ee8d93 100644 --- a/src/test/codegen/function-arguments.rs +++ b/src/test/codegen/function-arguments.rs @@ -9,74 +9,75 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] #![feature(custom_attribute)] pub struct S { - _field: [i64; 4], + _field: [i32; 8], } pub struct UnsafeInner { _field: std::cell::UnsafeCell, } -// CHECK: zeroext i1 @boolean(i1 zeroext) +// CHECK: zeroext i1 @boolean(i1 zeroext %x) #[no_mangle] pub fn boolean(x: bool) -> bool { x } -// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4)) +// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4) %arg0) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn readonly_borrow(_: &i32) { } -// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4)) +// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4) %arg0) // static borrow may be captured #[no_mangle] pub fn static_borrow(_: &'static i32) { } -// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4)) +// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4) %arg0) // borrow with named lifetime may be captured #[no_mangle] pub fn named_borrow<'r>(_: &'r i32) { } -// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2)) +// CHECK: @unsafe_borrow(i16* dereferenceable(2) %arg0) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] pub fn unsafe_borrow(_: &UnsafeInner) { } -// CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2)) +// CHECK: @mutable_unsafe_borrow(i16* dereferenceable(2) %arg0) // ... unless this is a mutable borrow, those never alias // ... except that there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) { } -// CHECK: @mutable_borrow(i32* dereferenceable(4)) +// CHECK: @mutable_borrow(i32* dereferenceable(4) %arg0) // FIXME #25759 This should also have `nocapture` // ... there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] pub fn mutable_borrow(_: &mut i32) { } -// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32)) +// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32) %arg0) #[no_mangle] pub fn indirect_struct(_: S) { } -// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32)) +// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32) %arg0) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn borrowed_struct(_: &S) { } -// CHECK: noalias dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4)) +// CHECK: noalias align 4 dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4) %x) #[no_mangle] pub fn _box(x: Box) -> Box { x @@ -86,55 +87,55 @@ pub fn _box(x: Box) -> Box { #[no_mangle] pub fn struct_return() -> S { S { - _field: [0, 0, 0, 0] + _field: [0, 0, 0, 0, 0, 0, 0, 0] } } // Hack to get the correct size for the length part in slices -// CHECK: @helper([[USIZE:i[0-9]+]]) +// CHECK: @helper([[USIZE:i[0-9]+]] %arg0) #[no_mangle] -fn helper(_: usize) { +pub fn helper(_: usize) { } -// CHECK: @slice(i8* noalias nonnull readonly, [[USIZE]]) +// CHECK: @slice([0 x i8]* noalias nonnull readonly %arg0.0, [[USIZE]] %arg0.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -fn slice(_: &[u8]) { +pub fn slice(_: &[u8]) { } -// CHECK: @mutable_slice(i8* nonnull, [[USIZE]]) +// CHECK: @mutable_slice([0 x i8]* nonnull %arg0.0, [[USIZE]] %arg0.1) // FIXME #25759 This should also have `nocapture` // ... there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] -fn mutable_slice(_: &mut [u8]) { +pub fn mutable_slice(_: &mut [u8]) { } -// CHECK: @unsafe_slice(%UnsafeInner* nonnull, [[USIZE]]) +// CHECK: @unsafe_slice([0 x i16]* nonnull %arg0.0, [[USIZE]] %arg0.1) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] pub fn unsafe_slice(_: &[UnsafeInner]) { } -// CHECK: @str(i8* noalias nonnull readonly, [[USIZE]]) +// CHECK: @str([0 x i8]* noalias nonnull readonly %arg0.0, [[USIZE]] %arg0.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -fn str(_: &[u8]) { +pub fn str(_: &[u8]) { } -// CHECK: @trait_borrow({}* nonnull, {}* noalias nonnull readonly) +// CHECK: @trait_borrow(%"core::ops::drop::Drop"* nonnull %arg0.0, {}* noalias nonnull readonly %arg0.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -fn trait_borrow(_: &Drop) { +pub fn trait_borrow(_: &Drop) { } -// CHECK: @trait_box({}* noalias nonnull, {}* noalias nonnull readonly) +// CHECK: @trait_box(%"core::ops::drop::Drop"* noalias nonnull, {}* noalias nonnull readonly) #[no_mangle] -fn trait_box(_: Box) { +pub fn trait_box(_: Box) { } -// CHECK: { i16*, [[USIZE]] } @return_slice(i16* noalias nonnull readonly, [[USIZE]]) +// CHECK: { [0 x i16]*, [[USIZE]] } @return_slice([0 x i16]* noalias nonnull readonly %x.0, [[USIZE]] %x.1) #[no_mangle] -fn return_slice(x: &[u16]) -> &[u16] { +pub fn return_slice(x: &[u16]) -> &[u16] { x } diff --git a/src/test/codegen/issue-32031.rs b/src/test/codegen/issue-32031.rs index 5d3ccbfa4ceb0..e5ec17385455e 100644 --- a/src/test/codegen/issue-32031.rs +++ b/src/test/codegen/issue-32031.rs @@ -15,7 +15,7 @@ #[no_mangle] pub struct F32(f32); -// CHECK: define float @add_newtype_f32(float, float) +// CHECK: define float @add_newtype_f32(float %a, float %b) #[inline(never)] #[no_mangle] pub fn add_newtype_f32(a: F32, b: F32) -> F32 { @@ -25,7 +25,7 @@ pub fn add_newtype_f32(a: F32, b: F32) -> F32 { #[no_mangle] pub struct F64(f64); -// CHECK: define double @add_newtype_f64(double, double) +// CHECK: define double @add_newtype_f64(double %a, double %b) #[inline(never)] #[no_mangle] pub fn add_newtype_f64(a: F64, b: F64) -> F64 { diff --git a/src/test/codegen/issue-37945.rs b/src/test/codegen/issue-37945.rs index e7c91f309181a..df02426badcc5 100644 --- a/src/test/codegen/issue-37945.rs +++ b/src/test/codegen/issue-37945.rs @@ -13,6 +13,7 @@ // ignore-x86 // ignore-arm // ignore-emscripten +// ignore-gnux32 // ignore 32-bit platforms (LLVM has a bug with them) // See issue #37945. diff --git a/src/test/codegen/link_section.rs b/src/test/codegen/link_section.rs index 98214dc5c6f3d..1879002e7f3d7 100644 --- a/src/test/codegen/link_section.rs +++ b/src/test/codegen/link_section.rs @@ -22,12 +22,12 @@ pub enum E { B(f32) } -// CHECK: @VAR2 = constant {{.*}} { i32 0, i32 666 }, section ".test_two" +// CHECK: @VAR2 = constant {{.*}}, section ".test_two" #[no_mangle] #[link_section = ".test_two"] pub static VAR2: E = E::A(666); -// CHECK: @VAR3 = constant {{.*}} { i32 1, float 1.000000e+00 }, section ".test_three" +// CHECK: @VAR3 = constant {{.*}}, section ".test_three" #[no_mangle] #[link_section = ".test_three"] pub static VAR3: E = E::B(1.); diff --git a/src/test/codegen/mainsubprogram.rs b/src/test/codegen/mainsubprogram.rs index 657f4b662f728..f0508bc90f20c 100644 --- a/src/test/codegen/mainsubprogram.rs +++ b/src/test/codegen/mainsubprogram.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// The minimum LLVM version is set to 3.8, but really this test -// depends on a patch that is was committed to upstream LLVM before -// 4.0; and also backported to the Rust LLVM fork. +// This test depends on a patch that was committed to upstream LLVM +// before 4.0, formerly backported to the Rust LLVM fork. // ignore-tidy-linelength // ignore-windows // ignore-macos -// min-llvm-version 3.8 +// min-llvm-version 4.0 // compile-flags: -g -C no-prepopulate-passes diff --git a/src/test/codegen/mainsubprogramstart.rs b/src/test/codegen/mainsubprogramstart.rs index cd34a1670dc7d..8325318f9afc5 100644 --- a/src/test/codegen/mainsubprogramstart.rs +++ b/src/test/codegen/mainsubprogramstart.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// The minimum LLVM version is set to 3.8, but really this test -// depends on a patch that is was committed to upstream LLVM before -// 4.0; and also backported to the Rust LLVM fork. +// This test depends on a patch that was committed to upstream LLVM +// before 4.0, formerly backported to the Rust LLVM fork. // ignore-tidy-linelength // ignore-windows // ignore-macos -// min-llvm-version 3.8 +// min-llvm-version 4.0 // compile-flags: -g -C no-prepopulate-passes diff --git a/src/test/codegen/match-optimizes-away.rs b/src/test/codegen/match-optimizes-away.rs new file mode 100644 index 0000000000000..d7b779374314d --- /dev/null +++ b/src/test/codegen/match-optimizes-away.rs @@ -0,0 +1,42 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// no-system-llvm +// compile-flags: -O +#![crate_type="lib"] + +pub enum Three { A, B, C } + +pub enum Four { A, B, C, D } + +#[no_mangle] +pub fn three_valued(x: Three) -> Three { + // CHECK-LABEL: @three_valued + // CHECK-NEXT: {{^.*:$}} + // CHECK-NEXT: ret i8 %0 + match x { + Three::A => Three::A, + Three::B => Three::B, + Three::C => Three::C, + } +} + +#[no_mangle] +pub fn four_valued(x: Four) -> Four { + // CHECK-LABEL: @four_valued + // CHECK-NEXT: {{^.*:$}} + // CHECK-NEXT: ret i8 %0 + match x { + Four::A => Four::A, + Four::B => Four::B, + Four::C => Four::C, + Four::D => Four::D, + } +} diff --git a/src/test/codegen/match.rs b/src/test/codegen/match.rs index aa100da60132f..660b6346c57f1 100644 --- a/src/test/codegen/match.rs +++ b/src/test/codegen/match.rs @@ -21,12 +21,15 @@ pub enum E { #[no_mangle] pub fn exhaustive_match(e: E) { // CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ -// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[TRUE:[a-zA-Z0-9_]+]] +// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]] +// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]] // CHECK-NEXT: ] -// CHECK: [[TRUE]]: +// CHECK: [[A]]: // CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] -// CHECK: [[OTHERWISE]]: +// CHECK: [[B]]: // CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] +// CHECK: [[OTHERWISE]]: +// CHECK-NEXT: unreachable match e { E::A => (), E::B => (), diff --git a/src/test/codegen/mir_zst_stores.rs b/src/test/codegen/mir_zst_stores.rs index 36602196cefeb..884cf59c1c192 100644 --- a/src/test/codegen/mir_zst_stores.rs +++ b/src/test/codegen/mir_zst_stores.rs @@ -19,7 +19,7 @@ struct Zst { phantom: PhantomData } // CHECK-LABEL: @mir // CHECK-NOT: store{{.*}}undef #[no_mangle] -fn mir() { +pub fn mir() { let x = Zst { phantom: PhantomData }; let y = (x, 0); drop(y); diff --git a/src/test/codegen/move-val-init.rs b/src/test/codegen/move-val-init.rs index 98b7db60b68fc..e2371d6148762 100644 --- a/src/test/codegen/move-val-init.rs +++ b/src/test/codegen/move-val-init.rs @@ -24,6 +24,6 @@ pub struct Big { // CHECK-LABEL: @test_mvi #[no_mangle] pub unsafe fn test_mvi(target: *mut Big, make_big: fn() -> Big) { - // CHECK: call void %1(%Big*{{[^%]*}} %0) + // CHECK: call void %make_big(%Big*{{[^%]*}} %target) move_val_init(target, make_big()); } diff --git a/src/test/codegen/naked-functions.rs b/src/test/codegen/naked-functions.rs index 9883ca6b35d04..aab5f1bfb4f86 100644 --- a/src/test/codegen/naked-functions.rs +++ b/src/test/codegen/naked-functions.rs @@ -16,10 +16,10 @@ #![feature(naked_functions)] // CHECK: Function Attrs: naked uwtable -// CHECK-NEXT: define internal void @naked_empty() +// CHECK-NEXT: define void @naked_empty() #[no_mangle] #[naked] -fn naked_empty() { +pub fn naked_empty() { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: ret void } @@ -27,8 +27,8 @@ fn naked_empty() { // CHECK: Function Attrs: naked uwtable #[no_mangle] #[naked] -// CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}}) -fn naked_with_args(a: isize) { +// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+}}) +pub fn naked_with_args(a: isize) { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: %a = alloca i{{[0-9]+}} &a; // keep variable in an alloca @@ -36,20 +36,20 @@ fn naked_with_args(a: isize) { } // CHECK: Function Attrs: naked uwtable -// CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_return() +// CHECK-NEXT: define i{{[0-9]+}} @naked_with_return() #[no_mangle] #[naked] -fn naked_with_return() -> isize { +pub fn naked_with_return() -> isize { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: ret i{{[0-9]+}} 0 0 } // CHECK: Function Attrs: naked uwtable -// CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}}) +// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}}) #[no_mangle] #[naked] -fn naked_with_args_and_return(a: isize) -> isize { +pub fn naked_with_args_and_return(a: isize) -> isize { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: %a = alloca i{{[0-9]+}} &a; // keep variable in an alloca @@ -58,10 +58,10 @@ fn naked_with_args_and_return(a: isize) -> isize { } // CHECK: Function Attrs: naked uwtable -// CHECK-NEXT: define internal void @naked_recursive() +// CHECK-NEXT: define void @naked_recursive() #[no_mangle] #[naked] -fn naked_recursive() { +pub fn naked_recursive() { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void @naked_empty() diff --git a/src/test/codegen/nontemporal.rs b/src/test/codegen/nontemporal.rs new file mode 100644 index 0000000000000..28ec534b97a68 --- /dev/null +++ b/src/test/codegen/nontemporal.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -O + +#![feature(core_intrinsics)] +#![crate_type = "lib"] + +#[no_mangle] +pub fn a(a: &mut u32, b: u32) { + // CHECK-LABEL: define void @a + // CHECK: store i32 %b, i32* %a, align 4, !nontemporal + unsafe { + std::intrinsics::nontemporal_store(a, b); + } +} diff --git a/src/test/codegen/nounwind.rs b/src/test/codegen/nounwind.rs new file mode 100644 index 0000000000000..9fea907d3c884 --- /dev/null +++ b/src/test/codegen/nounwind.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:nounwind.rs +// compile-flags: -C no-prepopulate-passes -C panic=abort -C metadata=a +// ignore-windows + +#![crate_type = "lib"] + +extern crate nounwind; + +#[no_mangle] +pub fn foo() { + nounwind::bar(); +// CHECK: @foo() unnamed_addr #0 +// CHECK: @bar() unnamed_addr #0 +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } +} + diff --git a/src/test/codegen/packed.rs b/src/test/codegen/packed.rs index 99e6e38a3bf0b..022f581278c2f 100644 --- a/src/test/codegen/packed.rs +++ b/src/test/codegen/packed.rs @@ -54,9 +54,17 @@ pub struct PackedPair(u8, u32); // CHECK-LABEL: @pkd_pair #[no_mangle] pub fn pkd_pair(pair1: &mut PackedPair, pair2: &mut PackedPair) { - // CHECK: [[V1:%[a-z0-9]+]] = load i8, i8* %{{.*}}, align 1 - // CHECK: [[V2:%[a-z0-9]+]] = load i32, i32* %{{.*}}, align 1 - // CHECK: store i8 [[V1]], i8* {{.*}}, align 1 - // CHECK: store i32 [[V2]], i32* {{.*}}, align 1 +// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 5, i32 1, i1 false) + *pair2 = *pair1; +} + +#[repr(packed)] +#[derive(Copy, Clone)] +pub struct PackedNestedPair((u32, u32)); + +// CHECK-LABEL: @pkd_nested_pair +#[no_mangle] +pub fn pkd_nested_pair(pair1: &mut PackedNestedPair, pair2: &mut PackedNestedPair) { +// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 1, i1 false) *pair2 = *pair1; } diff --git a/src/test/codegen/panic-abort-windows.rs b/src/test/codegen/panic-abort-windows.rs index 2ab15277084ca..15688bdc2a110 100644 --- a/src/test/codegen/panic-abort-windows.rs +++ b/src/test/codegen/panic-abort-windows.rs @@ -28,7 +28,7 @@ #![crate_type = "lib"] -// CHECK: Function Attrs: uwtable +// CHECK: Function Attrs: nounwind uwtable // CHECK-NEXT: define void @normal_uwtable() #[no_mangle] pub fn normal_uwtable() { diff --git a/src/test/codegen/refs.rs b/src/test/codegen/refs.rs index 49ed2229fcd2b..6c00ffa754b06 100644 --- a/src/test/codegen/refs.rs +++ b/src/test/codegen/refs.rs @@ -9,13 +9,14 @@ // except according to those terms. // compile-flags: -C no-prepopulate-passes +// ignore-tidy-linelength #![crate_type = "lib"] // Hack to get the correct size for the length part in slices -// CHECK: @helper([[USIZE:i[0-9]+]]) +// CHECK: @helper([[USIZE:i[0-9]+]] %arg0) #[no_mangle] -fn helper(_: usize) { +pub fn helper(_: usize) { } // CHECK-LABEL: @ref_dst @@ -23,10 +24,10 @@ fn helper(_: usize) { pub fn ref_dst(s: &[u8]) { // We used to generate an extra alloca and memcpy to ref the dst, so check that we copy // directly to the alloca for "x" -// CHECK: [[X0:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 0 -// CHECK: store i8* %0, i8** [[X0]] -// CHECK: [[X1:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 1 -// CHECK: store [[USIZE]] %1, [[USIZE]]* [[X1]] +// CHECK: [[X0:%[0-9]+]] = getelementptr {{.*}} { [0 x i8]*, [[USIZE]] }* %x, i32 0, i32 0 +// CHECK: store [0 x i8]* %s.0, [0 x i8]** [[X0]] +// CHECK: [[X1:%[0-9]+]] = getelementptr {{.*}} { [0 x i8]*, [[USIZE]] }* %x, i32 0, i32 1 +// CHECK: store [[USIZE]] %s.1, [[USIZE]]* [[X1]] let x = &*s; &x; // keep variable in an alloca diff --git a/src/test/codegen/remap_path_prefix/aux_mod.rs b/src/test/codegen/remap_path_prefix/aux_mod.rs new file mode 100644 index 0000000000000..2a7019957af15 --- /dev/null +++ b/src/test/codegen/remap_path_prefix/aux_mod.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test: this is not a test + +#[inline] +pub fn some_aux_mod_function() -> i32 { + 1234 +} diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index eb00c91ba5f39..c73739bb76543 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -16,12 +16,19 @@ extern crate remap_path_prefix_aux; +// Here we check that submodules and include files are found using the path without +// remapping. This test requires that rustc is called with an absolute path. +mod aux_mod; +include!("aux_mod.rs"); + // Here we check that the expansion of the file!() macro is mapped. // CHECK: internal constant [34 x i8] c"/the/src/remap_path_prefix/main.rs" pub static FILE_PATH: &'static str = file!(); fn main() { remap_path_prefix_aux::some_aux_function(); + aux_mod::some_aux_mod_function(); + some_aux_mod_function(); } // Here we check that local debuginfo is mapped correctly. diff --git a/src/test/codegen/slice-init.rs b/src/test/codegen/slice-init.rs index 569d937c812cb..915db493fc2a4 100644 --- a/src/test/codegen/slice-init.rs +++ b/src/test/codegen/slice-init.rs @@ -15,7 +15,7 @@ // CHECK-LABEL: @zero_sized_elem #[no_mangle] pub fn zero_sized_elem() { - // CHECK-NOT: br label %slice_loop_header{{.*}} + // CHECK-NOT: br label %repeat_loop_header{{.*}} // CHECK-NOT: call void @llvm.memset.p0i8 let x = [(); 4]; drop(&x); @@ -24,7 +24,7 @@ pub fn zero_sized_elem() { // CHECK-LABEL: @zero_len_array #[no_mangle] pub fn zero_len_array() { - // CHECK-NOT: br label %slice_loop_header{{.*}} + // CHECK-NOT: br label %repeat_loop_header{{.*}} // CHECK-NOT: call void @llvm.memset.p0i8 let x = [4; 0]; drop(&x); @@ -34,7 +34,7 @@ pub fn zero_len_array() { #[no_mangle] pub fn byte_array() { // CHECK: call void @llvm.memset.p0i8.i[[WIDTH:[0-9]+]](i8* {{.*}}, i8 7, i[[WIDTH]] 4 - // CHECK-NOT: br label %slice_loop_header{{.*}} + // CHECK-NOT: br label %repeat_loop_header{{.*}} let x = [7u8; 4]; drop(&x); } @@ -50,7 +50,7 @@ enum Init { #[no_mangle] pub fn byte_enum_array() { // CHECK: call void @llvm.memset.p0i8.i[[WIDTH:[0-9]+]](i8* {{.*}}, i8 {{.*}}, i[[WIDTH]] 4 - // CHECK-NOT: br label %slice_loop_header{{.*}} + // CHECK-NOT: br label %repeat_loop_header{{.*}} let x = [Init::Memset; 4]; drop(&x); } @@ -59,7 +59,7 @@ pub fn byte_enum_array() { #[no_mangle] pub fn zeroed_integer_array() { // CHECK: call void @llvm.memset.p0i8.i[[WIDTH:[0-9]+]](i8* {{.*}}, i8 0, i[[WIDTH]] 16 - // CHECK-NOT: br label %slice_loop_header{{.*}} + // CHECK-NOT: br label %repeat_loop_header{{.*}} let x = [0u32; 4]; drop(&x); } @@ -67,7 +67,7 @@ pub fn zeroed_integer_array() { // CHECK-LABEL: @nonzero_integer_array #[no_mangle] pub fn nonzero_integer_array() { - // CHECK: br label %slice_loop_header{{.*}} + // CHECK: br label %repeat_loop_header{{.*}} // CHECK-NOT: call void @llvm.memset.p0i8 let x = [0x1a_2b_3c_4d_u32; 4]; drop(&x); diff --git a/src/test/codegen/stores.rs b/src/test/codegen/stores.rs index 6135f49eb711b..08f5038fb186e 100644 --- a/src/test/codegen/stores.rs +++ b/src/test/codegen/stores.rs @@ -25,9 +25,9 @@ pub struct Bytes { #[no_mangle] pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) { // CHECK: [[TMP:%.+]] = alloca i32 -// CHECK: %arg1 = alloca [4 x i8] -// CHECK: store i32 %1, i32* [[TMP]] -// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %arg1 to i8* +// CHECK: %y = alloca [4 x i8] +// CHECK: store i32 %0, i32* [[TMP]] +// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %y to i8* // CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8* // CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false) *x = y; @@ -39,9 +39,9 @@ pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) { #[no_mangle] pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) { // CHECK: [[TMP:%.+]] = alloca i32 -// CHECK: %arg1 = alloca %Bytes -// CHECK: store i32 %1, i32* [[TMP]] -// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %arg1 to i8* +// CHECK: %y = alloca %Bytes +// CHECK: store i32 %0, i32* [[TMP]] +// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %y to i8* // CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8* // CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false) *x = y; diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs new file mode 100644 index 0000000000000..c2fc296617098 --- /dev/null +++ b/src/test/codegen/unchecked-float-casts.rs @@ -0,0 +1,46 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C no-prepopulate-passes + +// This file tests that we don't generate any code for saturation if +// -Z saturating-float-casts is not enabled. + +#![crate_type = "lib"] +#![feature(i128_type)] + +// CHECK-LABEL: @f32_to_u32 +#[no_mangle] +pub fn f32_to_u32(x: f32) -> u32 { + // CHECK: fptoui + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as u32 +} + +// CHECK-LABEL: @f32_to_i32 +#[no_mangle] +pub fn f32_to_i32(x: f32) -> i32 { + // CHECK: fptosi + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as i32 +} + +#[no_mangle] +pub fn f64_to_u16(x: f64) -> u16 { + // CHECK: fptoui + // CHECK-NOT: fcmp + // CHECK-NOT: icmp + // CHECK-NOT: select + x as u16 +} diff --git a/src/test/codegen/vtabletype.rs b/src/test/codegen/vtabletype.rs new file mode 100644 index 0000000000000..b646646754849 --- /dev/null +++ b/src/test/codegen/vtabletype.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test depends on a patch that was committed to upstream LLVM +// after 5.0, then backported to the Rust LLVM fork. + +// ignore-tidy-linelength +// ignore-windows +// ignore-macos +// min-system-llvm-version 5.1 + +// compile-flags: -g -C no-prepopulate-passes + +// CHECK-LABEL: @main +// CHECK: {{.*}}DICompositeType{{.*}}name: "vtable",{{.*}}vtableHolder:{{.*}} + +pub trait T { +} + +impl T for f64 { +} + +pub fn main() { + let d = 23.0f64; + let td = &d as &T; +} diff --git a/src/test/codegen/x86_mmx.rs b/src/test/codegen/x86_mmx.rs new file mode 100644 index 0000000000000..bedda63bbff31 --- /dev/null +++ b/src/test/codegen/x86_mmx.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-arm +// ignore-aarch64 +// ignore-emscripten +// compile-flags: -O + +#![feature(repr_simd)] +#![crate_type="lib"] + +#[repr(simd)] +#[derive(Clone, Copy)] +pub struct i8x8(u64); + +#[no_mangle] +pub fn a(a: &mut i8x8, b: i8x8) -> i8x8 { + // CHECK-LABEL: define x86_mmx @a(x86_mmx*{{.*}}, x86_mmx{{.*}}) + // CHECK: store x86_mmx %b, x86_mmx* %a + // CHECK: ret x86_mmx %b + *a = b; + return b +} diff --git a/src/test/compile-fail-fulldeps/auxiliary/lint_for_crate.rs b/src/test/compile-fail-fulldeps/auxiliary/lint_for_crate.rs index fc53031e7f226..d3f921e0878ae 100644 --- a/src/test/compile-fail-fulldeps/auxiliary/lint_for_crate.rs +++ b/src/test/compile-fail-fulldeps/auxiliary/lint_for_crate.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar, rustc_private)] #![feature(box_syntax)] +#![feature(macro_vis_matcher)] #[macro_use] extern crate rustc; extern crate rustc_plugin; diff --git a/src/test/compile-fail-fulldeps/auxiliary/lint_group_plugin_test.rs b/src/test/compile-fail-fulldeps/auxiliary/lint_group_plugin_test.rs index 490aa0d469312..a0c72243d4821 100644 --- a/src/test/compile-fail-fulldeps/auxiliary/lint_group_plugin_test.rs +++ b/src/test/compile-fail-fulldeps/auxiliary/lint_group_plugin_test.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar)] #![feature(box_syntax, rustc_private)] +#![feature(macro_vis_matcher)] // Load rustc as a plugin to get macros #[macro_use] diff --git a/src/test/compile-fail-fulldeps/auxiliary/lint_plugin_test.rs b/src/test/compile-fail-fulldeps/auxiliary/lint_plugin_test.rs index 8647797270f9a..cbbfbd8060360 100644 --- a/src/test/compile-fail-fulldeps/auxiliary/lint_plugin_test.rs +++ b/src/test/compile-fail-fulldeps/auxiliary/lint_plugin_test.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar)] #![feature(box_syntax, rustc_private)] +#![feature(macro_vis_matcher)] extern crate syntax; diff --git a/src/test/compile-fail-fulldeps/dropck_tarena_cycle_checked.rs b/src/test/compile-fail-fulldeps/dropck_tarena_cycle_checked.rs index fa85432fb8e3f..bc88ff9244c99 100644 --- a/src/test/compile-fail-fulldeps/dropck_tarena_cycle_checked.rs +++ b/src/test/compile-fail-fulldeps/dropck_tarena_cycle_checked.rs @@ -17,7 +17,6 @@ // for the error message we see here.) #![feature(rustc_private)] -#![feature(const_atomic_usize_new)] extern crate arena; diff --git a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs index b03409c9c285e..93790f5937298 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs @@ -17,7 +17,8 @@ extern crate derive_bad; #[derive( A )] -//~^^ ERROR: proc-macro derive produced unparseable tokens +//~^^ ERROR proc-macro derive produced unparseable tokens +//~| ERROR expected `:`, found `}` struct A; fn main() {} diff --git a/src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs b/src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs index b1fb7d42d8683..773b16b945f07 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs @@ -23,5 +23,5 @@ fn main() { bang_proc_macro2!(); //~^ ERROR cannot find value `foobar2` in this scope //~^^ did you mean `foobar`? - println!("{}", x); + println!("{}", x); //~ ERROR cannot find value `x` in this scope } diff --git a/src/test/compile-fail/E0017.rs b/src/test/compile-fail/E0017.rs index c6bec6090f242..726a6f8c6feb4 100644 --- a/src/test/compile-fail/E0017.rs +++ b/src/test/compile-fail/E0017.rs @@ -13,15 +13,9 @@ const C: i32 = 2; const CR: &'static mut i32 = &mut C; //~ ERROR E0017 //~| NOTE constants require immutable values - //~| ERROR E0017 - //~| NOTE constants require immutable values static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017 - //~| NOTE statics require immutable values - //~| ERROR E0017 //~| NOTE statics require immutable values //~| ERROR cannot borrow static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017 //~| NOTE statics require immutable values - //~| ERROR E0017 - //~| NOTE statics require immutable values fn main() {} diff --git a/src/test/compile-fail/E0029.rs b/src/test/compile-fail/E0029.rs index ec84e2a3f8a36..e43290bb15416 100644 --- a/src/test/compile-fail/E0029.rs +++ b/src/test/compile-fail/E0029.rs @@ -17,6 +17,7 @@ fn main() { //~| NOTE ranges require char or numeric types //~| NOTE start type: &'static str //~| NOTE end type: &'static str + //~| ERROR non-reference pattern used to match a reference _ => {} } } diff --git a/src/test/compile-fail/E0080.rs b/src/test/compile-fail/E0080.rs index 0329209d44bc7..2f199c48e46e7 100644 --- a/src/test/compile-fail/E0080.rs +++ b/src/test/compile-fail/E0080.rs @@ -10,7 +10,9 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 + //~| WARNING shift left with overflow Y = (1 / 0) //~ ERROR E0080 + //~| WARNING divide by zero } fn main() { diff --git a/src/test/compile-fail/E0084.rs b/src/test/compile-fail/E0084.rs index c7c5662f1feda..d19eed7124e82 100644 --- a/src/test/compile-fail/E0084.rs +++ b/src/test/compile-fail/E0084.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[repr(i32)] -enum Foo {} -//~^ ERROR E0084 -//~| unsupported enum representation +#[repr(i32)] //~ ERROR: E0084 +enum Foo {} //~ NOTE: zero-variant enum fn main() { } diff --git a/src/test/compile-fail/E0225.rs b/src/test/compile-fail/E0225.rs index 8c79c15e3de5b..c2f610ecd2816 100644 --- a/src/test/compile-fail/E0225.rs +++ b/src/test/compile-fail/E0225.rs @@ -10,6 +10,6 @@ fn main() { let _: Box; - //~^ ERROR only Send/Sync traits can be used as additional traits in a trait object [E0225] - //~| NOTE non-Send/Sync additional trait + //~^ ERROR only auto traits can be used as additional traits in a trait object [E0225] + //~| NOTE non-auto additional trait } diff --git a/src/test/compile-fail/E0259.rs b/src/test/compile-fail/E0259.rs index c285c4d9e00c1..e125cc0c19c37 100644 --- a/src/test/compile-fail/E0259.rs +++ b/src/test/compile-fail/E0259.rs @@ -18,5 +18,6 @@ extern crate libc as alloc; //~^ ERROR E0259 //~| NOTE `alloc` reimported here //~| NOTE `alloc` must be defined only once in the type namespace of this module +//~| NOTE You can use `as` to change the binding name of the import fn main() {} diff --git a/src/test/compile-fail/E0388.rs b/src/test/compile-fail/E0388.rs index 2c88039d373e5..c002badfef64c 100644 --- a/src/test/compile-fail/E0388.rs +++ b/src/test/compile-fail/E0388.rs @@ -12,11 +12,8 @@ static X: i32 = 1; const C: i32 = 2; const CR: &'static mut i32 = &mut C; //~ ERROR E0017 - //~| ERROR E0017 static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017 - //~| ERROR E0017 //~| ERROR cannot borrow static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017 - //~| ERROR E0017 fn main() {} diff --git a/src/test/compile-fail/E0506.rs b/src/test/compile-fail/E0506.rs index b2cf66849c759..c4a7f257394e7 100644 --- a/src/test/compile-fail/E0506.rs +++ b/src/test/compile-fail/E0506.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir struct FancyNum { num: u8, @@ -19,8 +19,7 @@ fn main() { let mut fancy_num = FancyNum { num: 5 }; let fancy_ref = &fancy_num; fancy_num = FancyNum { num: 6 }; //[ast]~ ERROR E0506 - //[mir]~^ ERROR (Mir) [E0506] - //[mir]~| ERROR (Ast) [E0506] + //[mir]~^ ERROR [E0506] println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num); } diff --git a/src/test/compile-fail/E0508.rs b/src/test/compile-fail/E0508.rs index a72c29cc3a59e..0c3dce6b0346a 100644 --- a/src/test/compile-fail/E0508.rs +++ b/src/test/compile-fail/E0508.rs @@ -8,9 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + struct NonCopy; fn main() { let array = [NonCopy; 1]; - let _value = array[0]; //~ ERROR E0508 + let _value = array[0]; //[ast]~ ERROR [E0508] + //[mir]~^ ERROR [E0508] } diff --git a/src/test/compile-fail/E0517.rs b/src/test/compile-fail/E0517.rs index b79cb2c44af39..7feda670f52a3 100644 --- a/src/test/compile-fail/E0517.rs +++ b/src/test/compile-fail/E0517.rs @@ -8,21 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[repr(C)] //~ ERROR E0517 - //~| requires a struct, enum or union -type Foo = u8; +#[repr(C)] //~ ERROR: E0517 +type Foo = u8; //~ NOTE: not a struct, enum or union -#[repr(packed)] //~ ERROR E0517 - //~| requires a struct -enum Foo2 {Bar, Baz} +#[repr(packed)] //~ ERROR: E0517 +enum Foo2 {Bar, Baz} //~ NOTE: not a struct -#[repr(u8)] //~ ERROR E0517 - //~| requires an enum -struct Foo3 {bar: bool, baz: bool} +#[repr(u8)] //~ ERROR: E0517 +struct Foo3 {bar: bool, baz: bool} //~ NOTE: not an enum -#[repr(C)] //~ ERROR E0517 - //~| requires a struct, enum or union -impl Foo3 { +#[repr(C)] //~ ERROR: E0517 +impl Foo3 { //~ NOTE: not a struct, enum or union } fn main() { diff --git a/src/test/compile-fail/E0518.rs b/src/test/compile-fail/E0518.rs index f9494e0bcb531..63d40db0049da 100644 --- a/src/test/compile-fail/E0518.rs +++ b/src/test/compile-fail/E0518.rs @@ -8,13 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[inline(always)] //~ ERROR E0518 - //~| requires a function -struct Foo; +#[inline(always)] //~ ERROR: E0518 +struct Foo; //~ NOTE: not a function -#[inline(never)] //~ ERROR E0518 - //~| requires a function -impl Foo { +#[inline(never)] //~ ERROR: E0518 +impl Foo { //~ NOTE: not a function } fn main() { diff --git a/src/test/compile-fail/E0534.rs b/src/test/compile-fail/E0534.rs index 8c036e6076d1d..fc465b268691e 100644 --- a/src/test/compile-fail/E0534.rs +++ b/src/test/compile-fail/E0534.rs @@ -11,4 +11,6 @@ #[inline()] //~ ERROR E0534 pub fn something() {} -fn main() {} +fn main() { + something(); +} diff --git a/src/test/compile-fail/E0594.rs b/src/test/compile-fail/E0594.rs new file mode 100644 index 0000000000000..f3fbc3b8b54db --- /dev/null +++ b/src/test/compile-fail/E0594.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +static NUM: i32 = 18; + +fn main() { + NUM = 20; //[ast]~ ERROR E0594 + //[mir]~^ ERROR cannot assign to immutable item `NUM` +} diff --git a/src/test/compile-fail/E0596.rs b/src/test/compile-fail/E0596.rs index 1f1af69d97768..52bdff55d86a3 100644 --- a/src/test/compile-fail/E0596.rs +++ b/src/test/compile-fail/E0596.rs @@ -8,7 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + fn main() { let x = 1; - let y = &mut x; //~ ERROR E0596 + let y = &mut x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] } diff --git a/src/test/compile-fail/E0657.rs b/src/test/compile-fail/E0657.rs new file mode 100644 index 0000000000000..b72a8f03089b2 --- /dev/null +++ b/src/test/compile-fail/E0657.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(warnings)] +#![feature(conservative_impl_trait)] + +trait Id {} +trait Lt<'a> {} + +impl<'a> Lt<'a> for () {} +impl Id for T {} + +fn free_fn_capture_hrtb_in_impl_trait() + -> impl for<'a> Id> + //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657] +{ + () +} + +struct Foo; +impl Foo { + fn impl_fn_capture_hrtb_in_impl_trait() + -> impl for<'a> Id> + //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level + { + () + } +} + +fn main() {} diff --git a/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs b/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs new file mode 100644 index 0000000000000..8e5ba489c565e --- /dev/null +++ b/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(use_nested_groups)] +#![allow(unused_imports)] + +mod foo {} + +use foo::{ + ::bar, //~ ERROR crate root in paths can only be used in start position + super::bar, //~ ERROR `super` in paths can only be used in start position + self::bar, //~ ERROR `self` in paths can only be used in start position +}; + +fn main() {} diff --git a/src/test/compile-fail/arbitrary-self-types-not-object-safe.rs b/src/test/compile-fail/arbitrary-self-types-not-object-safe.rs new file mode 100644 index 0000000000000..6b10739bd8e59 --- /dev/null +++ b/src/test/compile-fail/arbitrary-self-types-not-object-safe.rs @@ -0,0 +1,55 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(arbitrary_self_types)] + +use std::rc::Rc; + +trait Foo { + fn foo(self: Rc) -> usize; +} + +trait Bar { + fn foo(self: Rc) -> usize where Self: Sized; + fn bar(self: Box) -> usize; +} + +impl Foo for usize { + fn foo(self: Rc) -> usize { + *self + } +} + +impl Bar for usize { + fn foo(self: Rc) -> usize { + *self + } + + fn bar(self: Box) -> usize { + *self + } +} + +fn make_foo() { + let x = Box::new(5usize) as Box; + //~^ ERROR E0038 + //~| NOTE method `foo` has a non-standard `self` type + //~| NOTE the trait `Foo` cannot be made into an object + //~| ERROR E0038 + //~| NOTE method `foo` has a non-standard `self` type + //~| NOTE the trait `Foo` cannot be made into an object + //~| NOTE requirements on the impl of `std::ops::CoerceUnsized>` +} + +fn make_bar() { + let x = Box::new(5usize) as Box; + x.bar(); +} + +fn main() {} diff --git a/src/test/compile-fail/asm-out-assign-imm.rs b/src/test/compile-fail/asm-out-assign-imm.rs index 546d402252e27..f2629fa52ffdd 100644 --- a/src/test/compile-fail/asm-out-assign-imm.rs +++ b/src/test/compile-fail/asm-out-assign-imm.rs @@ -27,8 +27,8 @@ pub fn main() { foo(x); unsafe { asm!("mov $1, $0" : "=r"(x) : "r"(5)); - //~^ ERROR re-assignment of immutable variable `x` - //~| NOTE re-assignment of immutable + //~^ ERROR cannot assign twice to immutable variable `x` + //~| NOTE cannot assign twice to immutable } foo(x); } diff --git a/src/test/compile-fail/assign-imm-local-twice.rs b/src/test/compile-fail/assign-imm-local-twice.rs index 9a5d6289b589e..d5e412c3745e0 100644 --- a/src/test/compile-fail/assign-imm-local-twice.rs +++ b/src/test/compile-fail/assign-imm-local-twice.rs @@ -8,12 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + fn test() { let v: isize; - v = 1; //~ NOTE first assignment + v = 1; //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment println!("v={}", v); - v = 2; //~ ERROR re-assignment of immutable variable - //~| NOTE re-assignment of immutable + v = 2; //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `v` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable println!("v={}", v); } diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs index 285a77d6b657a..5451a20d8166f 100644 --- a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs +++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs @@ -30,7 +30,7 @@ fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( { // x and y here have two distinct lifetimes: let z: I::A = if cond { x } else { y }; - //~^ ERROR cannot infer + //~^ ERROR lifetime mismatch } pub fn main() {} diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs index 0e822aff01e87..a5e8f4068e661 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs @@ -50,9 +50,10 @@ fn baz<'a,'b>(x: &'a u32) -> &'static u32 { #[cfg(krisskross)] // two instantiations, mixing and matching: BAD fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { - let a = bar(foo, y); //[krisskross]~ ERROR E0495 - let b = bar(foo, x); //[krisskross]~ ERROR E0495 - (a, b) + let a = bar(foo, y); + let b = bar(foo, x); + (a, b) //[krisskross]~ ERROR 55:5: 55:6: lifetime mismatch [E0623] + //[krisskross]~^ ERROR 55:8: 55:9: lifetime mismatch [E0623] } #[rustc_error] diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs index 10fe612980d34..6e4bdd4b21c79 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs @@ -45,8 +45,8 @@ fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { #[cfg(oneuse)] // one instantiation: BAD fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { let f = foo; // <-- No consistent type can be inferred for `f` here. - let a = bar(f, x); //[oneuse]~^ ERROR E0495 - let b = bar(f, y); + let a = bar(f, x); + let b = bar(f, y); //[oneuse]~ ERROR 49:19: 49:20: lifetime mismatch [E0623] (a, b) } @@ -60,9 +60,9 @@ fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { #[cfg(krisskross)] // two instantiations, mixing and matching: BAD fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - let a = bar(foo, y); //[krisskross]~ ERROR E0495 - let b = bar(foo, x); //[krisskross]~ ERROR E0495 - (a, b) + let a = bar(foo, y); //[krisskross]~ ERROR E0623 + let b = bar(foo, x); + (a, b) //[krisskross]~ ERROR E0623 } #[rustc_error] diff --git a/src/test/compile-fail/auto-impl-future-compat.rs b/src/test/compile-fail/auto-impl-future-compat.rs new file mode 100644 index 0000000000000..5c32a75639880 --- /dev/null +++ b/src/test/compile-fail/auto-impl-future-compat.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(optin_builtin_traits)] + +trait Foo {} +impl Foo for .. {} +//~^ ERROR The form `impl Foo for .. {}` will be removed, please use `auto trait Foo {}` +//~^^ WARN this was previously accepted by the compiler diff --git a/src/test/compile-fail/auto-trait-validation.rs b/src/test/compile-fail/auto-trait-validation.rs new file mode 100644 index 0000000000000..b28b776d9c2a6 --- /dev/null +++ b/src/test/compile-fail/auto-trait-validation.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(optin_builtin_traits)] + +auto trait Generic {} +//~^ ERROR auto traits cannot have generics +//~^^ traits with auto impls (`e.g. impl Trait for ..`) can not have type parameters +auto trait Bound : Copy {} +//~^ ERROR auto traits cannot have super traits +//~^^ traits with auto impls (`e.g. impl Trait for ..`) cannot have predicates +auto trait MyTrait { fn foo() {} } +//~^ ERROR auto traits cannot contain items +//~^^ traits with default impls (`e.g. impl Trait for ..`) must have no methods or associated items +fn main() {} diff --git a/src/test/compile-fail/auxiliary/deprecation-lint.rs b/src/test/compile-fail/auxiliary/deprecation-lint.rs index ff872efb7bdb1..175102898759b 100644 --- a/src/test/compile-fail/auxiliary/deprecation-lint.rs +++ b/src/test/compile-fail/auxiliary/deprecation-lint.rs @@ -52,6 +52,24 @@ pub enum Enum { #[deprecated(since = "1.0.0", note = "text")] pub struct DeprecatedTupleStruct(pub isize); +pub mod nested { + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedStruct { + pub i: isize + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedUnitStruct; + + pub enum Enum { + #[deprecated(since = "1.0.0", note = "text")] + DeprecatedVariant, + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedTupleStruct(pub isize); +} + pub struct Stable { #[deprecated(since = "1.0.0", note = "text")] pub override2: u8, diff --git a/src/test/compile-fail/auxiliary/issue_5844_aux.rs b/src/test/compile-fail/auxiliary/issue_5844_aux.rs index 5c878b1e667d9..7fa937e93b34b 100644 --- a/src/test/compile-fail/auxiliary/issue_5844_aux.rs +++ b/src/test/compile-fail/auxiliary/issue_5844_aux.rs @@ -8,10 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(libc)] - -extern crate libc; - extern "C" { - pub fn rand() -> libc::c_int; + pub fn rand() -> u32; } diff --git a/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs b/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs index 2e425ac96c55f..1e1c55de87e17 100644 --- a/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs +++ b/src/test/compile-fail/auxiliary/tdticc_coherence_lib.rs @@ -12,6 +12,7 @@ #![crate_type = "rlib"] pub trait DefaultedTrait { } +#[allow(auto_impl)] impl DefaultedTrait for .. { } pub struct Something { t: T } diff --git a/src/test/compile-fail/bad-intrinsic-monomorphization.rs b/src/test/compile-fail/bad-intrinsic-monomorphization.rs index cfb64f8076767..2fe94d43acdd1 100644 --- a/src/test/compile-fail/bad-intrinsic-monomorphization.rs +++ b/src/test/compile-fail/bad-intrinsic-monomorphization.rs @@ -10,6 +10,7 @@ #![feature(repr_simd, platform_intrinsics, core_intrinsics)] #![allow(warnings)] +#![crate_type = "rlib"] // Bad monomorphizations could previously cause LLVM asserts even though the // error was caught in the compiler. @@ -21,21 +22,19 @@ extern "platform-intrinsic" { use std::intrinsics; #[derive(Copy, Clone)] -struct Foo(i64); +pub struct Foo(i64); -unsafe fn test_cttz(v: Foo) -> Foo { +pub unsafe fn test_cttz(v: Foo) -> Foo { intrinsics::cttz(v) //~^ ERROR `cttz` intrinsic: expected basic integer type, found `Foo` } -unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo { +pub unsafe fn test_fadd_fast(a: Foo, b: Foo) -> Foo { intrinsics::fadd_fast(a, b) //~^ ERROR `fadd_fast` intrinsic: expected basic float type, found `Foo` } -unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo { +pub unsafe fn test_simd_add(a: Foo, b: Foo) -> Foo { simd_add(a, b) //~^ ERROR `simd_add` intrinsic: expected SIMD input type, found non-SIMD `Foo` } - -fn main() {} diff --git a/src/test/compile-fail/bad-sized.rs b/src/test/compile-fail/bad-sized.rs index a2e2e5caafe6d..df3da5096bf53 100644 --- a/src/test/compile-fail/bad-sized.rs +++ b/src/test/compile-fail/bad-sized.rs @@ -12,7 +12,7 @@ trait Trait {} pub fn main() { let x: Vec = Vec::new(); - //~^ ERROR only Send/Sync traits can be used as additional traits in a trait object + //~^ ERROR only auto traits can be used as additional traits in a trait object //~| ERROR the trait bound `Trait: std::marker::Sized` is not satisfied //~| ERROR the trait bound `Trait: std::marker::Sized` is not satisfied } diff --git a/src/test/compile-fail/blind-item-block-middle.rs b/src/test/compile-fail/blind-item-block-middle.rs index 0db7eaf0ca7c3..a501a5cd3ec4c 100644 --- a/src/test/compile-fail/blind-item-block-middle.rs +++ b/src/test/compile-fail/blind-item-block-middle.rs @@ -12,6 +12,6 @@ mod foo { pub struct bar; } fn main() { let bar = 5; - //~^ ERROR let bindings cannot shadow unit structs + //~^ ERROR mismatched types use foo::bar; } diff --git a/src/test/compile-fail/bogus-tag.rs b/src/test/compile-fail/bogus-tag.rs index c388d47da6e51..a629f76d8b3e9 100644 --- a/src/test/compile-fail/bogus-tag.rs +++ b/src/test/compile-fail/bogus-tag.rs @@ -10,11 +10,14 @@ enum color { rgb(isize, isize, isize), rgba(isize, isize, isize, isize), } +//~^ NOTE variant `hsl` not found here fn main() { let red: color = color::rgb(255, 0, 0); match red { color::rgb(r, g, b) => { println!("rgb"); } - color::hsl(h, s, l) => { println!("hsl"); } //~ ERROR no function + color::hsl(h, s, l) => { println!("hsl"); } + //~^ ERROR no variant + //~| NOTE variant not found in `color` } } diff --git a/src/test/compile-fail/borrowck/borrowck-access-permissions.rs b/src/test/compile-fail/borrowck/borrowck-access-permissions.rs new file mode 100644 index 0000000000000..00a3da860746d --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-access-permissions.rs @@ -0,0 +1,70 @@ +// 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. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +static static_x : i32 = 1; +static mut static_x_mut : i32 = 1; + +fn main() { + let x = 1; + let mut x_mut = 1; + + { // borrow of local + let _y1 = &mut x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] + let _y2 = &mut x_mut; // No error + } + + { // borrow of static + let _y1 = &mut static_x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] + unsafe { let _y2 = &mut static_x_mut; } // No error + } + + { // borrow of deref to box + let box_x = Box::new(1); + let mut box_x_mut = Box::new(1); + + let _y1 = &mut *box_x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] + let _y2 = &mut *box_x_mut; // No error + } + + { // borrow of deref to reference + let ref_x = &x; + let ref_x_mut = &mut x_mut; + + let _y1 = &mut *ref_x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] + let _y2 = &mut *ref_x_mut; // No error + } + + { // borrow of deref to pointer + let ptr_x : *const _ = &x; + let ptr_mut_x : *mut _ = &mut x_mut; + + unsafe { + let _y1 = &mut *ptr_x; //[ast]~ ERROR [E0596] + //[mir]~^ ERROR [E0596] + let _y2 = &mut *ptr_mut_x; // No error + } + } + + { // borrowing mutably through an immutable reference + struct Foo<'a> { f: &'a mut i32, g: &'a i32 }; + let mut foo = Foo { f: &mut x_mut, g: &x }; + let foo_ref = &foo; + let _y = &mut *foo_ref.f; //[ast]~ ERROR [E0389] + //[mir]~^ ERROR [E0596] + // FIXME: Wrong error in MIR + } +} diff --git a/src/test/compile-fail/borrowck/borrowck-assign-comp.rs b/src/test/compile-fail/borrowck/borrowck-assign-comp.rs index e63de3a3bed79..d68420eb205c7 100644 --- a/src/test/compile-fail/borrowck/borrowck-assign-comp.rs +++ b/src/test/compile-fail/borrowck/borrowck-assign-comp.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir struct point { x: isize, y: isize } @@ -21,8 +21,7 @@ fn a() { // inherently mutable; since `p` was made immutable, `p.x` is now // immutable. Otherwise the type of &_q.x (&isize) would be wrong. p.x = 5; //[ast]~ ERROR cannot assign to `p.x` - //[mir]~^ ERROR cannot assign to `p.x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `p.0` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `p.x` because it is borrowed q.x; } @@ -33,8 +32,7 @@ fn c() { let mut p = point {x: 3, y: 4}; let q = &p.y; p = point {x: 5, y: 7};//[ast]~ ERROR cannot assign to `p` - //[mir]~^ ERROR cannot assign to `p` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `p` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `p` because it is borrowed p.x; // silence warning *q; // stretch loan } @@ -46,8 +44,7 @@ fn d() { let mut p = point {x: 3, y: 4}; let q = &p.y; p.y = 5; //[ast]~ ERROR cannot assign to `p.y` - //[mir]~^ ERROR cannot assign to `p.y` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `p.1` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `p.y` because it is borrowed *q; } diff --git a/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs b/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs index 1b5b1899e0d9d..76a670af3531c 100644 --- a/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs +++ b/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs @@ -8,9 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + static foo: isize = 5; fn main() { // assigning to various global constants - foo = 6; //~ ERROR cannot assign to immutable static item + foo = 6; //[ast]~ ERROR cannot assign to immutable static item + //[mir]~^ ERROR cannot assign to immutable item `foo` } diff --git a/src/test/compile-fail/borrowck/borrowck-closures-mut-and-imm.rs b/src/test/compile-fail/borrowck/borrowck-closures-mut-and-imm.rs index 6c003ec2d48b3..f498d8d500e64 100644 --- a/src/test/compile-fail/borrowck/borrowck-closures-mut-and-imm.rs +++ b/src/test/compile-fail/borrowck/borrowck-closures-mut-and-imm.rs @@ -13,7 +13,7 @@ // ignore-tidy-linelength // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir #![feature(box_syntax)] @@ -29,48 +29,42 @@ fn a() { let mut x = 3; let c1 = || x = 4; let c2 = || x * 5; //[ast]~ ERROR cannot borrow `x` - //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Ast) - //[mir]~| ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Mir) + //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable } fn b() { let mut x = 3; let c1 = || set(&mut x); let c2 = || get(&x); //[ast]~ ERROR cannot borrow `x` - //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Ast) - //[mir]~| ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Mir) + //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable } fn c() { let mut x = 3; let c1 = || set(&mut x); let c2 = || x * 5; //[ast]~ ERROR cannot borrow `x` - //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Ast) - //[mir]~| ERROR cannot borrow `x` as immutable because it is also borrowed as mutable (Mir) + //[mir]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable } fn d() { let mut x = 3; let c2 = || x * 5; x = 5; //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `x` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `x` because it is borrowed } fn e() { let mut x = 3; let c1 = || get(&x); x = 5; //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `x` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `x` because it is borrowed } fn f() { let mut x: Box<_> = box 3; let c1 = || get(&*x); - *x = 5; //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `*x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `(*x)` because it is borrowed (Mir) + *x = 5; //[ast]~ ERROR cannot assign to `*x` + //[mir]~^ ERROR cannot assign to `*x` because it is borrowed } fn g() { @@ -81,8 +75,7 @@ fn g() { let mut x: Box<_> = box Foo { f: box 3 }; let c1 = || get(&*x.f); *x.f = 5; //[ast]~ ERROR cannot assign to `*x.f` - //[mir]~^ ERROR cannot assign to `*x.f` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `(*(*x).0)` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `*x.f` because it is borrowed } fn h() { @@ -93,8 +86,7 @@ fn h() { let mut x: Box<_> = box Foo { f: box 3 }; let c1 = || get(&*x.f); let c2 = || *x.f = 5; //[ast]~ ERROR cannot borrow `x` as mutable - //[mir]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable (Ast) - //[mir]~| ERROR cannot borrow `x` as mutable because it is also borrowed as immutable (Mir) + //[mir]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable } fn main() { diff --git a/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs b/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs new file mode 100644 index 0000000000000..062cc976a3dc1 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs @@ -0,0 +1,305 @@ +// 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. + +// ignore-tidy-linelength +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(slice_patterns)] +#![feature(advanced_slice_patterns)] + +pub struct Foo { + x: u32 +} + +pub struct Bar(u32); + +pub enum Baz { + X(u32) +} + +union U { + a: u8, + b: u64, +} + +impl Foo { + fn x(&mut self) -> &mut u32 { &mut self.x } +} + +impl Bar { + fn x(&mut self) -> &mut u32 { &mut self.0 } +} + +impl Baz { + fn x(&mut self) -> &mut u32 { + match *self { + Baz::X(ref mut value) => value + } + } +} + +fn main() { + // Local and field from struct + { + let mut f = Foo { x: 22 }; + let _x = f.x(); + f.x; //[ast]~ ERROR cannot use `f.x` because it was mutably borrowed + //[mir]~^ ERROR cannot use `f.x` because it was mutably borrowed + } + // Local and field from tuple-struct + { + let mut g = Bar(22); + let _0 = g.x(); + g.0; //[ast]~ ERROR cannot use `g.0` because it was mutably borrowed + //[mir]~^ ERROR cannot use `g.0` because it was mutably borrowed + } + // Local and field from tuple + { + let mut h = (22, 23); + let _0 = &mut h.0; + h.0; //[ast]~ ERROR cannot use `h.0` because it was mutably borrowed + //[mir]~^ ERROR cannot use `h.0` because it was mutably borrowed + } + // Local and field from enum + { + let mut e = Baz::X(2); + let _e0 = e.x(); + match e { + Baz::X(value) => value + //[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed + }; + } + // Local and field from union + unsafe { + let mut u = U { b: 0 }; + let _ra = &mut u.a; + u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed + //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed + } + // Deref and field from struct + { + let mut f = Box::new(Foo { x: 22 }); + let _x = f.x(); + f.x; //[ast]~ ERROR cannot use `f.x` because it was mutably borrowed + //[mir]~^ ERROR cannot use `f.x` because it was mutably borrowed + } + // Deref and field from tuple-struct + { + let mut g = Box::new(Bar(22)); + let _0 = g.x(); + g.0; //[ast]~ ERROR cannot use `g.0` because it was mutably borrowed + //[mir]~^ ERROR cannot use `g.0` because it was mutably borrowed + } + // Deref and field from tuple + { + let mut h = Box::new((22, 23)); + let _0 = &mut h.0; + h.0; //[ast]~ ERROR cannot use `h.0` because it was mutably borrowed + //[mir]~^ ERROR cannot use `h.0` because it was mutably borrowed + } + // Deref and field from enum + { + let mut e = Box::new(Baz::X(3)); + let _e0 = e.x(); + match *e { + Baz::X(value) => value + //[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed + }; + } + // Deref and field from union + unsafe { + let mut u = Box::new(U { b: 0 }); + let _ra = &mut u.a; + u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed + //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed + } + // Constant index + { + let mut v = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let _v = &mut v; + match v { + &[x, _, .., _, _] => println!("{}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[_, x, .., _, _] => println!("{}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[_, _, .., x, _] => println!("{}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[_, _, .., _, x] => println!("{}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + } + // Subslices + { + let mut v = &[1, 2, 3, 4, 5]; + let _v = &mut v; + match v { + &[x..] => println!("{:?}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[_, x..] => println!("{:?}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[x.., _] => println!("{:?}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + match v { + &[_, x.., _] => println!("{:?}", x), + //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed + _ => panic!("other case"), + } + } + // Downcasted field + { + enum E { A(X), B { x: X } } + + let mut e = E::A(3); + let _e = &mut e; + match e { + E::A(ref ax) => + //[ast]~^ ERROR cannot borrow `e.0` as immutable because `e` is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `e.0` as immutable because it is also borrowed as mutable + //[mir]~| ERROR cannot use `e` because it was mutably borrowed + println!("e.ax: {:?}", ax), + E::B { x: ref bx } => + //[ast]~^ ERROR cannot borrow `e.x` as immutable because `e` is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `e.x` as immutable because it is also borrowed as mutable + println!("e.bx: {:?}", bx), + } + } + // Field in field + { + struct F { x: u32, y: u32 }; + struct S { x: F, y: (u32, u32), }; + let mut s = S { x: F { x: 1, y: 2}, y: (999, 998) }; + let _s = &mut s; + match s { + S { y: (ref y0, _), .. } => + //[ast]~^ ERROR cannot borrow `s.y.0` as immutable because `s` is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `s.y.0` as immutable because it is also borrowed as mutable + println!("y0: {:?}", y0), + _ => panic!("other case"), + } + match s { + S { x: F { y: ref x0, .. }, .. } => + //[ast]~^ ERROR cannot borrow `s.x.y` as immutable because `s` is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `s.x.y` as immutable because it is also borrowed as mutable + println!("x0: {:?}", x0), + _ => panic!("other case"), + } + } + // Field of ref + { + struct Block<'a> { + current: &'a u8, + unrelated: &'a u8, + }; + + fn bump<'a>(mut block: &mut Block<'a>) { + let x = &mut block; + let p: &'a u8 = &*block.current; + //[mir]~^ ERROR cannot borrow `*block.current` as immutable because it is also borrowed as mutable + // No errors in AST because of issue rust#38899 + } + } + // Field of ptr + { + struct Block2 { + current: *const u8, + unrelated: *const u8, + } + + unsafe fn bump2(mut block: *mut Block2) { + let x = &mut block; + let p : *const u8 = &*(*block).current; + //[mir]~^ ERROR cannot borrow `*block.current` as immutable because it is also borrowed as mutable + // No errors in AST because of issue rust#38899 + } + } + // Field of index + { + struct F {x: u32, y: u32}; + let mut v = &[F{x: 1, y: 2}, F{x: 3, y: 4}]; + let _v = &mut v; + v[0].y; + //[ast]~^ ERROR cannot use `v[..].y` because it was mutably borrowed + //[mir]~^^ ERROR cannot use `v[..].y` because it was mutably borrowed + //[mir]~| ERROR cannot use `*v` because it was mutably borrowed + } + // Field of constant index + { + struct F {x: u32, y: u32}; + let mut v = &[F{x: 1, y: 2}, F{x: 3, y: 4}]; + let _v = &mut v; + match v { + &[_, F {x: ref xf, ..}] => println!("{}", xf), + //[mir]~^ ERROR cannot borrow `v[..].x` as immutable because it is also borrowed as mutable + // No errors in AST + _ => panic!("other case") + } + } + // Field from upvar + { + let mut x = 0; + || { + let y = &mut x; + &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time + //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time + *y = 1; + }; + } + // Field from upvar nested + { + let mut x = 0; + || { + || { + let y = &mut x; + &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time + //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time + *y = 1; + } + }; + } + { + fn foo(x: Vec) { + let c = || { + drop(x); + drop(x); //[ast]~ ERROR use of moved value: `x` + //[mir]~^ ERROR use of moved value: `x` + }; + c(); + } + } +} diff --git a/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs b/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs new file mode 100644 index 0000000000000..496aa84b593f3 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs @@ -0,0 +1,24 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +//compile-flags: -Z borrowck=mir + +fn foo(_:String) {} + +fn main() +{ + let my_str = "hello".to_owned(); + match Some(42) { + Some(_) if { drop(my_str); false } => {} + Some(_) => {} + None => { foo(my_str); } //~ ERROR [E0382] + } +} diff --git a/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs b/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs index 3098807f272f3..33b78cb26d8e3 100644 --- a/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs +++ b/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs @@ -8,12 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Check that we check fns appearing in constant declarations. // Issue #22382. const MOVE: fn(&String) -> String = { fn broken(x: &String) -> String { - return *x //~ ERROR cannot move + return *x //[ast]~ ERROR cannot move out of borrowed content [E0507] + //[mir]~^ ERROR [E0507] } broken }; diff --git a/src/test/compile-fail/borrowck/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs b/src/test/compile-fail/borrowck/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs index 03b6b1d732400..f09a0c7414ba9 100644 --- a/src/test/compile-fail/borrowck/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs +++ b/src/test/compile-fail/borrowck/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir fn main() { let mut _a = 3; @@ -17,7 +17,6 @@ fn main() { { let _c = &*_b; _a = 4; //[ast]~ ERROR cannot assign to `_a` - //[mir]~^ ERROR cannot assign to `_a` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `_a` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `_a` because it is borrowed } } diff --git a/src/test/compile-fail/borrowck/borrowck-init-in-fru.rs b/src/test/compile-fail/borrowck/borrowck-init-in-fru.rs index 569ddb80c2fe2..1ec0f98077477 100644 --- a/src/test/compile-fail/borrowck/borrowck-init-in-fru.rs +++ b/src/test/compile-fail/borrowck/borrowck-init-in-fru.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + #[derive(Clone)] struct point { x: isize, @@ -16,6 +19,8 @@ struct point { fn main() { let mut origin: point; - origin = point {x: 10,.. origin}; //~ ERROR use of possibly uninitialized variable: `origin.y` + origin = point {x: 10,.. origin}; + //[ast]~^ ERROR use of possibly uninitialized variable: `origin.y` [E0381] + //[mir]~^^ ERROR [E0381] origin.clone(); } diff --git a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs index 8b7ccedd6974f..8a09ab3fd06c8 100644 --- a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs +++ b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs @@ -14,6 +14,9 @@ // Also includes tests of the errors reported when the Box in question // is immutable (#14270). +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + #![feature(box_syntax)] struct A { a: isize } @@ -23,7 +26,8 @@ fn indirect_write_to_imm_box() { let mut x: isize = 1; let y: Box<_> = box &mut x; let p = &y; - ***p = 2; //~ ERROR cannot assign to data in a `&` reference + ***p = 2; //[ast]~ ERROR cannot assign to data in a `&` reference + //[mir]~^ ERROR cannot assign to immutable item `***p` drop(p); } @@ -32,7 +36,8 @@ fn borrow_in_var_from_var() { let mut y: Box<_> = box &mut x; let p = &y; let q = &***p; - **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed + **y = 2; //[ast]~ ERROR cannot assign to `**y` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y` because it is borrowed drop(p); drop(q); } @@ -42,7 +47,8 @@ fn borrow_in_var_from_var_via_imm_box() { let y: Box<_> = box &mut x; let p = &y; let q = &***p; - **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed + **y = 2; //[ast]~ ERROR cannot assign to `**y` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y` because it is borrowed drop(p); drop(q); } @@ -52,7 +58,8 @@ fn borrow_in_var_from_field() { let mut y: Box<_> = box &mut x.a; let p = &y; let q = &***p; - **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed + **y = 2; //[ast]~ ERROR cannot assign to `**y` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y` because it is borrowed drop(p); drop(q); } @@ -62,7 +69,8 @@ fn borrow_in_var_from_field_via_imm_box() { let y: Box<_> = box &mut x.a; let p = &y; let q = &***p; - **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed + **y = 2; //[ast]~ ERROR cannot assign to `**y` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y` because it is borrowed drop(p); drop(q); } @@ -72,7 +80,8 @@ fn borrow_in_field_from_var() { let mut y = B { a: box &mut x }; let p = &y.a; let q = &***p; - **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed + **y.a = 2; //[ast]~ ERROR cannot assign to `**y.a` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y.a` because it is borrowed drop(p); drop(q); } @@ -82,7 +91,8 @@ fn borrow_in_field_from_var_via_imm_box() { let y = B { a: box &mut x }; let p = &y.a; let q = &***p; - **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed + **y.a = 2; //[ast]~ ERROR cannot assign to `**y.a` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y.a` because it is borrowed drop(p); drop(q); } @@ -92,7 +102,8 @@ fn borrow_in_field_from_field() { let mut y = B { a: box &mut x.a }; let p = &y.a; let q = &***p; - **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed + **y.a = 2; //[ast]~ ERROR cannot assign to `**y.a` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y.a` because it is borrowed drop(p); drop(q); } @@ -102,7 +113,8 @@ fn borrow_in_field_from_field_via_imm_box() { let y = B { a: box &mut x.a }; let p = &y.a; let q = &***p; - **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed + **y.a = 2; //[ast]~ ERROR cannot assign to `**y.a` because it is borrowed + //[mir]~^ ERROR cannot assign to `**y.a` because it is borrowed drop(p); drop(q); } diff --git a/src/test/compile-fail/borrowck/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck/borrowck-lend-flow-match.rs index 0e8c003e408f6..2fe764568bc82 100644 --- a/src/test/compile-fail/borrowck/borrowck-lend-flow-match.rs +++ b/src/test/compile-fail/borrowck/borrowck-lend-flow-match.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir #![allow(unused_variables)] #![allow(unused_assignments)] @@ -26,8 +26,7 @@ fn separate_arms() { } Some(ref __isize) => { x = Some(1); //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `x` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `x` because it is borrowed } } x.clone(); // just to prevent liveness warnings diff --git a/src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs b/src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs new file mode 100644 index 0000000000000..3f5725029d887 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +fn cplusplus_mode(x: isize) -> &'static isize { + &x //[ast]~ ERROR `x` does not live long enough + //[mir]~^ ERROR borrowed value does not live long enough +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-local-borrow-with-panic-outlives-fn.rs b/src/test/compile-fail/borrowck/borrowck-local-borrow-with-panic-outlives-fn.rs new file mode 100644 index 0000000000000..7009c6f33e61a --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-local-borrow-with-panic-outlives-fn.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +fn cplusplus_mode_exceptionally_unsafe(x: &mut Option<&'static mut isize>) { + let mut z = (0, 0); + *x = Some(&mut z.1); //[ast]~ ERROR [E0597] + //[mir]~^ ERROR [E0597] + panic!("catch me for a dangling pointer!") +} + +fn main() { + cplusplus_mode_exceptionally_unsafe(&mut None); +} diff --git a/src/test/compile-fail/borrowck/borrowck-match-already-borrowed.rs b/src/test/compile-fail/borrowck/borrowck-match-already-borrowed.rs new file mode 100644 index 0000000000000..4336812af9b58 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-match-already-borrowed.rs @@ -0,0 +1,40 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +enum Foo { + A(i32), + B +} + +fn match_enum() { + let mut foo = Foo::B; + let p = &mut foo; + let _ = match foo { + Foo::B => 1, //[mir]~ ERROR [E0503] + _ => 2, + Foo::A(x) => x //[ast]~ ERROR [E0503] + //[mir]~^ ERROR [E0503] + }; +} + + +fn main() { + let mut x = 1; + let _x = &mut x; + let _ = match x { + x => x + 1, //[ast]~ ERROR [E0503] + //[mir]~^ ERROR [E0503] + y => y + 2, //[ast]~ ERROR [E0503] + //[mir]~^ ERROR [E0503] + }; +} diff --git a/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs b/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs index 3639db5cfc4cd..32a573911ecec 100644 --- a/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs +++ b/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Zemit-end-regions -Zborrowck-mir +//[mir]compile-flags: -Z borrowck=mir // Test that immutable pattern bindings cannot be reassigned. @@ -26,41 +26,36 @@ struct S { pub fn main() { match 1 { x => { - x += 1; //[ast]~ ERROR re-assignment of immutable variable `x` - //[mir]~^ ERROR (Mir) [E0384] - //[mir]~| ERROR (Ast) [E0384] + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable `x` + //[mir]~^ ERROR [E0384] } } match E::Foo(1) { E::Foo(x) => { - x += 1; //[ast]~ ERROR re-assignment of immutable variable `x` - //[mir]~^ ERROR (Mir) [E0384] - //[mir]~| ERROR (Ast) [E0384] + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable `x` + //[mir]~^ ERROR [E0384] } } match (S { bar: 1 }) { S { bar: x } => { - x += 1; //[ast]~ ERROR re-assignment of immutable variable `x` - //[mir]~^ ERROR (Mir) [E0384] - //[mir]~| ERROR (Ast) [E0384] + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable `x` + //[mir]~^ ERROR [E0384] } } match (1,) { (x,) => { - x += 1; //[ast]~ ERROR re-assignment of immutable variable `x` - //[mir]~^ ERROR (Mir) [E0384] - //[mir]~| ERROR (Ast) [E0384] + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable `x` + //[mir]~^ ERROR [E0384] } } match [1,2,3] { [x,_,_] => { - x += 1; //[ast]~ ERROR re-assignment of immutable variable `x` - //[mir]~^ ERROR (Mir) [E0384] - //[mir]~| ERROR (Ast) [E0384] + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable `x` + //[mir]~^ ERROR [E0384] } } } diff --git a/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs b/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs index ec505faf88502..5fdde484f8220 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs @@ -8,19 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + fn with(f: F) where F: FnOnce(&String) {} fn arg_item(&_x: &String) {} - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR [E0507] fn arg_closure() { with(|&_x| ()) - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR [E0507] } fn let_pat() { let &_x = &"hi".to_string(); - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR [E0507] } pub fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs index bf4c74741368c..af9202d8d7792 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs @@ -8,9 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + use std::rc::Rc; pub fn main() { let _x = Rc::new(vec![1, 2]).into_iter(); - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR [E0507] } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs index 8b83b945fd148..79eb68c95a037 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Ensure that moves out of static items is forbidden struct Foo { @@ -22,5 +25,6 @@ fn test(f: Foo) { } fn main() { - test(BAR); //~ ERROR cannot move out of static item + test(BAR); //[ast]~ ERROR cannot move out of static item [E0507] + //[mir]~^ ERROR [E0507] } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs index 16302d276cee2..8e1f7c7291474 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + struct S {f:String} impl Drop for S { fn drop(&mut self) { println!("{}", self.f); } @@ -16,17 +19,20 @@ impl Drop for S { fn move_in_match() { match (S {f:"foo".to_string()}) { S {f:_s} => {} - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR [E0509] } } fn move_in_let() { let S {f:_s} = S {f:"foo".to_string()}; - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR [E0509] } fn move_in_fn_arg(S {f:_s}: S) { - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR [E0509] } fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs index 38e0e27a7b98e..6896d166e7a4a 100644 --- a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs +++ b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs @@ -12,17 +12,26 @@ // conflicts with a new loan, as opposed to every issued loan. This keeps us // down to O(n) errors (for n problem lines), instead of O(n^2) errors. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + fn main() { let mut x = 1; let mut addr; loop { match 1 { - 1 => { addr = &mut x; } - //~^ ERROR cannot borrow `x` as mutable more than once at a time - 2 => { addr = &mut x; } - //~^ ERROR cannot borrow `x` as mutable more than once at a time - _ => { addr = &mut x; } - //~^ ERROR cannot borrow `x` as mutable more than once at a time + 1 => { addr = &mut x; } //[ast]~ ERROR [E0499] + //[mir]~^ ERROR [E0499] + 2 => { addr = &mut x; } //[ast]~ ERROR [E0499] + //[mir]~^ ERROR [E0506] + //[mir]~| ERROR [E0499] + //[mir]~| ERROR [E0499] + _ => { addr = &mut x; } //[ast]~ ERROR [E0499] + //[mir]~^ ERROR [E0506] + //[mir]~| ERROR [E0499] + //[mir]~| ERROR [E0499] } } } + + diff --git a/src/test/compile-fail/borrowck/borrowck-overloaded-call.rs b/src/test/compile-fail/borrowck/borrowck-overloaded-call.rs index 4c20688331b6d..41f3e472cd125 100644 --- a/src/test/compile-fail/borrowck/borrowck-overloaded-call.rs +++ b/src/test/compile-fail/borrowck/borrowck-overloaded-call.rs @@ -67,7 +67,6 @@ fn f() { }; let sp = &mut s; s(3); //~ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable - //~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable } fn g() { diff --git a/src/test/compile-fail/borrowck/borrowck-overloaded-index-and-overloaded-deref.rs b/src/test/compile-fail/borrowck/borrowck-overloaded-index-and-overloaded-deref.rs index 9b20cd470f62b..931d053ae7b9f 100644 --- a/src/test/compile-fail/borrowck/borrowck-overloaded-index-and-overloaded-deref.rs +++ b/src/test/compile-fail/borrowck/borrowck-overloaded-index-and-overloaded-deref.rs @@ -14,7 +14,7 @@ // here is rather subtle. Issue #20232. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir use std::ops::{Deref, Index}; @@ -43,8 +43,7 @@ fn main() { let i = &v[0].f; v = MyVec { x: MyPtr { x: Foo { f: 23 } } }; //[ast]~^ ERROR cannot assign to `v` - //[mir]~^^ ERROR cannot assign to `v` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `v` because it is borrowed (Mir) + //[mir]~^^ ERROR cannot assign to `v` because it is borrowed read(*i); } diff --git a/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs b/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs index 4c50caf49768d..3a4c22eb1395a 100644 --- a/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs +++ b/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + use std::ops::{Index, IndexMut}; struct Foo { @@ -57,12 +60,15 @@ fn main() { let mut s = "hello".to_string(); let rs = &mut s; println!("{}", f[&s]); - //~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable + //[ast]~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable f[&s] = 10; - //~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable + //[ast]~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable + //[mir]~^^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable let s = Bar { x: 1, }; s[2] = 20; - //~^ ERROR cannot assign to immutable indexed content + //[ast]~^ ERROR cannot assign to immutable indexed content + //[mir]~^^ ERROR cannot assign to immutable item } diff --git a/src/test/compile-fail/borrowck/borrowck-pat-reassign-binding.rs b/src/test/compile-fail/borrowck/borrowck-pat-reassign-binding.rs index 06bb98fa0ec10..0f3a841821080 100644 --- a/src/test/compile-fail/borrowck/borrowck-pat-reassign-binding.rs +++ b/src/test/compile-fail/borrowck/borrowck-pat-reassign-binding.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir fn main() { let mut x: Option = None; @@ -21,8 +21,7 @@ fn main() { Some(ref i) => { // But on this branch, `i` is an outstanding borrow x = Some(*i+1); //[ast]~ ERROR cannot assign to `x` - //[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `x` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `x` because it is borrowed } } x.clone(); // just to prevent liveness warnings diff --git a/src/test/compile-fail/borrowck/borrowck-reborrow-from-shorter-lived-andmut.rs b/src/test/compile-fail/borrowck/borrowck-reborrow-from-shorter-lived-andmut.rs index eee407472bf14..af85e68f5debe 100644 --- a/src/test/compile-fail/borrowck/borrowck-reborrow-from-shorter-lived-andmut.rs +++ b/src/test/compile-fail/borrowck/borrowck-reborrow-from-shorter-lived-andmut.rs @@ -17,7 +17,7 @@ struct S<'a> { fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> { S { pointer: &mut *p.pointer } - //~^ ERROR cannot infer + //~^ ERROR lifetime mismatch } fn main() { diff --git a/src/test/compile-fail/borrowck/borrowck-storage-dead.rs b/src/test/compile-fail/borrowck/borrowck-storage-dead.rs new file mode 100644 index 0000000000000..bc01088696da6 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-storage-dead.rs @@ -0,0 +1,37 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z borrowck=compare + +fn ok() { + loop { + let _x = 1; + } +} + +fn also_ok() { + loop { + let _x = String::new(); + } +} + +fn fail() { + loop { + let x: i32; + let _ = x + 1; //~ERROR (Ast) [E0381] + //~^ ERROR (Mir) [E0381] + } +} + +fn main() { + ok(); + also_ok(); + fail(); +} diff --git a/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs b/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs index c364788a9cc8d..f90651687a53f 100644 --- a/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs +++ b/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Issue 4691: Ensure that functional-struct-update can only copy, not // move, when the struct implements Drop. @@ -20,12 +23,14 @@ impl Drop for T { fn drop(&mut self) { } } fn f(s0:S) { let _s2 = S{a: 2, ..s0}; - //~^ error: cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ error: cannot move out of type `S`, which implements the `Drop` trait + //[mir]~^^ ERROR [E0509] } fn g(s0:T) { let _s2 = T{a: 2, ..s0}; - //~^ error: cannot move out of type `T`, which implements the `Drop` trait + //[ast]~^ error: cannot move out of type `T`, which implements the `Drop` trait + //[mir]~^^ ERROR [E0509] } fn main() { } diff --git a/src/test/compile-fail/impl-trait/disallowed-2.rs b/src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs similarity index 62% rename from src/test/compile-fail/impl-trait/disallowed-2.rs rename to src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs index 46b3106ab8d6e..f2e6d51d064d1 100644 --- a/src/test/compile-fail/impl-trait/disallowed-2.rs +++ b/src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs @@ -8,11 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait)] +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir +#![feature(thread_local)] + +#[thread_local] +static FOO: u8 = 3; + +fn assert_static(_t: &'static u8) {} fn main() { - let _: impl Fn() = || {}; - //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - let _ = || -> impl Fn() { || {} }; - //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + assert_static(&FOO); //[ast]~ ERROR [E0597] + //[mir]~^ ERROR [E0597] } diff --git a/src/test/compile-fail/borrowck/borrowck-unary-move.rs b/src/test/compile-fail/borrowck/borrowck-unary-move.rs index 5b5c5f4da912c..8163ce2993926 100644 --- a/src/test/compile-fail/borrowck/borrowck-unary-move.rs +++ b/src/test/compile-fail/borrowck/borrowck-unary-move.rs @@ -8,10 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-tidy-linelength +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir fn foo(x: Box) -> isize { let y = &*x; - free(x); //~ ERROR cannot move out of `x` because it is borrowed + free(x); //[ast]~ ERROR cannot move out of `x` because it is borrowed + //[mir]~^ ERROR cannot move out of `x` because it is borrowed *y } diff --git a/src/test/compile-fail/borrowck/borrowck-unboxed-closures.rs b/src/test/compile-fail/borrowck/borrowck-unboxed-closures.rs index 0f9829ab259a9..4813b4b6a72cd 100644 --- a/src/test/compile-fail/borrowck/borrowck-unboxed-closures.rs +++ b/src/test/compile-fail/borrowck/borrowck-unboxed-closures.rs @@ -11,7 +11,6 @@ fn a isize>(mut f: F) { let g = &mut f; f(1, 2); //~ ERROR cannot borrow `f` as immutable - //~^ ERROR cannot borrow `f` as immutable } fn b isize>(f: F) { diff --git a/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs new file mode 100644 index 0000000000000..a214e3c126e8d --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs @@ -0,0 +1,46 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +// Check that do not allow access to fields of uninitialized or moved +// structs. + +#[derive(Default)] +struct Point { + x: isize, + y: isize, +} + +#[derive(Default)] +struct Line { + origin: Point, + middle: Point, + target: Point, +} + +impl Line { fn consume(self) { } } + +fn main() { + let mut a: Point; + let _ = a.x + 1; //[ast]~ ERROR use of possibly uninitialized variable: `a.x` + //[mir]~^ ERROR [E0381] + + let mut line1 = Line::default(); + let _moved = line1.origin; + let _ = line1.origin.x + 1; //[ast]~ ERROR use of collaterally moved value: `line1.origin.x` + //[mir]~^ [E0382] + + let mut line2 = Line::default(); + let _moved = (line2.origin, line2.middle); + line2.consume(); //[ast]~ ERROR use of partially moved value: `line2` [E0382] + //[mir]~^ [E0382] +} diff --git a/src/test/compile-fail/borrowck/borrowck-uninit-ref-chain.rs b/src/test/compile-fail/borrowck/borrowck-uninit-ref-chain.rs new file mode 100644 index 0000000000000..c52b1f0bf64c0 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-uninit-ref-chain.rs @@ -0,0 +1,53 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +struct S { + x: X, + y: Y, +} + +fn main() { + let x: &&Box; + let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381] + //[mir]~^ [E0381] + + let x: &&S; + let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381] + //[mir]~^ [E0381] + + let x: &&i32; + let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381] + //[mir]~^ [E0381] + + + let mut a: S; + a.x = 0; + let _b = &a.x; //[ast]~ ERROR use of possibly uninitialized variable: `a.x` [E0381] + // (deliberately *not* an error under MIR-borrowck) + + let mut a: S<&&i32, &&i32>; + a.x = &&0; + let _b = &**a.x; //[ast]~ ERROR use of possibly uninitialized variable: `**a.x` [E0381] + // (deliberately *not* an error under MIR-borrowck) + + + let mut a: S; + a.x = 0; + let _b = &a.y; //[ast]~ ERROR use of possibly uninitialized variable: `a.y` [E0381] + //[mir]~^ ERROR [E0381] + + let mut a: S<&&i32, &&i32>; + a.x = &&0; + let _b = &**a.y; //[ast]~ ERROR use of possibly uninitialized variable: `**a.y` [E0381] + //[mir]~^ ERROR [E0381] +} diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs index 73d323ea82cfb..975de8b6c41f9 100644 --- a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs +++ b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs @@ -10,7 +10,7 @@ // ignore-tidy-linelength // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir #[derive(Clone, Copy)] union U { @@ -33,14 +33,12 @@ fn main() { { let ra = &u.a; let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable - //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable (Ast) - //[mir]~| ERROR cannot borrow `u.0` as mutable because it is also borrowed as immutable (Mir) + //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable } { let ra = &u.a; u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `u.0` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed } // Imm borrow, other field { @@ -54,63 +52,53 @@ fn main() { { let ra = &u.a; let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - //[mir]~^ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) (Ast) // FIXME Error for MIR (needs support for union) } { let ra = &u.a; u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed (Ast) // FIXME Error for MIR (needs support for union) } // Mut borrow, same field { let rma = &mut u.a; let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable - //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable (Ast) - //[mir]~| ERROR cannot borrow `u.0` as immutable because it is also borrowed as mutable (Mir) + //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable } { let ra = &mut u.a; let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed - //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed (Ast) - //[mir]~| ERROR cannot use `u.0` because it was mutably borrowed (Mir) + //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed } { let rma = &mut u.a; let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time - //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time (Ast) - //[mir]~| ERROR cannot borrow `u.0` as mutable more than once at a time (Mir) + //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time } { let rma = &mut u.a; u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `u.0` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed } // Mut borrow, other field { let rma = &mut u.a; let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - //[mir]~^ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) (Ast) // FIXME Error for MIR (needs support for union) } { let ra = &mut u.a; let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed - //[mir]~^ ERROR cannot use `u.b` because it was mutably borrowed (Ast) // FIXME Error for MIR (needs support for union) } { let rma = &mut u.a; let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time - //[mir]~^ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time (Ast) // FIXME Error for MIR (needs support for union) } { let rma = &mut u.a; u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed (Ast) // FIXME Error for MIR (needs support for union) } } diff --git a/src/test/compile-fail/borrowck/borrowck-use-in-index-lvalue.rs b/src/test/compile-fail/borrowck/borrowck-use-in-index-lvalue.rs index 7291bcd2ce126..eb99f78f461f4 100644 --- a/src/test/compile-fail/borrowck/borrowck-use-in-index-lvalue.rs +++ b/src/test/compile-fail/borrowck/borrowck-use-in-index-lvalue.rs @@ -8,12 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + fn test() { let w: &mut [isize]; - w[5] = 0; //~ ERROR use of possibly uninitialized variable: `*w` + w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381] + //[mir]~^ ERROR [E0381] let mut w: &mut [isize]; - w[5] = 0; //~ ERROR use of possibly uninitialized variable: `*w` + w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381] + //[mir]~^ ERROR [E0381] } fn main() { test(); } diff --git a/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast-trait.rs b/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast-trait.rs index 796b455f5c70a..57c2d508356bc 100644 --- a/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast-trait.rs +++ b/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast-trait.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Variation on `borrowck-use-uninitialized-in-cast` in which we do a // trait cast from an uninitialized source. Issue #20791. @@ -16,5 +19,6 @@ impl Foo for i32 { } fn main() { let x: &i32; - let y = x as *const Foo; //~ ERROR use of possibly uninitialized variable: `*x` + let y = x as *const Foo; //[ast]~ ERROR use of possibly uninitialized variable: `*x` + //[mir]~^ ERROR [E0381] } diff --git a/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast.rs b/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast.rs index 3f429bbd4b634..dbc20d020577e 100644 --- a/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast.rs +++ b/src/test/compile-fail/borrowck/borrowck-use-uninitialized-in-cast.rs @@ -8,11 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Check that we detect unused values that are cast to other things. // The problem was specified to casting to `*`, as creating unsafe // pointers was not being fully checked. Issue #20791. fn main() { let x: &i32; - let y = x as *const i32; //~ ERROR use of possibly uninitialized variable: `*x` + let y = x as *const i32; //[ast]~ ERROR use of possibly uninitialized variable: `*x` [E0381] + //[mir]~^ ERROR [E0381] } diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs index b5916584930b8..304a41c14ed33 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +// revisions: ast cmp +//[cmp]compile-flags: -Z borrowck=compare #![feature(slice_patterns)] @@ -21,7 +21,7 @@ fn main() { }; println!("t[0]: {}", t[0]); a[2] = 0; //[ast]~ ERROR cannot assign to `a[..]` because it is borrowed - //[mir]~^ ERROR cannot assign to `a[..]` because it is borrowed (Ast) + //[cmp]~^ ERROR cannot assign to `a[..]` because it is borrowed (Ast) // FIXME Error for MIR (error missed) println!("t[0]: {}", t[0]); t[0]; diff --git a/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs b/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs index 3c1980e5b366c..508e09318ae11 100644 --- a/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs +++ b/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Regression test for #38520. Check that moves of `Foo` are not // permitted as `Foo` is not copy (even in a static/const // initializer). @@ -21,8 +24,10 @@ const fn get(x: Foo) -> usize { } const X: Foo = Foo(22); -static Y: usize = get(*&X); //~ ERROR E0507 -const Z: usize = get(*&X); //~ ERROR E0507 +static Y: usize = get(*&X); //[ast]~ ERROR E0507 + //[mir]~^ ERROR [E0507] +const Z: usize = get(*&X); //[ast]~ ERROR E0507 + //[mir]~^ ERROR [E0507] fn main() { } diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs index 5ddde6460b01f..8eed61ec8d531 100644 --- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs +++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs @@ -8,6 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-tidy-linelength +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + #![feature(unboxed_closures)] use std::io::Read; @@ -17,9 +21,11 @@ fn to_fn_once>(f: F) -> F { f } fn main() { let x = 1; to_fn_once(move|| { x = 2; }); - //~^ ERROR: cannot assign to immutable captured outer variable + //[ast]~^ ERROR: cannot assign to immutable captured outer variable + //[mir]~^^ ERROR: cannot assign to immutable item `x` let s = std::io::stdin(); to_fn_once(move|| { s.read_to_end(&mut Vec::new()); }); - //~^ ERROR: cannot borrow immutable captured outer variable + //[ast]~^ ERROR: cannot borrow immutable captured outer variable + //[mir]~^^ ERROR: cannot borrow immutable item `s` as mutable } diff --git a/src/test/compile-fail/changing-crates.rs b/src/test/compile-fail/changing-crates.rs index f74855a0849b1..89310706b52ed 100644 --- a/src/test/compile-fail/changing-crates.rs +++ b/src/test/compile-fail/changing-crates.rs @@ -17,8 +17,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on +//~| NOTE: the following crate versions were found //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: fn main() {} diff --git a/src/test/compile-fail/check-static-immutable-mut-slices.rs b/src/test/compile-fail/check-static-immutable-mut-slices.rs index 370cfe9d55012..1804b9e04c2c9 100644 --- a/src/test/compile-fail/check-static-immutable-mut-slices.rs +++ b/src/test/compile-fail/check-static-immutable-mut-slices.rs @@ -12,6 +12,5 @@ static TEST: &'static mut [isize] = &mut []; //~^ ERROR references in statics may only refer to immutable values -//~^^ ERROR references in statics may only refer to immutable values pub fn main() { } diff --git a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs index 16ed73e9095e4..513a17e2ef2f4 100644 --- a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs +++ b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs @@ -13,8 +13,7 @@ fn bar(blk: F) where F: FnOnce() + 'static { fn foo(x: &()) { bar(|| { - //~^ ERROR cannot infer - //~| ERROR does not fulfill + //~^ ERROR does not fulfill let _ = x; }) } diff --git a/src/test/compile-fail/closure-expected-type/README.md b/src/test/compile-fail/closure-expected-type/README.md new file mode 100644 index 0000000000000..9995b00a9a76d --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/README.md @@ -0,0 +1 @@ +See `src/test/run-pass/closure-expected-type`. diff --git a/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs new file mode 100644 index 0000000000000..f1b198a059176 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs @@ -0,0 +1,49 @@ +// 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. + +// must-compile-successfully + +#![feature(underscore_lifetimes)] +#![allow(warnings)] + +type Different<'a, 'b> = &'a mut (&'a (), &'b ()); +type Same<'a> = Different<'a, 'a>; + +fn with_closure_expecting_different(_: F) + where F: for<'a, 'b> FnOnce(Different<'a, 'b>) +{ +} + +fn with_closure_expecting_different_anon(_: F) + where F: FnOnce(Different<'_, '_>) +{ +} + +fn supplying_nothing_expecting_anon() { + with_closure_expecting_different_anon(|x: Different| { + }) +} + +fn supplying_nothing_expecting_named() { + with_closure_expecting_different(|x: Different| { + }) +} + +fn supplying_underscore_expecting_anon() { + with_closure_expecting_different_anon(|x: Different<'_, '_>| { + }) +} + +fn supplying_underscore_expecting_named() { + with_closure_expecting_different(|x: Different<'_, '_>| { + }) +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs new file mode 100644 index 0000000000000..645fd1f80babf --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs @@ -0,0 +1,70 @@ +// 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. + +#![feature(underscore_lifetimes)] + +fn with_closure_expecting_fn_with_free_region(_: F) + where F: for<'a> FnOnce(fn(&'a u32), &i32) +{ +} + +fn with_closure_expecting_fn_with_bound_region(_: F) + where F: FnOnce(fn(&u32), &i32) +{ +} + +fn expect_free_supply_free_from_fn<'x>(x: &'x u32) { + // Here, the type given for `'x` "obscures" a region from the + // expected signature that is bound at closure level. + with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); + //~^ ERROR mismatched types + //~| ERROR mismatched types +} + +fn expect_free_supply_free_from_closure() { + // A variant on the previous test. Here, the region `'a` will be + // bound at the closure level, just as is expected, so no error + // results. + type Foo<'a> = fn(&'a u32); + with_closure_expecting_fn_with_free_region(|_x: Foo<'_>, y| {}); +} + +fn expect_free_supply_bound() { + // Here, we are given a function whose region is bound at closure level, + // but we expect one bound in the argument. Error results. + with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); + //~^ ERROR type mismatch in closure arguments +} + +fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) { + // Here, we are given a `fn(&u32)` but we expect a `fn(&'x + // u32)`. In principle, this could be ok, but we demand equality. + with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {}); + //~^ ERROR type mismatch in closure arguments +} + +fn expect_bound_supply_free_from_closure() { + // A variant on the previous test. Here, the region `'a` will be + // bound at the closure level, but we expect something bound at + // the argument level. + type Foo<'a> = fn(&'a u32); + with_closure_expecting_fn_with_bound_region(|_x: Foo<'_>, y| {}); + //~^ ERROR type mismatch in closure arguments +} + +fn expect_bound_supply_bound<'x>(x: &'x u32) { + // No error in this case. The supplied type supplies the bound + // regions, and hence we are able to figure out the type of `y` + // from the expected type + with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| { + }); +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs new file mode 100644 index 0000000000000..bef69a4b0b9f8 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs @@ -0,0 +1,35 @@ +// 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. + +fn with_closure(_: F) + where F: FnOnce(A, A) +{ +} + +fn a() { + with_closure(|x: u32, y| { + // We deduce type of `y` from `x`. + }); +} + +fn b() { + // Here we take the supplied types, resulting in an error later on. + with_closure(|x: u32, y: i32| { + //~^ ERROR type mismatch in closure arguments + }); +} + +fn c() { + with_closure(|x, y: i32| { + // We deduce type of `x` from `y`. + }); +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs new file mode 100644 index 0000000000000..f8cb643c8d646 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +fn with_closure(_: F) + where F: FnOnce(A, &u32) +{ +} + +fn foo() { + // This version works; we infer `A` to be `u32`, and take the type + // of `y` to be `&u32`. + with_closure(|x: u32, y| {}); +} + +fn bar() { + // This version also works. + with_closure(|x: &u32, y| {}); +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs new file mode 100644 index 0000000000000..d3c111c5daf15 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +fn with_closure(_: F) + where F: FnOnce(A, &u32) +{ +} + +fn foo() { + // This version works; we infer `A` to be `u32`, and take the type + // of `y` to be `&u32`. + with_closure(|x: u32, y| {}); +} + +fn bar<'x>(x: &'x u32) { + // Same. + with_closure(|x: &'x u32, y| {}); +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs b/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs new file mode 100644 index 0000000000000..9da12dc901fba --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs @@ -0,0 +1,80 @@ +// 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. + +#![allow(warnings)] + +fn closure_expecting_bound(_: F) + where F: FnOnce(&u32) +{ +} + +fn closure_expecting_free<'a, F>(_: F) + where F: FnOnce(&'a u32) +{ +} + +fn expect_bound_supply_nothing() { + // Because `x` is inferred to have a bound region, we cannot allow + // it to escape into `f`: + let mut f: Option<&u32> = None; + closure_expecting_bound(|x| { + f = Some(x); //~ ERROR E0495 + }); +} + +fn expect_bound_supply_bound() { + // Because `x` is inferred to have a bound region, we cannot allow + // it to escape into `f`, even with an explicit type annotation on + // closure: + let mut f: Option<&u32> = None; + closure_expecting_bound(|x: &u32| { + f = Some(x); //~ ERROR E0495 + }); +} + +fn expect_bound_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here we give a type annotation that `x` should be free. We get + // an error because of that. + closure_expecting_bound(|x: &'x u32| { + //~^ ERROR mismatched types + //~| ERROR mismatched types + + // And we still cannot let `x` escape into `f`. + f = Some(x); + //~^ ERROR cannot infer + }); +} + +fn expect_free_supply_nothing() { + let mut f: Option<&u32> = None; + closure_expecting_free(|x| f = Some(x)); // OK +} + +fn expect_free_supply_bound() { + let mut f: Option<&u32> = None; + + // Here, even though the annotation `&u32` could be seen as being + // bound in the closure, we permit it to be defined as a free + // region (which is inferred to something in the fn body). + closure_expecting_free(|x: &u32| f = Some(x)); // OK +} + +fn expect_free_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here, even though the annotation `&u32` could be seen as being + // bound in the closure, we permit it to be defined as a free + // region (which is inferred to something in the fn body). + closure_expecting_free(|x: &'x u32| f = Some(x)); // OK +} + +fn main() { } diff --git a/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs b/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs new file mode 100644 index 0000000000000..377eaadbd6af0 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn with_closure(_: F) + where F: FnOnce(A, B) +{ +} + +fn a() { + // Type of `y` is unconstrained. + with_closure(|x: u32, y| {}); //~ ERROR E0282 +} + +fn b() { + with_closure(|x: u32, y: u32| {}); // OK +} + +fn c() { + with_closure(|x: u32, y: u32| {}); // OK +} + +fn main() { } diff --git a/src/test/compile-fail/coerce-overloaded-autoderef.rs b/src/test/compile-fail/coerce-overloaded-autoderef.rs index 43b771ce5dbed..0487b03171adb 100644 --- a/src/test/compile-fail/coerce-overloaded-autoderef.rs +++ b/src/test/compile-fail/coerce-overloaded-autoderef.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir fn borrow_mut(x: &mut T) -> &mut T { x } fn borrow(x: &T) -> &T { x } @@ -21,8 +21,7 @@ fn double_mut_borrow(x: &mut Box) { let y = borrow_mut(x); let z = borrow_mut(x); //[ast]~^ ERROR cannot borrow `*x` as mutable more than once at a time - //[mir]~^^ ERROR cannot borrow `*x` as mutable more than once at a time (Ast) - //[mir]~| ERROR cannot borrow `(*x)` as mutable more than once at a time (Mir) + //[mir]~^^ ERROR cannot borrow `*x` as mutable more than once at a time } fn double_imm_borrow(x: &mut Box) { @@ -30,22 +29,19 @@ fn double_imm_borrow(x: &mut Box) { let z = borrow(x); **x += 1; //[ast]~^ ERROR cannot assign to `**x` because it is borrowed - //[mir]~^^ ERROR cannot assign to `**x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `(*(*x))` because it is borrowed (Mir) + //[mir]~^^ ERROR cannot assign to `**x` because it is borrowed } fn double_mut_borrow2(x: &mut Box) { borrow_mut2(x, x); //[ast]~^ ERROR cannot borrow `*x` as mutable more than once at a time - //[mir]~^^ ERROR cannot borrow `*x` as mutable more than once at a time (Ast) - //[mir]~| ERROR cannot borrow `(*x)` as mutable more than once at a time (Mir) + //[mir]~^^ ERROR cannot borrow `*x` as mutable more than once at a time } fn double_borrow2(x: &mut Box) { borrow2(x, x); //[ast]~^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable - //[mir]~^^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable (Ast) - //[mir]~| ERROR cannot borrow `(*x)` as immutable because it is also borrowed as mutable (Mir) + //[mir]~^^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable } pub fn main() {} diff --git a/src/test/compile-fail/coerce-to-bang-cast.rs b/src/test/compile-fail/coerce-to-bang-cast.rs index 0479f5cce6537..0d5bf6cd68cb8 100644 --- a/src/test/compile-fail/coerce-to-bang-cast.rs +++ b/src/test/compile-fail/coerce-to-bang-cast.rs @@ -12,8 +12,11 @@ fn foo(x: usize, y: !, z: usize) { } +#[deny(coerce_never)] fn cast_a() { let y = {return; 22} as !; + //~^ ERROR cannot coerce `i32` to ! + //~| hard error } fn cast_b() { diff --git a/src/test/compile-fail/coerce-to-bang.rs b/src/test/compile-fail/coerce-to-bang.rs index 870665bb49ee6..2cf568777d475 100644 --- a/src/test/compile-fail/coerce-to-bang.rs +++ b/src/test/compile-fail/coerce-to-bang.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(never_type)] +#![deny(coerce_never)] fn foo(x: usize, y: !, z: usize) { } @@ -17,6 +18,8 @@ fn call_foo_a() { // the coercion to `!`, but within same expression. Not clear that // these are the rules we want. foo(return, 22, 44); + //~^ ERROR cannot coerce `{integer}` to ! + //~| hard error } fn call_foo_b() { @@ -36,6 +39,8 @@ fn call_foo_d() { let b = 22; let c = 44; foo(a, b, c); // ... and hence a reference to `a` is expected to diverge. + //~^ ERROR cannot coerce `{integer}` to ! + //~| hard error } fn call_foo_e() { @@ -75,6 +80,8 @@ fn tuple_a() { fn tuple_b() { // Divergence happens before coercion: OK let x: (usize, !, usize) = (return, 44, 66); + //~^ ERROR cannot coerce `{integer}` to ! + //~| hard error } fn tuple_c() { diff --git a/src/test/compile-fail/coherence-default-trait-impl.rs b/src/test/compile-fail/coherence-default-trait-impl.rs index e6bf068156c2b..9c26b8b05f259 100644 --- a/src/test/compile-fail/coherence-default-trait-impl.rs +++ b/src/test/compile-fail/coherence-default-trait-impl.rs @@ -12,18 +12,22 @@ trait MyTrait { fn foo() {} } +#[allow(auto_impl)] impl MyTrait for .. {} -//~^ ERROR redundant default implementations of trait `MyTrait` +//~^ ERROR redundant auto implementations of trait `MyTrait` +#[allow(auto_impl)] impl MyTrait for .. {} trait MySafeTrait {} +#[allow(auto_impl)] unsafe impl MySafeTrait for .. {} //~^ ERROR implementing the trait `MySafeTrait` is not unsafe unsafe trait MyUnsafeTrait {} +#[allow(auto_impl)] impl MyUnsafeTrait for .. {} //~^ ERROR the trait `MyUnsafeTrait` requires an `unsafe impl` declaration diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 944e458c4c0f0..e65194ab56f98 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -24,8 +24,6 @@ fn black_box(_: T) { const FOO: u8 = [5u8][1]; //~^ ERROR constant evaluation error //~| index out of bounds: the len is 1 but the index is 1 -//~^^^ ERROR constant evaluation error -//~| index out of bounds: the len is 1 but the index is 1 fn main() { let a = -std::i8::MIN; @@ -33,8 +31,7 @@ fn main() { //~| attempt to negate with overflow let b = 200u8 + 200u8 + 200u8; //~^ WARN this expression will panic at run-time - //~| attempt to add with overflow - //~^^^ WARN this expression will panic at run-time + //~^^ WARN this expression will panic at run-time //~| attempt to add with overflow let c = 200u8 * 4; //~^ WARN this expression will panic at run-time diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs index 7c1fb2ccd4729..9889ca1392ac1 100644 --- a/src/test/compile-fail/const-err2.rs +++ b/src/test/compile-fail/const-err2.rs @@ -21,7 +21,6 @@ fn main() { //~^ ERROR attempt to negate with overflow let b = 200u8 + 200u8 + 200u8; //~^ ERROR attempt to add with overflow - //~| ERROR attempt to add with overflow let c = 200u8 * 4; //~^ ERROR attempt to multiply with overflow let d = 42u8 - (42u8 + 1); diff --git a/src/test/compile-fail/const-eval-overflow-4.rs b/src/test/compile-fail/const-eval-overflow-4.rs index 06b7d0206b128..2b1c1017b5b00 100644 --- a/src/test/compile-fail/const-eval-overflow-4.rs +++ b/src/test/compile-fail/const-eval-overflow-4.rs @@ -23,6 +23,8 @@ const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] //~^ ERROR constant evaluation error //~^^ NOTE attempt to add with overflow + //~| WARNING constant evaluation error + //~| NOTE on by default = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs index 3c688d58fd195..058a8d0a1bd4f 100644 --- a/src/test/compile-fail/const-eval-overflow.rs +++ b/src/test/compile-fail/const-eval-overflow.rs @@ -82,7 +82,7 @@ const VALS_I64: (i64, i64, i64, i64) = ); const VALS_U8: (u8, u8, u8, u8) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow. + ( //~ WARN constant evaluation error: attempt to subtract with overflow -(u8::MIN as i8) as u8, u8::MIN - 1, //~^ ERROR constant evaluation error @@ -96,7 +96,7 @@ const VALS_U8: (u8, u8, u8, u8) = ); const VALS_U16: (u16, u16, u16, u16) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow. + ( //~ WARN constant evaluation error: attempt to subtract with overflow -(u16::MIN as i16) as u16, u16::MIN - 1, //~^ ERROR constant evaluation error @@ -110,7 +110,7 @@ const VALS_U16: (u16, u16, u16, u16) = ); const VALS_U32: (u32, u32, u32, u32) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow. + ( //~ WARN constant evaluation error: attempt to subtract with overflow -(u32::MIN as i32) as u32, u32::MIN - 1, //~^ ERROR constant evaluation error @@ -124,7 +124,7 @@ const VALS_U32: (u32, u32, u32, u32) = ); const VALS_U64: (u64, u64, u64, u64) = - ( //~ WARN constant evaluation error: attempt to subtract with overflow. + ( //~ WARN constant evaluation error: attempt to subtract with overflow -(u64::MIN as i64) as u64, u64::MIN - 1, //~^ ERROR constant evaluation error diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs index 385daef44dfe5..baf836b4dad1b 100644 --- a/src/test/compile-fail/const-fn-error.rs +++ b/src/test/compile-fail/const-fn-error.rs @@ -13,8 +13,9 @@ const X : usize = 2; const fn f(x: usize) -> usize { - let mut sum = 0; - for i in 0..x { + let mut sum = 0; //~ ERROR blocks in constant functions are limited + for i in 0..x { //~ ERROR calls in constant functions + //~| ERROR constant function contains unimplemented sum += i; } sum //~ ERROR E0080 @@ -24,4 +25,6 @@ const fn f(x: usize) -> usize { #[allow(unused_variables)] fn main() { let a : [i32; f(X)]; //~ NOTE for constant expression here + //~| WARNING constant evaluation error: non-constant path + //~| on by default } diff --git a/src/test/compile-fail/const-len-underflow-separate-spans.rs b/src/test/compile-fail/const-len-underflow-separate-spans.rs index 3c84810554214..eaad9e7e92bab 100644 --- a/src/test/compile-fail/const-len-underflow-separate-spans.rs +++ b/src/test/compile-fail/const-len-underflow-separate-spans.rs @@ -17,6 +17,8 @@ const TWO: usize = 2; const LEN: usize = ONE - TWO; //~^ ERROR E0080 //~| attempt to subtract with overflow +//~| NOTE attempt to subtract with overflow +//~| NOTE on by default fn main() { let a: [i8; LEN] = unimplemented!(); diff --git a/src/test/compile-fail/const-match-check.rs b/src/test/compile-fail/const-match-check.rs new file mode 100644 index 0000000000000..36a6600b62d98 --- /dev/null +++ b/src/test/compile-fail/const-match-check.rs @@ -0,0 +1,44 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: matchck eval1 eval2 + +#[cfg(matchck)] +const X: i32 = { let 0 = 0; 0 }; +//[matchck]~^ ERROR refutable pattern in local binding + +#[cfg(matchck)] +static Y: i32 = { let 0 = 0; 0 }; +//[matchck]~^ ERROR refutable pattern in local binding + +#[cfg(matchck)] +trait Bar { + const X: i32 = { let 0 = 0; 0 }; + //[matchck]~^ ERROR refutable pattern in local binding +} + +#[cfg(matchck)] +impl Bar for () { + const X: i32 = { let 0 = 0; 0 }; + //[matchck]~^ ERROR refutable pattern in local binding +} + +#[cfg(eval1)] +enum Foo { + A = { let 0 = 0; 0 }, + //[eval1]~^ ERROR refutable pattern in local binding +} + +fn main() { + #[cfg(eval2)] + let x: [i32; { let 0 = 0; 0 }] = []; + //[eval2]~^ ERROR refutable pattern in local binding + //[eval2]~| ERROR constant evaluation error +} diff --git a/src/test/compile-fail/cycle-trait-default-type-trait.rs b/src/test/compile-fail/cycle-trait-default-type-trait.rs index 6825572b26c83..e6caeb34a8c8f 100644 --- a/src/test/compile-fail/cycle-trait-default-type-trait.rs +++ b/src/test/compile-fail/cycle-trait-default-type-trait.rs @@ -13,7 +13,6 @@ trait Foo> { //~^ ERROR unsupported cyclic reference - //~| ERROR unsupported cyclic reference } fn main() { } diff --git a/src/test/compile-fail/deprecation-lint.rs b/src/test/compile-fail/deprecation-lint.rs index edee24206cd33..a058234a64921 100644 --- a/src/test/compile-fail/deprecation-lint.rs +++ b/src/test/compile-fail/deprecation-lint.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:deprecation-lint.rs +// ignore-tidy-linelength #![deny(deprecated)] #![allow(warnings)] @@ -23,76 +24,86 @@ mod cross_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ ERROR use of deprecated item - foo.method_deprecated(); //~ ERROR use of deprecated item - Foo::method_deprecated(&foo); //~ ERROR use of deprecated item - ::method_deprecated(&foo); //~ ERROR use of deprecated item - foo.trait_deprecated(); //~ ERROR use of deprecated item - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - - deprecated_text(); //~ ERROR use of deprecated item: text - foo.method_deprecated_text(); //~ ERROR use of deprecated item: text - Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - - let _ = DeprecatedStruct { //~ ERROR use of deprecated item - i: 0 //~ ERROR use of deprecated item + deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated' + foo.method_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' + Foo::method_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' + ::method_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' + foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + + deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text + foo.method_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + + let _ = DeprecatedStruct { //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedStruct': text + i: 0 //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedStruct::i': text }; - let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedUnitStruct': text - let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'deprecation_lint::Enum::DeprecatedVariant': text - let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTupleStruct': text + + let _ = nested::DeprecatedStruct { //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedStruct': text + i: 0 //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedStruct::i': text + }; + + let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedUnitStruct': text + + let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'deprecation_lint::nested::Enum::DeprecatedVariant': text + + let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedTupleStruct': text // At the moment, the lint checker only checks stability in // in the arguments of macros. // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels // on macros themselves are not yet linted. - macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item: text - macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item: text + macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text + macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ ERROR use of deprecated item - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ ERROR use of deprecated item - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text } struct S; - impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text - trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item: text + impl DeprecatedTrait for S {} //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTrait': text + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTrait': text pub fn foo() { let x = Stable { override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text }; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text let Stable { override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text } = x; // all fine let Stable { .. } = x; @@ -100,56 +111,56 @@ mod cross_crate { let x = Stable2(1, 2, 3); let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Stable2::2': text let Stable2(_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Stable2::2': text = x; // all fine let Stable2(..) = x; let x = Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text inherit: 1, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text }; let _ = x.inherit; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text let Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text inherit: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text } = x; let Deprecated - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text let _ = x.0; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::0': text let _ = x.1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::1': text let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::2': text let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text (_, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::0': text _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::1': text _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::2': text = x; let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text // the patterns are all fine: (..) = x; } @@ -159,7 +170,7 @@ mod inheritance { use deprecation_lint::*; fn test_inheritance() { - deprecated_mod::deprecated(); //~ ERROR use of deprecated item + deprecated_mod::deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_mod::deprecated': text } } @@ -209,7 +220,27 @@ mod this_crate { #[deprecated(since = "1.0.0", note = "text")] pub struct DeprecatedTupleStruct(isize); + mod nested { + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedStruct { + i: isize + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedUnitStruct; + + pub enum Enum { + #[deprecated(since = "1.0.0", note = "text")] + DeprecatedVariant, + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedTupleStruct(pub isize); + } + fn test() { + use self::nested; + // Only the deprecated cases of the following should generate // errors, because other stability attributes now have meaning // only *across* crates, not within a single crate. @@ -217,50 +248,61 @@ mod this_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ ERROR use of deprecated item - foo.method_deprecated(); //~ ERROR use of deprecated item - Foo::method_deprecated(&foo); //~ ERROR use of deprecated item - ::method_deprecated(&foo); //~ ERROR use of deprecated item - foo.trait_deprecated(); //~ ERROR use of deprecated item - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - - deprecated_text(); //~ ERROR use of deprecated item: text - foo.method_deprecated_text(); //~ ERROR use of deprecated item: text - Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + deprecated(); //~ ERROR use of deprecated item 'this_crate::deprecated' + foo.method_deprecated(); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' + Foo::method_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' + ::method_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' + foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + + deprecated_text(); //~ ERROR use of deprecated item 'this_crate::deprecated_text': text + foo.method_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text let _ = DeprecatedStruct { - //~^ ERROR use of deprecated item - i: 0 //~ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate::DeprecatedStruct': text + i: 0 //~ ERROR use of deprecated item 'this_crate::DeprecatedStruct::i': text + }; + + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item 'this_crate::DeprecatedUnitStruct': text + + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'this_crate::Enum::DeprecatedVariant': text + + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'this_crate::DeprecatedTupleStruct': text + + let _ = nested::DeprecatedStruct { + //~^ ERROR use of deprecated item 'this_crate::nested::DeprecatedStruct': text + i: 0 //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedStruct::i': text }; - let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item + let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedUnitStruct': text - let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item + let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'this_crate::nested::Enum::DeprecatedVariant': text - let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item + let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedTupleStruct': text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ ERROR use of deprecated item - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - ::trait_deprecated(&foo); //~ ERROR use of deprecated item - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ ERROR use of deprecated item - foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text } #[deprecated(since = "1.0.0", note = "text")] @@ -269,6 +311,14 @@ mod this_crate { fn_in_body(); } + fn test_fn_closure_body() { + let _ = || { + #[deprecated] + fn bar() { } + bar(); //~ ERROR use of deprecated item 'this_crate::test_fn_closure_body::{{closure}}::bar' + }; + } + impl MethodTester { #[deprecated(since = "1.0.0", note = "text")] fn test_method_body(&self) { @@ -284,9 +334,9 @@ mod this_crate { struct S; - impl DeprecatedTrait for S { } //~ ERROR use of deprecated item + impl DeprecatedTrait for S { } //~ ERROR use of deprecated item 'this_crate::DeprecatedTrait': text - trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item 'this_crate::DeprecatedTrait': text } mod this_crate2 { @@ -312,15 +362,15 @@ mod this_crate2 { pub fn foo() { let x = Stable { override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text }; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text let Stable { override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text } = x; // all fine let Stable { .. } = x; @@ -328,57 +378,57 @@ mod this_crate2 { let x = Stable2(1, 2, 3); let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Stable2::2': text let Stable2(_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Stable2::2': text = x; // all fine let Stable2(..) = x; let x = Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text inherit: 1, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text }; let _ = x.inherit; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text let Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text inherit: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text } = x; let Deprecated - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text // the patterns are all fine: { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text let _ = x.0; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::0': text let _ = x.1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::1': text let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::2': text let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text (_, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::0': text _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::1': text _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::2': text = x; let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text // the patterns are all fine: (..) = x; } diff --git a/src/test/compile-fail/derives-span-Clone-enum-struct-variant.rs b/src/test/compile-fail/derives-span-Clone-enum-struct-variant.rs index 0b73f5bebb23c..244acbf660533 100644 --- a/src/test/compile-fail/derives-span-Clone-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-Clone-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Clone-enum.rs b/src/test/compile-fail/derives-span-Clone-enum.rs index 6944ea38b3726..785a3d3543088 100644 --- a/src/test/compile-fail/derives-span-Clone-enum.rs +++ b/src/test/compile-fail/derives-span-Clone-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Clone-struct.rs b/src/test/compile-fail/derives-span-Clone-struct.rs index 92bf148ccbd94..b1b1dc7bed162 100644 --- a/src/test/compile-fail/derives-span-Clone-struct.rs +++ b/src/test/compile-fail/derives-span-Clone-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Clone-tuple-struct.rs b/src/test/compile-fail/derives-span-Clone-tuple-struct.rs index 21adfc90301b8..d56e21b9a8af2 100644 --- a/src/test/compile-fail/derives-span-Clone-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Clone-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Debug-enum-struct-variant.rs b/src/test/compile-fail/derives-span-Debug-enum-struct-variant.rs index da777e8a14b45..4c25e482c2a68 100644 --- a/src/test/compile-fail/derives-span-Debug-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-Debug-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Debug-enum.rs b/src/test/compile-fail/derives-span-Debug-enum.rs index bf5d3f2d81b29..0cb02aa54e69d 100644 --- a/src/test/compile-fail/derives-span-Debug-enum.rs +++ b/src/test/compile-fail/derives-span-Debug-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Debug-struct.rs b/src/test/compile-fail/derives-span-Debug-struct.rs index b0b275fa2d347..33fa82355ec67 100644 --- a/src/test/compile-fail/derives-span-Debug-struct.rs +++ b/src/test/compile-fail/derives-span-Debug-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Debug-tuple-struct.rs b/src/test/compile-fail/derives-span-Debug-tuple-struct.rs index 9689054a7be6d..760ed199f6abd 100644 --- a/src/test/compile-fail/derives-span-Debug-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Debug-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Default-struct.rs b/src/test/compile-fail/derives-span-Default-struct.rs index 68b99ed25b855..4adfe75adaf91 100644 --- a/src/test/compile-fail/derives-span-Default-struct.rs +++ b/src/test/compile-fail/derives-span-Default-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -15,7 +15,7 @@ struct Error; #[derive(Default)] struct Struct { - x: Error //~ ERROR `Error: std::default::Default` is not satisfied + x: Error //~ ERROR } fn main() {} diff --git a/src/test/compile-fail/derives-span-Default-tuple-struct.rs b/src/test/compile-fail/derives-span-Default-tuple-struct.rs index 822abe975a1cc..a5e3a7cd49f81 100644 --- a/src/test/compile-fail/derives-span-Default-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Default-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Eq-enum-struct-variant.rs b/src/test/compile-fail/derives-span-Eq-enum-struct-variant.rs index fdc74d5fef6b1..6abd1d31e6615 100644 --- a/src/test/compile-fail/derives-span-Eq-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-Eq-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Eq-enum.rs b/src/test/compile-fail/derives-span-Eq-enum.rs index 4bf30fdf93f77..f361278a620f4 100644 --- a/src/test/compile-fail/derives-span-Eq-enum.rs +++ b/src/test/compile-fail/derives-span-Eq-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Eq-struct.rs b/src/test/compile-fail/derives-span-Eq-struct.rs index 685188f133786..7067caa6d5cca 100644 --- a/src/test/compile-fail/derives-span-Eq-struct.rs +++ b/src/test/compile-fail/derives-span-Eq-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Eq-tuple-struct.rs b/src/test/compile-fail/derives-span-Eq-tuple-struct.rs index 0e636d027dd37..1a09628b77091 100644 --- a/src/test/compile-fail/derives-span-Eq-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Eq-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Hash-enum-struct-variant.rs b/src/test/compile-fail/derives-span-Hash-enum-struct-variant.rs index bfb6566223cb3..907045cce47d5 100644 --- a/src/test/compile-fail/derives-span-Hash-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-Hash-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Hash-enum.rs b/src/test/compile-fail/derives-span-Hash-enum.rs index 99f28b376dfe9..321b9e71a0f29 100644 --- a/src/test/compile-fail/derives-span-Hash-enum.rs +++ b/src/test/compile-fail/derives-span-Hash-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Hash-struct.rs b/src/test/compile-fail/derives-span-Hash-struct.rs index acfd5aa7b2a74..7f69c3a8e2565 100644 --- a/src/test/compile-fail/derives-span-Hash-struct.rs +++ b/src/test/compile-fail/derives-span-Hash-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Hash-tuple-struct.rs b/src/test/compile-fail/derives-span-Hash-tuple-struct.rs index 3d76b29834f09..2dee63c4298d0 100644 --- a/src/test/compile-fail/derives-span-Hash-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Hash-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Ord-enum-struct-variant.rs b/src/test/compile-fail/derives-span-Ord-enum-struct-variant.rs index 06ee588e69f49..8f4e393c96a41 100644 --- a/src/test/compile-fail/derives-span-Ord-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-Ord-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Ord-enum.rs b/src/test/compile-fail/derives-span-Ord-enum.rs index af9cfbc911097..b8ceacf3753ef 100644 --- a/src/test/compile-fail/derives-span-Ord-enum.rs +++ b/src/test/compile-fail/derives-span-Ord-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Ord-struct.rs b/src/test/compile-fail/derives-span-Ord-struct.rs index 4477d933a6c7a..2ff62bac2bce1 100644 --- a/src/test/compile-fail/derives-span-Ord-struct.rs +++ b/src/test/compile-fail/derives-span-Ord-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-Ord-tuple-struct.rs b/src/test/compile-fail/derives-span-Ord-tuple-struct.rs index ebc7518641289..24eacb71d7b4a 100644 --- a/src/test/compile-fail/derives-span-Ord-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-Ord-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-PartialEq-enum-struct-variant.rs b/src/test/compile-fail/derives-span-PartialEq-enum-struct-variant.rs index 7c98dcc2a6f1d..14d94f1599e5f 100644 --- a/src/test/compile-fail/derives-span-PartialEq-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-PartialEq-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-PartialEq-enum.rs b/src/test/compile-fail/derives-span-PartialEq-enum.rs index fe6355e456cca..ab58bb938b9dd 100644 --- a/src/test/compile-fail/derives-span-PartialEq-enum.rs +++ b/src/test/compile-fail/derives-span-PartialEq-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-PartialEq-struct.rs b/src/test/compile-fail/derives-span-PartialEq-struct.rs index 10d9d64277683..05a0990ff035a 100644 --- a/src/test/compile-fail/derives-span-PartialEq-struct.rs +++ b/src/test/compile-fail/derives-span-PartialEq-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-PartialEq-tuple-struct.rs b/src/test/compile-fail/derives-span-PartialEq-tuple-struct.rs index c92eb0f63c4da..cdeb7ce45bc4c 100644 --- a/src/test/compile-fail/derives-span-PartialEq-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-PartialEq-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/compile-fail/derives-span-PartialOrd-enum-struct-variant.rs b/src/test/compile-fail/derives-span-PartialOrd-enum-struct-variant.rs index 898104d0ab29c..cf3d69bc16c43 100644 --- a/src/test/compile-fail/derives-span-PartialOrd-enum-struct-variant.rs +++ b/src/test/compile-fail/derives-span-PartialOrd-enum-struct-variant.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -19,12 +19,6 @@ enum Enum { x: Error //~ ERROR //~^ ERROR //~^^ ERROR -//~^^^ ERROR -//~^^^^ ERROR -//~^^^^^ ERROR -//~^^^^^^ ERROR -//~^^^^^^^ ERROR -//~^^^^^^^^ ERROR } } diff --git a/src/test/compile-fail/derives-span-PartialOrd-enum.rs b/src/test/compile-fail/derives-span-PartialOrd-enum.rs index c0585999473b5..c4d587237a52f 100644 --- a/src/test/compile-fail/derives-span-PartialOrd-enum.rs +++ b/src/test/compile-fail/derives-span-PartialOrd-enum.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -19,12 +19,6 @@ enum Enum { Error //~ ERROR //~^ ERROR //~^^ ERROR -//~^^^ ERROR -//~^^^^ ERROR -//~^^^^^ ERROR -//~^^^^^^ ERROR -//~^^^^^^^ ERROR -//~^^^^^^^^ ERROR ) } diff --git a/src/test/compile-fail/derives-span-PartialOrd-struct.rs b/src/test/compile-fail/derives-span-PartialOrd-struct.rs index af05434af9de3..e065abd9b46a2 100644 --- a/src/test/compile-fail/derives-span-PartialOrd-struct.rs +++ b/src/test/compile-fail/derives-span-PartialOrd-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -18,12 +18,6 @@ struct Struct { x: Error //~ ERROR //~^ ERROR //~^^ ERROR -//~^^^ ERROR -//~^^^^ ERROR -//~^^^^^ ERROR -//~^^^^^^ ERROR -//~^^^^^^^ ERROR -//~^^^^^^^^ ERROR } fn main() {} diff --git a/src/test/compile-fail/derives-span-PartialOrd-tuple-struct.rs b/src/test/compile-fail/derives-span-PartialOrd-tuple-struct.rs index 1afb7bc2b4c47..f2df01222b989 100644 --- a/src/test/compile-fail/derives-span-PartialOrd-tuple-struct.rs +++ b/src/test/compile-fail/derives-span-PartialOrd-tuple-struct.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -18,12 +18,6 @@ struct Struct( Error //~ ERROR //~^ ERROR //~^^ ERROR -//~^^^ ERROR -//~^^^^ ERROR -//~^^^^^ ERROR -//~^^^^^^ ERROR -//~^^^^^^^ ERROR -//~^^^^^^^^ ERROR ); fn main() {} diff --git a/src/test/compile-fail/diverging-fn-tail-35849.rs b/src/test/compile-fail/diverging-fn-tail-35849.rs index 3a27c08413328..a91c000bbf712 100644 --- a/src/test/compile-fail/diverging-fn-tail-35849.rs +++ b/src/test/compile-fail/diverging-fn-tail-35849.rs @@ -8,9 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn _converge() -> ! { - 42 //~ ERROR mismatched types +#[deny(coerce_never)] +fn assert_sizeof() -> ! { + unsafe { + ::std::mem::transmute::(panic!()) + //~^ ERROR cannot coerce `[u8; 8]` to ! + //~| hard error + } } fn main() { } - diff --git a/src/test/compile-fail/dollar-crate-is-keyword-2.rs b/src/test/compile-fail/dollar-crate-is-keyword-2.rs index ac96279d61467..87a2903803581 100644 --- a/src/test/compile-fail/dollar-crate-is-keyword-2.rs +++ b/src/test/compile-fail/dollar-crate-is-keyword-2.rs @@ -13,8 +13,8 @@ mod a {} macro_rules! m { () => { use a::$crate; //~ ERROR unresolved import `a::$crate` - use a::$crate::b; //~ ERROR unresolved import `a::$crate` - type A = a::$crate; //~ ERROR cannot find type `$crate` in module `a` + use a::$crate::b; //~ ERROR `$crate` in paths can only be used in start position + type A = a::$crate; //~ ERROR `$crate` in paths can only be used in start position } } diff --git a/src/test/compile-fail/dropck_trait_cycle_checked.rs b/src/test/compile-fail/dropck_trait_cycle_checked.rs index c0f0e3650d9fb..b6b7fa1a233d7 100644 --- a/src/test/compile-fail/dropck_trait_cycle_checked.rs +++ b/src/test/compile-fail/dropck_trait_cycle_checked.rs @@ -13,8 +13,6 @@ // // (Compare against compile-fail/dropck_vec_cycle_checked.rs) -#![feature(const_atomic_usize_new)] - use std::cell::Cell; use id::Id; diff --git a/src/test/compile-fail/dupe-symbols-2.rs b/src/test/compile-fail/dupe-symbols-2.rs index 976a65589b869..1f19bd2f249b3 100644 --- a/src/test/compile-fail/dupe-symbols-2.rs +++ b/src/test/compile-fail/dupe-symbols-2.rs @@ -11,13 +11,13 @@ #![crate_type="rlib"] #![allow(warnings)] -mod a { +pub mod a { #[no_mangle] pub extern fn fail() { } } -mod b { +pub mod b { #[no_mangle] pub extern fn fail() { //~^ symbol `fail` is already defined diff --git a/src/test/compile-fail/dyn-trait-compatibility.rs b/src/test/compile-fail/dyn-trait-compatibility.rs new file mode 100644 index 0000000000000..a7cfda504c753 --- /dev/null +++ b/src/test/compile-fail/dyn-trait-compatibility.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +type A0 = dyn; +//~^ ERROR cannot find type `dyn` in this scope +type A1 = dyn::dyn; +//~^ ERROR Use of undeclared type or module `dyn` +type A2 = dyn; +//~^ ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope +type A3 = dyn<::dyn>; +//~^ ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope +//~| ERROR Use of undeclared type or module `dyn` +type A4 = dyn(dyn, dyn) -> dyn; +//~^ ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope +//~| ERROR cannot find type `dyn` in this scope + +fn main() {} diff --git a/src/test/compile-fail/empty-struct-braces-expr.rs b/src/test/compile-fail/empty-struct-braces-expr.rs index d4e85e9744d64..3096e8f831368 100644 --- a/src/test/compile-fail/empty-struct-braces-expr.rs +++ b/src/test/compile-fail/empty-struct-braces-expr.rs @@ -29,6 +29,6 @@ fn main() { let xe1 = XEmpty1; //~ ERROR expected value, found struct `XEmpty1` let xe1 = XEmpty1(); //~ ERROR expected function, found struct `XEmpty1` - let xe3 = XE::Empty3; //~ ERROR no associated item named `Empty3` found for type - let xe3 = XE::Empty3(); //~ ERROR no associated item named `Empty3` found for type + let xe3 = XE::Empty3; //~ ERROR no variant named `Empty3` found for type + let xe3 = XE::Empty3(); //~ ERROR no variant named `Empty3` found for type } diff --git a/src/test/compile-fail/extern-types-distinct-types.rs b/src/test/compile-fail/extern-types-distinct-types.rs new file mode 100644 index 0000000000000..8b434bbfc6d33 --- /dev/null +++ b/src/test/compile-fail/extern-types-distinct-types.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(extern_types)] + +extern { + type A; + type B; +} + +fn foo(r: &A) -> &B { + r //~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/compile-fail/extern-types-not-sync-send.rs b/src/test/compile-fail/extern-types-not-sync-send.rs new file mode 100644 index 0000000000000..2f00cf812e473 --- /dev/null +++ b/src/test/compile-fail/extern-types-not-sync-send.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure extern types are !Sync and !Send. + +#![feature(extern_types)] + +extern { + type A; +} + +fn assert_sync() { } +fn assert_send() { } + +fn main() { + assert_sync::(); + //~^ ERROR the trait bound `A: std::marker::Sync` is not satisfied + + assert_send::(); + //~^ ERROR the trait bound `A: std::marker::Send` is not satisfied +} diff --git a/src/test/compile-fail/extern-types-unsized.rs b/src/test/compile-fail/extern-types-unsized.rs new file mode 100644 index 0000000000000..faa27894806f8 --- /dev/null +++ b/src/test/compile-fail/extern-types-unsized.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure extern types are !Sized. + +#![feature(extern_types)] + +extern { + type A; +} + +struct Foo { + x: u8, + tail: A, +} + +struct Bar { + x: u8, + tail: T, +} + +fn assert_sized() { } + +fn main() { + assert_sized::(); + //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + + assert_sized::(); + //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + + assert_sized::>(); + //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + + assert_sized::>>(); + //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied +} diff --git a/src/test/compile-fail/feature-gate-arbitrary-self-types.rs b/src/test/compile-fail/feature-gate-arbitrary-self-types.rs new file mode 100644 index 0000000000000..ff0306f199310 --- /dev/null +++ b/src/test/compile-fail/feature-gate-arbitrary-self-types.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rc::Rc; + +trait Foo { + fn foo(self: Rc>); //~ ERROR arbitrary `self` types are unstable +} + +struct Bar; + +impl Foo for Bar { + fn foo(self: Rc>) {} //~ ERROR arbitrary `self` types are unstable +} + +impl Bar { + fn bar(self: Box>) {} //~ ERROR arbitrary `self` types are unstable +} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-crate_visibility_modifier.rs b/src/test/compile-fail/feature-gate-crate_visibility_modifier.rs new file mode 100644 index 0000000000000..a2937d6de31f3 --- /dev/null +++ b/src/test/compile-fail/feature-gate-crate_visibility_modifier.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +crate struct Bender { //~ ERROR `crate` visibility modifier is experimental + earth: bool, + fire: bool, + air: bool, + water: bool, +} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-doc_spotlight.rs b/src/test/compile-fail/feature-gate-doc_spotlight.rs new file mode 100644 index 0000000000000..6369358538d50 --- /dev/null +++ b/src/test/compile-fail/feature-gate-doc_spotlight.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[doc(spotlight)] //~ ERROR: #[doc(spotlight)] is experimental +trait SomeTrait {} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-dyn-trait.rs b/src/test/compile-fail/feature-gate-dyn-trait.rs new file mode 100644 index 0000000000000..4b3803d019baa --- /dev/null +++ b/src/test/compile-fail/feature-gate-dyn-trait.rs @@ -0,0 +1,14 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Trait {} +type A = Box; //~ ERROR `dyn Trait` syntax is unstable + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-extern_types.rs b/src/test/compile-fail/feature-gate-extern_types.rs new file mode 100644 index 0000000000000..1203b598df3c2 --- /dev/null +++ b/src/test/compile-fail/feature-gate-extern_types.rs @@ -0,0 +1,15 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern { + type T; //~ ERROR extern types are experimental +} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-external_doc.rs b/src/test/compile-fail/feature-gate-external_doc.rs new file mode 100644 index 0000000000000..fa0a2a29078c5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-external_doc.rs @@ -0,0 +1,12 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[doc(include="asdf.md")] //~ ERROR: #[doc(include = "...")] is experimental +fn main() {} diff --git a/src/test/compile-fail/feature-gate-generic_associated_types.rs b/src/test/compile-fail/feature-gate-generic_associated_types.rs new file mode 100644 index 0000000000000..724ec2496f24c --- /dev/null +++ b/src/test/compile-fail/feature-gate-generic_associated_types.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +trait PointerFamily { + type Pointer: Deref; + //~^ ERROR generic associated types are unstable + type Pointer2: Deref where T: Clone, U: Clone; + //~^ ERROR generic associated types are unstable +} + +struct Foo; +impl PointerFamily for Foo { + type Pointer = Box; + //~^ ERROR generic associated types are unstable + type Pointer2 = Box; + //~^ ERROR generic associated types are unstable +} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-in_band_lifetimes.rs b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs new file mode 100644 index 0000000000000..ae1f81c2f5721 --- /dev/null +++ b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs @@ -0,0 +1,72 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] + +fn foo(x: &'x u8) -> &'x u8 { x } +//~^ ERROR use of undeclared lifetime name +//~^^ ERROR use of undeclared lifetime name + +struct X<'a>(&'a u8); + +impl<'a> X<'a> { + fn inner(&self) -> &'a u8 { + self.0 + } +} + +impl<'a> X<'b> { +//~^ ERROR use of undeclared lifetime name + fn inner_2(&self) -> &'b u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +impl X<'b> { +//~^ ERROR use of undeclared lifetime name + fn inner_3(&self) -> &'b u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +struct Y(T); + +impl Y<&'a u8> { + //~^ ERROR use of undeclared lifetime name + fn inner(&self) -> &'a u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +trait MyTrait<'a> { + fn my_lifetime(&self) -> &'a u8; + fn any_lifetime() -> &'b u8; + //~^ ERROR use of undeclared lifetime name + fn borrowed_lifetime(&'b self) -> &'b u8; + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR use of undeclared lifetime name +} + +impl MyTrait<'a> for Y<&'a u8> { +//~^ ERROR use of undeclared lifetime name +//~^^ ERROR use of undeclared lifetime name + fn my_lifetime(&self) -> &'a u8 { self.0 } + //~^ ERROR use of undeclared lifetime name + fn any_lifetime() -> &'b u8 { &0 } + //~^ ERROR use of undeclared lifetime name + fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR use of undeclared lifetime name +} + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-match_default_bindings.rs b/src/test/compile-fail/feature-gate-match_default_bindings.rs new file mode 100644 index 0000000000000..4ee2c1e2936a8 --- /dev/null +++ b/src/test/compile-fail/feature-gate-match_default_bindings.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn main() { + match &Some(3) { + Some(n) => {}, + //~^ ERROR non-reference pattern used to match a reference + _ => panic!(), + } +} diff --git a/src/test/compile-fail/feature-gate-no-debug.rs b/src/test/compile-fail/feature-gate-no-debug.rs index 9815db6550d66..d21493de50a7f 100644 --- a/src/test/compile-fail/feature-gate-no-debug.rs +++ b/src/test/compile-fail/feature-gate-no-debug.rs @@ -10,5 +10,5 @@ #![allow(deprecated)] -#[no_debug] //~ ERROR the `#[no_debug]` attribute is +#[no_debug] //~ ERROR the `#[no_debug]` attribute was fn main() {} diff --git a/src/librustc_back/slice.rs b/src/test/compile-fail/feature-gate-non_exhaustive.rs similarity index 68% rename from src/librustc_back/slice.rs rename to src/test/compile-fail/feature-gate-non_exhaustive.rs index 5d8fc3acefd6f..d2711084a4d48 100644 --- a/src/librustc_back/slice.rs +++ b/src/test/compile-fail/feature-gate-non_exhaustive.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem; +//#![feature(non_exhaustive)] -pub fn ref_slice(ptr: &T) -> &[T; 1] { - unsafe { mem::transmute(ptr) } +#[non_exhaustive] //~ERROR non exhaustive is an experimental feature (see issue #44109) +pub enum NonExhaustiveEnum { + Unit, + Tuple(u32), + Struct { field: u32 } } -pub fn mut_ref_slice(ptr: &mut T) -> &mut [T; 1] { - unsafe { mem::transmute(ptr) } -} +fn main() { } diff --git a/src/test/compile-fail/feature-gate-optin-builtin-traits.rs b/src/test/compile-fail/feature-gate-optin-builtin-traits.rs index 59d7473a741d6..4c5502cec18a8 100644 --- a/src/test/compile-fail/feature-gate-optin-builtin-traits.rs +++ b/src/test/compile-fail/feature-gate-optin-builtin-traits.rs @@ -17,8 +17,12 @@ trait DummyTrait { fn dummy(&self) {} } +auto trait AutoDummyTrait {} +//~^ ERROR auto traits are experimental and possibly buggy + +#[allow(auto_impl)] impl DummyTrait for .. {} -//~^ ERROR default trait implementations are experimental and possibly buggy +//~^ ERROR auto trait implementations are experimental and possibly buggy impl !DummyTrait for DummyStruct {} //~^ ERROR negative trait bounds are not yet fully implemented; use marker types for now diff --git a/src/test/compile-fail/feature-gate-use_nested_groups.rs b/src/test/compile-fail/feature-gate-use_nested_groups.rs new file mode 100644 index 0000000000000..56413a999d7f7 --- /dev/null +++ b/src/test/compile-fail/feature-gate-use_nested_groups.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_imports, dead_code)] + +mod a { + pub enum B {} + pub enum C {} + + pub mod d { + pub enum E {} + pub enum F {} + + pub mod g { + pub enum H {} + } + } +} + +use a::{B, d::{*, g::H}}; //~ ERROR glob imports in `use` groups are experimental + //~^ ERROR nested groups in `use` are experimental + //~^^ ERROR paths in `use` groups are experimental + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-wasm_import_memory.rs b/src/test/compile-fail/feature-gate-wasm_import_memory.rs new file mode 100644 index 0000000000000..a010ebb3551d0 --- /dev/null +++ b/src/test/compile-fail/feature-gate-wasm_import_memory.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![wasm_import_memory] //~ ERROR: currently unstable + +fn main() {} + diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs index 06b8720666953..ab2fe02bb1476 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs @@ -424,7 +424,7 @@ mod no_mangle { mod inner { #![no_mangle="3500"] } #[no_mangle = "3500"] fn f() { } - //~^ WARN function f is marked #[no_mangle], but not exported + //~^ WARN function is marked #[no_mangle], but not exported #[no_mangle = "3500"] struct S; diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-rustc_deprecated.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-rustc_deprecated.rs index 4709ec2bc579b..10c1398634923 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-rustc_deprecated.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-rustc_deprecated.rs @@ -28,7 +28,6 @@ mod rustc_deprecated { #[rustc_deprecated = "1500"] struct S; //~^ ERROR stability attributes may not be used outside of the standard library - //~| ERROR stability attributes may not be used outside of the standard library #[rustc_deprecated = "1500"] type T = S; //~^ ERROR stability attributes may not be used outside of the standard library diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-stable.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-stable.rs index 9627d32d42aad..a6eaabf7a383e 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-stable.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-stable.rs @@ -28,7 +28,6 @@ mod stable { #[stable = "1300"] struct S; //~^ ERROR stability attributes may not be used outside of the standard library - //~| ERROR stability attributes may not be used outside of the standard library #[stable = "1300"] type T = S; //~^ ERROR stability attributes may not be used outside of the standard library diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-unstable.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-unstable.rs index 0708dc8f728e8..ff0600deb1936 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-unstable.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-unstable.rs @@ -28,7 +28,6 @@ mod unstable { #[unstable = "1200"] struct S; //~^ ERROR stability attributes may not be used outside of the standard library - //~| ERROR stability attributes may not be used outside of the standard library #[unstable = "1200"] type T = S; //~^ ERROR stability attributes may not be used outside of the standard library diff --git a/src/test/compile-fail/float-int-invalid-const-cast.rs b/src/test/compile-fail/float-int-invalid-const-cast.rs new file mode 100644 index 0000000000000..2efefd926919f --- /dev/null +++ b/src/test/compile-fail/float-int-invalid-const-cast.rs @@ -0,0 +1,61 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(i128_type)] +#![allow(const_err)] // this test is only about hard errors + +use std::{f32, f64}; + +// Forces evaluation of constants, triggering hard error +fn force(_: T) {} + +fn main() { + { const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error + { const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error + { const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error + { const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error + { const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error + { const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error + { const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error + + { const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error + { const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error + { const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error + { const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error + { const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error + { const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error + + { const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error + { const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error + { const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error + { const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error + { const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error + { const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error + { const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error + { const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error +} \ No newline at end of file diff --git a/src/test/compile-fail/generator-yielding-or-returning-itself.rs b/src/test/compile-fail/generator-yielding-or-returning-itself.rs new file mode 100644 index 0000000000000..13abdf616b29b --- /dev/null +++ b/src/test/compile-fail/generator-yielding-or-returning-itself.rs @@ -0,0 +1,45 @@ +// 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. + +#![feature(generator_trait)] +#![feature(generators)] + +// Test that we cannot create a generator that returns a value of its +// own type. + +use std::ops::Generator; + +pub fn want_cyclic_generator_return(_: T) + where T: Generator +{ +} + +fn supply_cyclic_generator_return() { + want_cyclic_generator_return(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +pub fn want_cyclic_generator_yield(_: T) + where T: Generator +{ +} + +fn supply_cyclic_generator_yield() { + want_cyclic_generator_yield(|| { + //~^ ERROR type mismatch + if false { yield None.unwrap(); } + None.unwrap() + }) +} + +fn main() { } diff --git a/src/test/compile-fail/hrtb-identity-fn-borrows.rs b/src/test/compile-fail/hrtb-identity-fn-borrows.rs index b6216ce058915..5f5b70dda5e81 100644 --- a/src/test/compile-fail/hrtb-identity-fn-borrows.rs +++ b/src/test/compile-fail/hrtb-identity-fn-borrows.rs @@ -12,7 +12,7 @@ // of the output to the region of the input. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir trait FnLike { fn call(&self, arg: A) -> R; @@ -25,8 +25,7 @@ fn call_repeatedly(f: F) let mut x = 3; let y = f.call(&x); x = 5; //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `x` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `x` because it is borrowed // Result is not stored: can re-assign `x` let mut x = 3; diff --git a/src/test/compile-fail/hygiene/assoc_item_ctxt.rs b/src/test/compile-fail/hygiene/assoc_item_ctxt.rs new file mode 100644 index 0000000000000..e336b0df13fea --- /dev/null +++ b/src/test/compile-fail/hygiene/assoc_item_ctxt.rs @@ -0,0 +1,52 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] +#![allow(unused)] + +mod ok { + macro mac_trait_item($method: ident) { + fn $method(); + } + + trait Tr { + mac_trait_item!(method); + } + + macro mac_trait_impl() { + impl Tr for u8 { // OK + fn method() {} // OK + } + } + + mac_trait_impl!(); +} + +mod error { + macro mac_trait_item() { + fn method(); + } + + trait Tr { + mac_trait_item!(); + } + + macro mac_trait_impl() { + impl Tr for u8 { //~ ERROR not all trait items implemented, missing: `method` + fn method() {} //~ ERROR method `method` is not a member of trait `Tr` + } + } + + mac_trait_impl!(); +} + +fn main() {} diff --git a/src/test/compile-fail/hygiene/assoc_ty_bindings.rs b/src/test/compile-fail/hygiene/assoc_ty_bindings.rs new file mode 100644 index 0000000000000..46a138749ff17 --- /dev/null +++ b/src/test/compile-fail/hygiene/assoc_ty_bindings.rs @@ -0,0 +1,49 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro, associated_type_defaults)] +#![feature(rustc_attrs)] + +trait Base { + type AssocTy; + fn f(); +} +trait Derived: Base { + fn g(); +} + +macro mac() { + type A = Base; + type B = Derived; + + impl Base for u8 { + type AssocTy = u8; + fn f() { + let _: Self::AssocTy; + } + } + impl Derived for u8 { + fn g() { + let _: Self::AssocTy; + } + } + + fn h() { + let _: T::AssocTy; + let _: U::AssocTy; + } +} + +mac!(); + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/hygiene/impl_items.rs b/src/test/compile-fail/hygiene/impl_items.rs index 445aa62f2361e..cdba559445d19 100644 --- a/src/test/compile-fail/hygiene/impl_items.rs +++ b/src/test/compile-fail/hygiene/impl_items.rs @@ -19,7 +19,7 @@ mod foo { } pub macro m() { - let _: () = S.f(); //~ ERROR type `fn(&foo::S) {foo::S::f}` is private + let _: () = S.f(); //~ ERROR type `for<'r> fn(&'r foo::S) {foo::S::f}` is private } } diff --git a/src/test/compile-fail/ifmt-bad-arg.rs b/src/test/compile-fail/ifmt-bad-arg.rs index a23b4b077410c..afe9bc152a36c 100644 --- a/src/test/compile-fail/ifmt-bad-arg.rs +++ b/src/test/compile-fail/ifmt-bad-arg.rs @@ -11,36 +11,44 @@ fn main() { // bad arguments to the format! call - format!("{}"); //~ ERROR: invalid reference to argument + // bad number of arguments, see #44954 (originally #15780) - format!("{1}", 1); //~ ERROR: invalid reference to argument `1` - //~^ ERROR: argument never used - format!("{foo}"); //~ ERROR: no argument named `foo` + format!("{}"); + //~^ ERROR: 1 positional argument in format string, but no arguments were given - format!("", 1, 2); //~ ERROR: multiple unused formatting arguments - format!("{}", 1, 2); //~ ERROR: argument never used - format!("{1}", 1, 2); //~ ERROR: argument never used - format!("{}", 1, foo=2); //~ ERROR: named argument never used - format!("{foo}", 1, foo=2); //~ ERROR: argument never used - format!("", foo=2); //~ ERROR: named argument never used + format!("{1}", 1); + //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument) + //~^^ ERROR: argument never used - format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument - format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow - - // bad number of arguments, see #15780 - - format!("{0}"); - //~^ ERROR invalid reference to argument `0` (no arguments given) + format!("{} {}"); + //~^ ERROR: 2 positional arguments in format string, but no arguments were given format!("{0} {1}", 1); - //~^ ERROR invalid reference to argument `1` (there is 1 argument) + //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument) format!("{0} {1} {2}", 1, 2); - //~^ ERROR invalid reference to argument `2` (there are 2 arguments) - - format!("{0} {1}"); - //~^ ERROR invalid reference to argument `0` (no arguments given) - //~^^ ERROR invalid reference to argument `1` (no arguments given) + //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments) + + format!("{} {value} {} {}", 1, value=2); + //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments) + format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); + //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments) + + format!("{} {foo} {} {bar} {}", 1, 2, 3); + //~^ ERROR: there is no argument named `foo` + //~^^ ERROR: there is no argument named `bar` + + format!("{foo}"); //~ ERROR: no argument named `foo` + format!("", 1, 2); //~ ERROR: multiple unused formatting arguments + format!("{}", 1, 2); //~ ERROR: argument never used + format!("{1}", 1, 2); //~ ERROR: argument never used + format!("{}", 1, foo=2); //~ ERROR: named argument never used + format!("{foo}", 1, foo=2); //~ ERROR: argument never used + format!("", foo=2); //~ ERROR: named argument never used + format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments + + format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument + format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow // bad named arguments, #35082 diff --git a/src/test/compile-fail/impl-trait/disallowed.rs b/src/test/compile-fail/impl-trait/disallowed.rs deleted file mode 100644 index 0467c49b0311d..0000000000000 --- a/src/test/compile-fail/impl-trait/disallowed.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(conservative_impl_trait)] - -fn arguments(_: impl Fn(), -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - _: Vec) {} -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - -type Factory = impl Fn() -> R; -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - -type GlobalFactory = fn() -> impl FnOnce() -> R; -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - -trait LazyToString { - fn lazy_to_string<'a>(&'a self) -> impl Fn() -> String; - //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types -} - -impl LazyToString for String { - fn lazy_to_string<'a>(&'a self) -> impl Fn() -> String { - //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - || self.clone() - } -} - -#[derive(Copy, Clone)] -struct Lazy(T); - -impl std::ops::Add> for Lazy { - type Output = impl Fn() -> Lazy; - //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types - - fn add(self, other: Lazy) -> Self::Output { - move || Lazy(self.0 + other.0) - } -} - -impl std::ops::Add -for impl Fn() -> Lazy -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types -where F: Fn() -> impl FnOnce() -> i32 -//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types -{ - type Output = Self; - - fn add(self, other: F) -> Self::Output { - move || Lazy(self().0 + other()()) - } -} - -fn main() {} diff --git a/src/test/compile-fail/impl-trait/feature-gate-universal.rs b/src/test/compile-fail/impl-trait/feature-gate-universal.rs new file mode 100644 index 0000000000000..e5bdf3a42eb3e --- /dev/null +++ b/src/test/compile-fail/impl-trait/feature-gate-universal.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// gate-test-universal_impl_trait + +fn foo(x: impl std::fmt::Debug) { print!("{:?}", x); } +//~^ ERROR `impl Trait` in argument position is experimental + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/feature-gate.rs b/src/test/compile-fail/impl-trait/feature-gate.rs index f171b6becc4b7..d46a16450db52 100644 --- a/src/test/compile-fail/impl-trait/feature-gate.rs +++ b/src/test/compile-fail/impl-trait/feature-gate.rs @@ -11,6 +11,6 @@ // gate-test-conservative_impl_trait fn foo() -> impl Fn() { || {} } -//~^ ERROR `impl Trait` is experimental +//~^ ERROR `impl Trait` in return position is experimental fn main() {} diff --git a/src/test/compile-fail/impl-trait/impl-generic-mismatch-ab.rs b/src/test/compile-fail/impl-trait/impl-generic-mismatch-ab.rs new file mode 100644 index 0000000000000..43b47e9e915f0 --- /dev/null +++ b/src/test/compile-fail/impl-trait/impl-generic-mismatch-ab.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] +use std::fmt::Debug; + +trait Foo { + fn foo(&self, a: &A, b: &impl Debug); +} + +impl Foo for () { + fn foo(&self, a: &impl Debug, b: &B) { } + //~^ ERROR method `foo` has an incompatible type for trait +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/impl-generic-mismatch.rs b/src/test/compile-fail/impl-trait/impl-generic-mismatch.rs new file mode 100644 index 0000000000000..a95da61aa4c00 --- /dev/null +++ b/src/test/compile-fail/impl-trait/impl-generic-mismatch.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] +use std::fmt::Debug; + +trait Foo { + fn foo(&self, _: &impl Debug); +} + +impl Foo for () { + fn foo(&self, _: &U) { } + //~^ Error method `foo` has incompatible signature for trait +} + +trait Bar { + fn bar(&self, _: &U); +} + +impl Bar for () { + fn bar(&self, _: &impl Debug) { } + //~^ Error method `bar` has incompatible signature for trait +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/lifetimes.rs b/src/test/compile-fail/impl-trait/lifetimes.rs deleted file mode 100644 index 9d9f6bf72974a..0000000000000 --- a/src/test/compile-fail/impl-trait/lifetimes.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(conservative_impl_trait)] - -// Helper creating a fake borrow, captured by the impl Trait. -fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () } - -fn stack() -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - let x = 0; - &x -} - -fn late_bound(x: &i32) -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - x -} - -// FIXME(#34511) Should work but doesn't at the moment, -// region-checking needs an overhault to support this. -fn early_bound<'a>(x: &'a i32) -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - x -} - -fn ambiguous<'a, 'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - if x.len() < y.len() { - x.iter().cloned() - } else { - y.iter().cloned() - } -} - -fn main() {} diff --git a/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs new file mode 100644 index 0000000000000..837160bc2fcd9 --- /dev/null +++ b/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +fn elided(x: &i32) -> impl Copy { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit<'a>(x: &'a i32) -> impl Copy { x } +//~^ ERROR cannot infer an appropriate lifetime + +trait LifetimeTrait<'a> {} +impl<'a> LifetimeTrait<'a> for &'a i32 {} + +fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +// Tests that a closure type contianing 'b cannot be returned from a type where +// only 'a was expected. +fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { + //~^ ERROR lifetime mismatch + move |_| println!("{}", y) +} + +fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { + //~^ ERROR the parameter type `T` may not live long enough + x +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs b/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs new file mode 100644 index 0000000000000..2a06580fe605d --- /dev/null +++ b/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +trait MultiRegionTrait<'a, 'b> {} +impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {} + +fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { +//~^ ERROR ambiguous lifetime bound + (x, y) +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/type_parameters_captured.rs b/src/test/compile-fail/impl-trait/type_parameters_captured.rs new file mode 100644 index 0000000000000..c6ff762b9050a --- /dev/null +++ b/src/test/compile-fail/impl-trait/type_parameters_captured.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +trait Any {} +impl Any for T {} + +// Check that type parameters are captured and not considered 'static +fn foo(x: T) -> impl Any + 'static { + //~^ ERROR the parameter type `T` may not live long enough + x +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/where-allowed.rs b/src/test/compile-fail/impl-trait/where-allowed.rs new file mode 100644 index 0000000000000..af83a2d0a2337 --- /dev/null +++ b/src/test/compile-fail/impl-trait/where-allowed.rs @@ -0,0 +1,234 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A simple test for testing many permutations of allowedness of +//! impl Trait +#![feature(conservative_impl_trait, universal_impl_trait, dyn_trait)] +use std::fmt::Debug; + +// Allowed +fn in_parameters(_: impl Debug) { panic!() } + +// Allowed +fn in_return() -> impl Debug { panic!() } + +// Allowed +fn in_adt_in_parameters(_: Vec) { panic!() } + +// Allowed +fn in_adt_in_return() -> Vec { panic!() } + +// Disallowed +fn in_fn_parameter_in_parameters(_: fn(impl Debug)) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_fn_return_in_parameters(_: fn() -> impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_fn_parameter_in_return() -> fn(impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_fn_return_in_return() -> fn() -> impl Debug { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_dyn_Fn_parameter_in_parameters(_: &dyn Fn(impl Debug)) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_dyn_Fn_return_in_parameters(_: &dyn Fn() -> impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_dyn_Fn_parameter_in_return() -> &'static dyn Fn(impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_dyn_Fn_return_in_return() -> &'static dyn Fn() -> impl Debug { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_Fn_parameter_in_generics (_: F) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_Fn_return_in_generics impl Debug> (_: F) { panic!() } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + + +// Allowed +fn in_impl_Trait_in_parameters(_: impl Iterator) { panic!() } + +// Allowed +fn in_impl_Trait_in_return() -> impl IntoIterator { + vec![vec![0; 10], vec![12; 7], vec![8; 3]] +} + +// Disallowed +struct InBraceStructField { x: impl Debug } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +struct InAdtInBraceStructField { x: Vec } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +struct InTupleStructField(impl Debug); +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +enum InEnum { + InBraceVariant { x: impl Debug }, + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + InTupleVariant(impl Debug), + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Allowed +trait InTraitDefnParameters { + fn in_parameters(_: impl Debug); +} + +// Disallowed +trait InTraitDefnReturn { + fn in_return() -> impl Debug; + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Allowed and disallowed in trait impls +trait DummyTrait { + type Out; + fn in_trait_impl_parameter(impl Debug); + fn in_trait_impl_return() -> Self::Out; +} +impl DummyTrait for () { + type Out = impl Debug; + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + + fn in_trait_impl_parameter(_: impl Debug) { } + // Allowed + + fn in_trait_impl_return() -> impl Debug { () } + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Allowed +struct DummyType; +impl DummyType { + fn in_inherent_impl_parameters(_: impl Debug) { } + fn in_inherent_impl_return() -> impl Debug { () } +} + +// Disallowed +extern "C" { + fn in_foreign_parameters(_: impl Debug); + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + + fn in_foreign_return() -> impl Debug; + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Allowed +extern "C" fn in_extern_fn_parameters(_: impl Debug) { +} + +// Allowed +extern "C" fn in_extern_fn_return() -> impl Debug { + 22 +} + +type InTypeAlias = impl Debug; +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +type InReturnInTypeAlias = fn() -> impl Debug; +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed in impl headers +impl PartialEq for () { + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Disallowed in impl headers +impl PartialEq<()> for impl Debug { + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Disallowed in inherent impls +impl impl Debug { + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Disallowed in inherent impls +struct InInherentImplAdt { t: T } +impl InInherentImplAdt { + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + +// Disallowed in where clauses +fn in_fn_where_clause() + where impl Debug: Debug +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +{ +} + +// Disallowed in where clauses +fn in_adt_in_fn_where_clause() + where Vec: Debug +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +{ +} + +// Disallowed +fn in_trait_parameter_in_fn_where_clause() + where T: PartialEq +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +{ +} + +// Disallowed +fn in_Fn_parameter_in_fn_where_clause() + where T: Fn(impl Debug) +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +{ +} + +// Disallowed +fn in_Fn_return_in_fn_where_clause() + where T: Fn() -> impl Debug +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +{ +} + +fn main() { + let _in_local_variable: impl Fn() = || {}; + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + let _in_return_in_local_variable = || -> impl Fn() { || {} }; + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +} + diff --git a/src/test/compile-fail/index-help.rs b/src/test/compile-fail/index-help.rs new file mode 100644 index 0000000000000..2d37fc7925035 --- /dev/null +++ b/src/test/compile-fail/index-help.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x = vec![1]; + x[0i32]; //~ ERROR E0277 + //~| NOTE vector indices are of type `usize` or ranges of `usize` +} diff --git a/src/test/compile-fail/invalid-inline.rs b/src/test/compile-fail/invalid-inline.rs index ad89087d66020..93b985b4fb063 100644 --- a/src/test/compile-fail/invalid-inline.rs +++ b/src/test/compile-fail/invalid-inline.rs @@ -21,4 +21,8 @@ fn b() { fn c() { } -fn main() {} +fn main() { + a(); + b(); + c(); +} diff --git a/src/test/compile-fail/issue-10755.rs b/src/test/compile-fail/issue-10755.rs index 1d8db84ab1393..87faff2719519 100644 --- a/src/test/compile-fail/issue-10755.rs +++ b/src/test/compile-fail/issue-10755.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -C linker=llllll +// compile-flags: -C linker=llllll -Z linker-flavor=ld // error-pattern: the linker `llllll` fn main() { diff --git a/src/test/compile-fail/issue-12997-2.rs b/src/test/compile-fail/issue-12997-2.rs index 276d7f7c9ed33..85d91bb2db202 100644 --- a/src/test/compile-fail/issue-12997-2.rs +++ b/src/test/compile-fail/issue-12997-2.rs @@ -15,6 +15,6 @@ #[bench] fn bar(x: isize) { } //~^ ERROR mismatched types -//~| expected type `fn(&mut __test::test::Bencher)` +//~| expected type `for<'r> fn(&'r mut __test::test::Bencher)` //~| found type `fn(isize) {bar}` //~| expected mutable reference, found isize diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index ed1634441498b..27b23f083217d 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -22,7 +22,7 @@ impl<'r> Itble<'r, usize, Range> for (usize, usize) { fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool { let cont_iter = cont.iter(); -//~^ ERROR cannot infer an appropriate lifetime for autoref due to conflicting requirements +//~^ ERROR 24:26: 24:30: explicit lifetime required in the type of `cont` [E0621] let result = cont_iter.fold(Some(0), |state, val| { state.map_or(None, |mask| { let bit = 1 << val; @@ -35,5 +35,5 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types -//~| HELP try with `&(3, 5)` +//~| HELP consider borrowing here } diff --git a/src/test/compile-fail/issue-14285.rs b/src/test/compile-fail/issue-14285.rs index 3a5df9e805bdb..dceecee6ca74b 100644 --- a/src/test/compile-fail/issue-14285.rs +++ b/src/test/compile-fail/issue-14285.rs @@ -19,7 +19,7 @@ impl Foo for A {} struct B<'a>(&'a (Foo+'a)); fn foo<'a>(a: &Foo) -> B<'a> { - B(a) //~ ERROR cannot infer an appropriate lifetime + B(a) //~ ERROR 22:5: 22:9: explicit lifetime required in the type of `a` [E0621] } fn main() { diff --git a/src/test/compile-fail/issue-15034.rs b/src/test/compile-fail/issue-15034.rs index 69e10b90bfeba..a62e46820d3cb 100644 --- a/src/test/compile-fail/issue-15034.rs +++ b/src/test/compile-fail/issue-15034.rs @@ -25,7 +25,7 @@ struct Parser<'a> { impl<'a> Parser<'a> { pub fn new(lexer: &'a mut Lexer) -> Parser<'a> { Parser { lexer: lexer } - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR 27:25: 27:30: explicit lifetime required in the type of `lexer` [E0621] } } diff --git a/src/test/compile-fail/issue-16338.rs b/src/test/compile-fail/issue-16338.rs index a4517e60d66e1..6fdf8802e385e 100644 --- a/src/test/compile-fail/issue-16338.rs +++ b/src/test/compile-fail/issue-16338.rs @@ -16,7 +16,6 @@ struct Slice { fn main() { let Slice { data: data, len: len } = "foo"; //~^ ERROR mismatched types - //~| expected type `&str` //~| found type `Slice<_>` - //~| expected &str, found struct `Slice` + //~| ERROR non-reference pattern used to match a reference } diff --git a/src/test/compile-fail/issue-17718-const-bad-values.rs b/src/test/compile-fail/issue-17718-const-bad-values.rs index af356588ed9e7..17ec77d77eea2 100644 --- a/src/test/compile-fail/issue-17718-const-bad-values.rs +++ b/src/test/compile-fail/issue-17718-const-bad-values.rs @@ -10,13 +10,11 @@ const C1: &'static mut [usize] = &mut []; //~^ ERROR: references in constants may only refer to immutable values -//~| ERROR: references in constants may only refer to immutable values static mut S: usize = 3; const C2: &'static mut usize = unsafe { &mut S }; //~^ ERROR: constants cannot refer to statics //~| ERROR: references in constants may only refer to immutable values //~| ERROR: references in constants may only refer to immutable values -//~| ERROR: references in constants may only refer to immutable values fn main() {} diff --git a/src/test/compile-fail/issue-17718-const-borrow.rs b/src/test/compile-fail/issue-17718-const-borrow.rs index 1464fcd9a1cd9..07123c6949255 100644 --- a/src/test/compile-fail/issue-17718-const-borrow.rs +++ b/src/test/compile-fail/issue-17718-const-borrow.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_unsafe_cell_new)] - use std::cell::UnsafeCell; const A: UnsafeCell = UnsafeCell::new(1); diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs index 9724d17bef1ea..8516a8ea52e42 100644 --- a/src/test/compile-fail/issue-17728.rs +++ b/src/test/compile-fail/issue-17728.rs @@ -21,9 +21,9 @@ trait TraversesWorld { fn attemptTraverse(&self, room: &Room, directionStr: &str) -> Result<&Room, &str> { let direction = str_to_direction(directionStr); let maybe_room = room.direction_to_room.get(&direction); - //~^ ERROR cannot infer an appropriate lifetime for autoref due to conflicting requirements match maybe_room { Some(entry) => Ok(entry), + //~^ ERROR 25:28: 25:37: lifetime mismatch [E0623] _ => Err("Direction does not exist in room.") } } diff --git a/src/test/compile-fail/issue-17740.rs b/src/test/compile-fail/issue-17740.rs index 664d62e87ae61..1d5ef4360dc1e 100644 --- a/src/test/compile-fail/issue-17740.rs +++ b/src/test/compile-fail/issue-17740.rs @@ -15,12 +15,12 @@ struct Foo<'a> { impl <'a> Foo<'a>{ fn bar(self: &mut Foo) { //~^ mismatched method receiver - //~| expected type `&mut Foo<'a>` - //~| found type `&mut Foo<'_>` + //~| expected type `Foo<'a>` + //~| found type `Foo<'_>` //~| lifetime mismatch //~| mismatched method receiver - //~| expected type `&mut Foo<'a>` - //~| found type `&mut Foo<'_>` + //~| expected type `Foo<'a>` + //~| found type `Foo<'_>` //~| lifetime mismatch } } diff --git a/src/test/compile-fail/issue-18937.rs b/src/test/compile-fail/issue-18937.rs index 5996c8e543878..f7f84e6452ddb 100644 --- a/src/test/compile-fail/issue-18937.rs +++ b/src/test/compile-fail/issue-18937.rs @@ -27,7 +27,6 @@ trait A<'a> { impl<'a> A<'a> for B { fn foo(&mut self, f: F) //~ ERROR impl has stricter - //~^ WARNING future release where F: fmt::Debug + 'static, { self.list.push(Box::new(f)); diff --git a/src/test/compile-fail/issue-1962.rs b/src/test/compile-fail/issue-1962.rs index db3e9c23b7621..9de3040bb616c 100644 --- a/src/test/compile-fail/issue-1962.rs +++ b/src/test/compile-fail/issue-1962.rs @@ -11,7 +11,7 @@ // compile-flags: -D while-true fn main() { let mut i = 0; - while true { //~ ERROR denote infinite loops with loop + while true { //~ ERROR denote infinite loops with `loop i += 1; if i == 5 { break; } } diff --git a/src/test/compile-fail/issue-20261.rs b/src/test/compile-fail/issue-20261.rs index 2f1910b26bbef..092aaa7695505 100644 --- a/src/test/compile-fail/issue-20261.rs +++ b/src/test/compile-fail/issue-20261.rs @@ -9,7 +9,9 @@ // except according to those terms. fn main() { - for (ref i,) in [].iter() { //~ ERROR mismatched types + // NB: this (almost) typechecks when default binding modes are enabled. + for (ref i,) in [].iter() { + //~^ ERROR non-reference pattern used to match a reference i.clone(); } } diff --git a/src/test/compile-fail/issue-20831-debruijn.rs b/src/test/compile-fail/issue-20831-debruijn.rs index 323cd24d8dda5..3f00f561ae96f 100644 --- a/src/test/compile-fail/issue-20831-debruijn.rs +++ b/src/test/compile-fail/issue-20831-debruijn.rs @@ -38,7 +38,6 @@ impl<'a> Publisher<'a> for MyStruct<'a> { fn subscribe(&mut self, t : Box::Output> + 'a>) { // Not obvious, but there is an implicit lifetime here -------^ //~^^ ERROR cannot infer - //~| ERROR cannot infer // // The fact that `Publisher` is using an implicit lifetime is // what was causing the debruijn accounting to be off, so diff --git a/src/test/compile-fail/issue-22560.rs b/src/test/compile-fail/issue-22560.rs index eb5c6076440e9..914a3bd79d460 100644 --- a/src/test/compile-fail/issue-22560.rs +++ b/src/test/compile-fail/issue-22560.rs @@ -23,6 +23,6 @@ type Test = Add + //~| NOTE missing reference to `RHS` //~| NOTE because of the default `Self` reference, type parameters must be specified on object types //~| ERROR E0225 - //~| NOTE non-Send/Sync additional trait + //~| NOTE non-auto additional trait fn main() { } diff --git a/src/test/compile-fail/issue-22638.rs b/src/test/compile-fail/issue-22638.rs index 65d1d837d7dc3..1c534ebbd4350 100644 --- a/src/test/compile-fail/issue-22638.rs +++ b/src/test/compile-fail/issue-22638.rs @@ -12,13 +12,13 @@ #![recursion_limit = "20"] #![type_length_limit = "20000000"] +#![crate_type = "rlib"] #[derive(Clone)] struct A (B); impl A { pub fn matches(&self, f: &F) { - //~^ ERROR reached the recursion limit while instantiating `A::matches::<[closure let &A(ref term) = self; term.matches(f); } @@ -58,6 +58,7 @@ struct D (Box); impl D { pub fn matches(&self, f: &F) { + //~^ ERROR reached the type-length limit while instantiating `D::matches::<[closure let &D(ref a) = self; a.matches(f) } @@ -66,5 +67,3 @@ impl D { pub fn matches() { A(B::Variant1).matches(&(|| ())) } - -fn main() {} diff --git a/src/test/compile-fail/issue-22933-2.rs b/src/test/compile-fail/issue-22933-2.rs index 97456c2da87fe..583f2ace4ba05 100644 --- a/src/test/compile-fail/issue-22933-2.rs +++ b/src/test/compile-fail/issue-22933-2.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum Delicious { +enum Delicious { //~ NOTE variant `PIE` not found here Pie = 0x1, Apple = 0x2, ApplePie = Delicious::Apple as isize | Delicious::PIE as isize, - //~^ ERROR no associated item named `PIE` found for type `Delicious` + //~^ ERROR no variant named `PIE` found for type `Delicious` + //~| NOTE variant not found in `Delicious` } fn main() {} diff --git a/src/test/compile-fail/issue-23080-2.rs b/src/test/compile-fail/issue-23080-2.rs index 9d20c17674bc3..bf44cd53f67dc 100644 --- a/src/test/compile-fail/issue-23080-2.rs +++ b/src/test/compile-fail/issue-23080-2.rs @@ -17,6 +17,7 @@ unsafe trait Trait { type Output; } +#[allow(auto_impl)] unsafe impl Trait for .. {} fn call_method(x: T) {} diff --git a/src/test/compile-fail/issue-23080.rs b/src/test/compile-fail/issue-23080.rs index 2e8cba87be515..1fb63391d5608 100644 --- a/src/test/compile-fail/issue-23080.rs +++ b/src/test/compile-fail/issue-23080.rs @@ -19,6 +19,7 @@ unsafe trait Trait { } } +#[allow(auto_impl)] unsafe impl Trait for .. {} fn call_method(x: T) { diff --git a/src/test/compile-fail/issue-23173.rs b/src/test/compile-fail/issue-23173.rs index 946e4b9e96e47..c0983eb0e5257 100644 --- a/src/test/compile-fail/issue-23173.rs +++ b/src/test/compile-fail/issue-23173.rs @@ -9,9 +9,27 @@ // except according to those terms. enum Token { LeftParen, RightParen, Plus, Minus, /* etc */ } +//~^ NOTE variant `Homura` not found here +struct Struct { + //~^ NOTE function or associated item `method` not found for this + //~| NOTE function or associated item `method` not found for this + //~| NOTE associated item `Assoc` not found for this + a: usize, +} fn use_token(token: &Token) { unimplemented!() } fn main() { - use_token(&Token::Homura); //~ ERROR no associated item named + use_token(&Token::Homura); + //~^ ERROR no variant named `Homura` + //~| NOTE variant not found in `Token` + Struct::method(); + //~^ ERROR no function or associated item named `method` found for type + //~| NOTE function or associated item not found in `Struct` + Struct::method; + //~^ ERROR no function or associated item named `method` found for type + //~| NOTE function or associated item not found in `Struct` + Struct::Assoc; + //~^ ERROR no associated item named `Assoc` found for type `Struct` in + //~| NOTE associated item not found in `Struct` } diff --git a/src/test/compile-fail/issue-23217.rs b/src/test/compile-fail/issue-23217.rs index 95f6526f11559..cce0b99c04d79 100644 --- a/src/test/compile-fail/issue-23217.rs +++ b/src/test/compile-fail/issue-23217.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub enum SomeEnum { +pub enum SomeEnum { //~ NOTE variant `A` not found here B = SomeEnum::A, - //~^ ERROR no associated item named `A` found for type `SomeEnum` + //~^ ERROR no variant named `A` found for type `SomeEnum` + //~| NOTE variant not found in `SomeEnum` } fn main() {} diff --git a/src/test/run-pass/issue-25439.rs b/src/test/compile-fail/issue-25439.rs similarity index 87% rename from src/test/run-pass/issue-25439.rs rename to src/test/compile-fail/issue-25439.rs index 88c48f42c513c..6e33fd5ae71a6 100644 --- a/src/test/run-pass/issue-25439.rs +++ b/src/test/compile-fail/issue-25439.rs @@ -15,5 +15,5 @@ fn fix(f: F) -> i32 where F: Fn(Helper, i32) -> i32 { } fn main() { - fix(|_, x| x); + fix(|_, x| x); //~ ERROR closure/generator type that references itself [E0644] } diff --git a/src/test/compile-fail/issue-25579.rs b/src/test/compile-fail/issue-25579.rs index 323ce3b0adf33..9e12d5b5de157 100644 --- a/src/test/compile-fail/issue-25579.rs +++ b/src/test/compile-fail/issue-25579.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + enum Sexpression { Num(()), Cons(&'static mut Sexpression) @@ -16,8 +19,14 @@ enum Sexpression { fn causes_ice(mut l: &mut Sexpression) { loop { match l { &mut Sexpression::Num(ref mut n) => {}, - &mut Sexpression::Cons(ref mut expr) => { //~ ERROR cannot borrow `l.0` - l = &mut **expr; //~ ERROR cannot assign to `l` + &mut Sexpression::Cons(ref mut expr) => { //[ast]~ ERROR [E0499] + //[mir]~^ ERROR [E0506] + //[mir]~| ERROR [E0499] + l = &mut **expr; //[ast]~ ERROR [E0506] + //[mir]~^ ERROR [E0506] + //[mir]~| ERROR [E0506] + //[mir]~| ERROR [E0499] + //[mir]~| ERROR [E0499] } }} } diff --git a/src/test/compile-fail/issue-26194.rs b/src/test/compile-fail/issue-26194.rs index ef91188c5d166..7ddd56229ceb7 100644 --- a/src/test/compile-fail/issue-26194.rs +++ b/src/test/compile-fail/issue-26194.rs @@ -12,7 +12,7 @@ struct S(String); impl S { fn f(self: *mut S) -> String { self.0 } - //~^ ERROR mismatched method receiver + //~^ ERROR invalid `self` type } fn main() { S("".to_owned()).f(); } diff --git a/src/test/ui/issue-26548.rs b/src/test/compile-fail/issue-26548.rs similarity index 70% rename from src/test/ui/issue-26548.rs rename to src/test/compile-fail/issue-26548.rs index 2591d7bcbaef4..39c6e97268f98 100644 --- a/src/test/ui/issue-26548.rs +++ b/src/test/compile-fail/issue-26548.rs @@ -8,7 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: overflow representing the type +// error-pattern: unsupported cyclic reference between types/traits detected +// note-pattern: the cycle begins when computing layout of +// note-pattern: ...which then requires computing layout of +// note-pattern: ...which then again requires computing layout of trait Mirror { type It: ?Sized; } diff --git a/src/test/compile-fail/issue-27060-2.rs b/src/test/compile-fail/issue-27060-2.rs new file mode 100644 index 0000000000000..28180b05c8de8 --- /dev/null +++ b/src/test/compile-fail/issue-27060-2.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(packed)] +pub struct Bad { + data: T, //~ ERROR `T: std::marker::Sized` is not satisfied +} + +fn main() {} diff --git a/src/test/compile-fail/issue-27060.rs b/src/test/compile-fail/issue-27060.rs new file mode 100644 index 0000000000000..37369d551fc77 --- /dev/null +++ b/src/test/compile-fail/issue-27060.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(packed)] +pub struct Good { + data: &'static u32, + data2: [&'static u32; 2], + aligned: [u8; 32], +} + +#[repr(packed)] +pub struct JustArray { + array: [u32] +} + +#[deny(safe_packed_borrows)] +fn main() { + let good = Good { + data: &0, + data2: [&0, &0], + aligned: [0; 32] + }; + + unsafe { + let _ = &good.data; // ok + let _ = &good.data2[0]; // ok + } + + let _ = &good.data; //~ ERROR borrow of packed field requires unsafe + //~| hard error + let _ = &good.data2[0]; //~ ERROR borrow of packed field requires unsafe + //~| hard error + let _ = &*good.data; // ok, behind a pointer + let _ = &good.aligned; // ok, has align 1 + let _ = &good.aligned[2]; // ok, has align 1 +} diff --git a/src/test/compile-fail/issue-27842.rs b/src/test/compile-fail/issue-27842.rs index 8c71761df2fb8..eb28e36dc076e 100644 --- a/src/test/compile-fail/issue-27842.rs +++ b/src/test/compile-fail/issue-27842.rs @@ -14,7 +14,7 @@ fn main() { let _ = tup[0]; //~^ ERROR cannot index into a value of type //~| HELP to access tuple elements, use - //~| SUGGESTION let _ = tup.0 + //~| SUGGESTION tup.0 // the case where we show just a general hint let i = 0_usize; diff --git a/src/test/compile-fail/issue-28971.rs b/src/test/compile-fail/issue-28971.rs index 1d14b71a40e4f..10be4d6210d7a 100644 --- a/src/test/compile-fail/issue-28971.rs +++ b/src/test/compile-fail/issue-28971.rs @@ -10,13 +10,15 @@ // This should not cause an ICE -enum Foo { +enum Foo { //~ NOTE variant `Baz` not found here Bar(u8) } fn main(){ foo(|| { match Foo::Bar(1) { - Foo::Baz(..) => (), //~ ERROR no associated + Foo::Baz(..) => (), + //~^ ERROR no variant named `Baz` found for type `Foo` + //~| NOTE variant not found in `Foo` _ => (), } }); diff --git a/src/test/compile-fail/issue-30355.rs b/src/test/compile-fail/issue-30355.rs new file mode 100644 index 0000000000000..ee19d04031893 --- /dev/null +++ b/src/test/compile-fail/issue-30355.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct X([u8]); + +pub static Y: &'static X = { + const Y: &'static [u8] = b""; + &X(*Y) + //~^ ERROR cannot move out + //~^^ ERROR cannot move a + //~^^^ ERROR cannot move a +}; + +fn main() {} diff --git a/src/test/compile-fail/issue-31221.rs b/src/test/compile-fail/issue-31221.rs index e2b80215caf61..8701ca0178fc9 100644 --- a/src/test/compile-fail/issue-31221.rs +++ b/src/test/compile-fail/issue-31221.rs @@ -13,8 +13,6 @@ #![allow(non_snake_case)] #![deny(unreachable_patterns)] //~^ NOTE lint level defined here -//~^^ NOTE lint level defined here -//~^^^ NOTE lint level defined here #[derive(Clone, Copy)] enum Enum { diff --git a/src/test/compile-fail/issue-3154.rs b/src/test/compile-fail/issue-3154.rs index 5f55c550aeb39..519e9d06d1b33 100644 --- a/src/test/compile-fail/issue-3154.rs +++ b/src/test/compile-fail/issue-3154.rs @@ -13,7 +13,7 @@ struct thing<'a, Q:'a> { } fn thing<'a,Q>(x: &Q) -> thing<'a,Q> { - thing{ x: x } //~ ERROR cannot infer + thing{ x: x } //~ ERROR 16:5: 16:18: explicit lifetime required in the type of `x` [E0621] } fn main() { diff --git a/src/test/compile-fail/issue-31924-non-snake-ffi.rs b/src/test/compile-fail/issue-31924-non-snake-ffi.rs new file mode 100644 index 0000000000000..d9ce1159c0ece --- /dev/null +++ b/src/test/compile-fail/issue-31924-non-snake-ffi.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![deny(non_snake_case)] + +#[no_mangle] +pub extern "C" fn SparklingGenerationForeignFunctionInterface() {} + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/issue-32963.rs b/src/test/compile-fail/issue-32963.rs index f146cfbe68b96..e97e5a86a9d7d 100644 --- a/src/test/compile-fail/issue-32963.rs +++ b/src/test/compile-fail/issue-32963.rs @@ -16,6 +16,6 @@ fn size_of_copy() -> usize { mem::size_of::() } fn main() { size_of_copy::(); - //~^ ERROR only Send/Sync traits can be used as additional traits in a trait object + //~^ ERROR only auto traits can be used as additional traits in a trait object //~| ERROR the trait bound `Misc: std::marker::Copy` is not satisfied } diff --git a/src/test/compile-fail/issue-33241.rs b/src/test/compile-fail/issue-33241.rs new file mode 100644 index 0000000000000..6a411b4c59c68 --- /dev/null +++ b/src/test/compile-fail/issue-33241.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +use std::fmt; + +// CoerceUnsized is not implemented for tuples. You can still create +// an unsized tuple by transmuting a trait object. +fn any() -> T { unreachable!() } + +#[rustc_error] +fn main() { //~ ERROR compilation successful + let t: &(u8, fmt::Debug) = any(); + println!("{:?}", &t.1); +} diff --git a/src/test/compile-fail/issue-33504.rs b/src/test/compile-fail/issue-33504.rs index bc78d20745a54..1e1994357c77a 100644 --- a/src/test/compile-fail/issue-33504.rs +++ b/src/test/compile-fail/issue-33504.rs @@ -14,6 +14,6 @@ struct Test; fn main() { || { - let Test = 1; //~ ERROR let bindings cannot shadow unit structs + let Test = 1; //~ ERROR mismatched types }; } diff --git a/src/test/compile-fail/issue-36082.rs b/src/test/compile-fail/issue-36082.rs index b46756bb8f554..33a9b1e926cf5 100644 --- a/src/test/compile-fail/issue-36082.rs +++ b/src/test/compile-fail/issue-36082.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + use std::cell::RefCell; fn main() { @@ -16,10 +19,15 @@ fn main() { let x = RefCell::new((&mut r,s)); let val: &_ = x.borrow().0; - //~^ ERROR borrowed value does not live long enough - //~| temporary value dropped here while still borrowed - //~| temporary value created here - //~| consider using a `let` binding to increase its lifetime + //[ast]~^ ERROR borrowed value does not live long enough [E0597] + //[ast]~| NOTE temporary value dropped here while still borrowed + //[ast]~| NOTE temporary value created here + //[ast]~| NOTE consider using a `let` binding to increase its lifetime + //[mir]~^^^^^ ERROR borrowed value does not live long enough [E0597] + //[mir]~| NOTE temporary value dropped here while still borrowed + //[mir]~| NOTE temporary value created here + //[mir]~| NOTE consider using a `let` binding to increase its lifetime println!("{}", val); } -//~^ temporary value needs to live until here +//[ast]~^ NOTE temporary value needs to live until here +//[mir]~^^ NOTE temporary value needs to live until here diff --git a/src/test/compile-fail/issue-37887.rs b/src/test/compile-fail/issue-37887.rs new file mode 100644 index 0000000000000..f120bbbfc9f1a --- /dev/null +++ b/src/test/compile-fail/issue-37887.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + extern crate libc; //~ ERROR use of unstable + use libc::*; //~ ERROR unresolved import +} diff --git a/src/test/compile-fail/issue-40288-2.rs b/src/test/compile-fail/issue-40288-2.rs index c1e8cb8b6defb..e16a7ecf6b908 100644 --- a/src/test/compile-fail/issue-40288-2.rs +++ b/src/test/compile-fail/issue-40288-2.rs @@ -12,12 +12,12 @@ fn prove_static(_: &'static T) {} fn lifetime_transmute_slice<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T { let mut out = [x]; - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements { let slice: &mut [_] = &mut out; slice[0] = y; } out[0] + //~^ ERROR 19:5: 19:11: explicit lifetime required in the type of `y` [E0621] } struct Struct { @@ -27,12 +27,12 @@ struct Struct { fn lifetime_transmute_struct<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T { let mut out = Struct { head: x, _tail: [()] }; - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements { let dst: &mut Struct<_, [()]> = &mut out; dst.head = y; } out.head + //~^ ERROR 34:5: 34:13: explicit lifetime required in the type of `y` [E0621] } fn main() { diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index a4585f7bac7da..191b867e7a8b5 100644 --- a/src/test/compile-fail/issue-41255.rs +++ b/src/test/compile-fail/issue-41255.rs @@ -39,8 +39,6 @@ fn main() { match (x, 5) { (3.14, 1) => {}, //~ ERROR floating-point literals cannot be used //~| WARNING hard error - //~| ERROR floating-point literals cannot be used - //~| WARNING hard error _ => {}, } // Or structs @@ -48,8 +46,6 @@ fn main() { match (Foo { x }) { Foo { x: 2.0 } => {}, //~ ERROR floating-point literals cannot be used //~| WARNING hard error - //~| ERROR floating-point literals cannot be used - //~| WARNING hard error _ => {}, } } diff --git a/src/test/compile-fail/issue-41394.rs b/src/test/compile-fail/issue-41394.rs index 1fb3b7c4ee120..89f11edaec862 100644 --- a/src/test/compile-fail/issue-41394.rs +++ b/src/test/compile-fail/issue-41394.rs @@ -10,7 +10,7 @@ enum Foo { A = "" + 1 - //~^ ERROR binary operation `+` cannot be applied to type `&'static str` + //~^ ERROR binary operation `+` cannot be applied to type `&str` } enum Bar { diff --git a/src/test/compile-fail/issue-43733-2.rs b/src/test/compile-fail/issue-43733-2.rs index 1bf165c89d329..0fd31454596e6 100644 --- a/src/test/compile-fail/issue-43733-2.rs +++ b/src/test/compile-fail/issue-43733-2.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_fn, const_cell_new, const_unsafe_cell_new)] +#![feature(const_fn)] #![feature(cfg_target_thread_local, thread_local_internals)] // On platforms *without* `#[thread_local]`, use diff --git a/src/test/compile-fail/issue-44239.rs b/src/test/compile-fail/issue-44239.rs new file mode 100644 index 0000000000000..131c65266425b --- /dev/null +++ b/src/test/compile-fail/issue-44239.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let n = 0; + + struct Foo; + impl Foo { + const N: usize = n; + //~^ ERROR attempt to use a non-constant value + } +} diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs new file mode 100644 index 0000000000000..a6ae21c3b5497 --- /dev/null +++ b/src/test/compile-fail/issue-44578.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + const AMT: usize; +} + +enum Bar { + First(A), + Second(B), +} + +impl Foo for Bar { + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR constant evaluation +} + +impl Foo for u8 { + const AMT: usize = 1; +} + +impl Foo for u16 { + const AMT: usize = 2; +} + +fn main() { + println!("{}", as Foo>::AMT); +} diff --git a/src/test/compile-fail/issue-45087-unreachable-unsafe.rs b/src/test/compile-fail/issue-45087-unreachable-unsafe.rs new file mode 100644 index 0000000000000..eeb66fa0e2c3b --- /dev/null +++ b/src/test/compile-fail/issue-45087-unreachable-unsafe.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + return; + *(1 as *mut u32) = 42; + //~^ ERROR dereference of raw pointer requires unsafe +} diff --git a/src/test/compile-fail/issue-45199.rs b/src/test/compile-fail/issue-45199.rs new file mode 100644 index 0000000000000..af8f7dce60875 --- /dev/null +++ b/src/test/compile-fail/issue-45199.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + +fn test_drop_replace() { + let b: Box; + b = Box::new(1); //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment + b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `b` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable +} + +fn test_call() { + let b = Box::new(1); //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment + b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `b` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable +} + +fn test_args(b: Box) { //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment + b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `b` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable +} + +fn main() {} diff --git a/src/test/compile-fail/issue-45729-unsafe-in-generator.rs b/src/test/compile-fail/issue-45729-unsafe-in-generator.rs new file mode 100644 index 0000000000000..489e91797f3d7 --- /dev/null +++ b/src/test/compile-fail/issue-45729-unsafe-in-generator.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators)] + +fn main() { + let _ = || { + *(1 as *mut u32) = 42; + //~^ ERROR dereference of raw pointer requires unsafe + yield; + }; +} diff --git a/src/test/compile-fail/issue-45801.rs b/src/test/compile-fail/issue-45801.rs new file mode 100644 index 0000000000000..7823a7d6ba8b5 --- /dev/null +++ b/src/test/compile-fail/issue-45801.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Params; + +pub trait Plugin { + type Error; +} + +pub trait Pluggable { + fn get_ref>(&mut self) -> Option { + None + } +} + +struct Foo; +impl Plugin for Params { + type Error = (); +} + +impl Pluggable for T {} + +fn handle(req: &mut i32) { + req.get_ref::(); + //~^ ERROR the trait bound `Params: Plugin` is not satisfied +} + +fn main() {} diff --git a/src/test/compile-fail/issue-46023.rs b/src/test/compile-fail/issue-46023.rs new file mode 100644 index 0000000000000..d51d92570fd67 --- /dev/null +++ b/src/test/compile-fail/issue-46023.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck=mir + +fn main() { + let x = 0; + + (move || { + x = 1; + //[mir]~^ ERROR cannot assign to immutable item `x` [E0594] + //[ast]~^^ ERROR cannot assign to captured outer variable in an `FnMut` closure [E0594] + })() +} diff --git a/src/test/compile-fail/issue-5500-1.rs b/src/test/compile-fail/issue-5500-1.rs index 7e5809cdee0b0..75ff0a1210142 100644 --- a/src/test/compile-fail/issue-5500-1.rs +++ b/src/test/compile-fail/issue-5500-1.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=compare + struct TrieMapIterator<'a> { node: &'a usize } @@ -15,6 +18,9 @@ struct TrieMapIterator<'a> { fn main() { let a = 5; let _iter = TrieMapIterator{node: &a}; - _iter.node = & //~ ERROR cannot assign to immutable field + _iter.node = & //[ast]~ ERROR cannot assign to immutable field `_iter.node` + //[mir]~^ ERROR cannot assign to immutable field `_iter.node` (Ast) + // MIR doesn't generate an error because the code isn't reachable. This is OK + // because the test is here to check that the compiler doesn't ICE (cf. #5500). panic!() } diff --git a/src/test/compile-fail/issue-7364.rs b/src/test/compile-fail/issue-7364.rs index ef53be7578005..3979790e3d4c7 100644 --- a/src/test/compile-fail/issue-7364.rs +++ b/src/test/compile-fail/issue-7364.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax, const_refcell_new)] +#![feature(box_syntax)] use std::cell::RefCell; diff --git a/src/test/compile-fail/issue-7867.rs b/src/test/compile-fail/issue-7867.rs index 7d9f8e9058521..016df6cb6ef81 100644 --- a/src/test/compile-fail/issue-7867.rs +++ b/src/test/compile-fail/issue-7867.rs @@ -21,17 +21,4 @@ fn main() { //~| expected tuple, found enum `A` _ => () } - - match &Some(42) { - Some(x) => (), - //~^ ERROR mismatched types - //~| expected type `&std::option::Option<{integer}>` - //~| found type `std::option::Option<_>` - //~| expected reference, found enum `std::option::Option` - None => () - //~^ ERROR mismatched types - //~| expected type `&std::option::Option<{integer}>` - //~| found type `std::option::Option<_>` - //~| expected reference, found enum `std::option::Option` - } } diff --git a/src/test/compile-fail/keyword-false-as-identifier.rs b/src/test/compile-fail/keyword-false-as-identifier.rs index e8af94f16b1b9..f246d6e75df8d 100644 --- a/src/test/compile-fail/keyword-false-as-identifier.rs +++ b/src/test/compile-fail/keyword-false-as-identifier.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let false = "foo"; //~ error: mismatched types + let false = 22; //~ error: mismatched types } diff --git a/src/test/compile-fail/keyword-self-as-identifier.rs b/src/test/compile-fail/keyword-self-as-identifier.rs index f01aab92356dd..b50fc68bed6be 100644 --- a/src/test/compile-fail/keyword-self-as-identifier.rs +++ b/src/test/compile-fail/keyword-self-as-identifier.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let Self = "foo"; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope + let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope } diff --git a/src/test/compile-fail/keyword-super-as-identifier.rs b/src/test/compile-fail/keyword-super-as-identifier.rs index 62649ba8a0fe1..54dac771f01ed 100644 --- a/src/test/compile-fail/keyword-super-as-identifier.rs +++ b/src/test/compile-fail/keyword-super-as-identifier.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let super = "foo"; //~ ERROR failed to resolve. There are too many initial `super`s + let super = 22; //~ ERROR failed to resolve. There are too many initial `super`s } diff --git a/src/test/compile-fail/keyword-true-as-identifier.rs b/src/test/compile-fail/keyword-true-as-identifier.rs index 90414fa912dba..b09d09db560f5 100644 --- a/src/test/compile-fail/keyword-true-as-identifier.rs +++ b/src/test/compile-fail/keyword-true-as-identifier.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let true = "foo"; //~ error: mismatched types + let true = 22; //~ error: mismatched types } diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/compile-fail/lint-ctypes.rs index 1d9b179c05d85..fd825563eba19 100644 --- a/src/test/compile-fail/lint-ctypes.rs +++ b/src/test/compile-fail/lint-ctypes.rs @@ -52,8 +52,6 @@ extern { pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without - pub fn good1(size: *const libc::c_int); - pub fn good2(size: *const libc::c_uint); pub fn good3(fptr: Option); pub fn good4(aptr: &[u8; 4 as usize]); pub fn good5(s: StructWithProjection); @@ -66,5 +64,11 @@ extern { pub fn good12(size: usize); } +#[cfg(not(target_arch = "wasm32"))] +extern { + pub fn good1(size: *const libc::c_int); + pub fn good2(size: *const libc::c_uint); +} + fn main() { } diff --git a/src/test/compile-fail/lint-dead-code-1.rs b/src/test/compile-fail/lint-dead-code-1.rs index f45e80f5252e3..d6ca5e6b1d969 100644 --- a/src/test/compile-fail/lint-dead-code-1.rs +++ b/src/test/compile-fail/lint-dead-code-1.rs @@ -74,7 +74,7 @@ pub enum pub_enum3 { enum priv_enum { foo2, bar2 } //~ ERROR: enum is never used enum used_enum { foo3, - bar3 //~ ERROR variant is never used + bar3 //~ ERROR variant is never constructed } fn f() {} diff --git a/src/test/compile-fail/lint-dead-code-3.rs b/src/test/compile-fail/lint-dead-code-3.rs index 28475b1ef8a03..f680e39544933 100644 --- a/src/test/compile-fail/lint-dead-code-3.rs +++ b/src/test/compile-fail/lint-dead-code-3.rs @@ -11,11 +11,9 @@ #![allow(unused_variables)] #![allow(non_camel_case_types)] #![deny(dead_code)] -#![feature(libc)] #![crate_type="lib"] -extern crate libc; pub use extern_foo as x; extern { @@ -54,14 +52,13 @@ pub fn pub_fn() { } mod blah { - use libc::size_t; // not warned because it's used in the parameter of `free` and return of // `malloc` below, which are also used. enum c_void {} extern { fn free(p: *const c_void); - fn malloc(size: size_t) -> *const c_void; + fn malloc(size: usize) -> *const c_void; } pub fn baz() { diff --git a/src/test/compile-fail/lint-dead-code-4.rs b/src/test/compile-fail/lint-dead-code-4.rs index 3df089fc20048..1296cf46e6fe4 100644 --- a/src/test/compile-fail/lint-dead-code-4.rs +++ b/src/test/compile-fail/lint-dead-code-4.rs @@ -22,8 +22,8 @@ fn field_read(f: Foo) -> usize { } enum XYZ { - X, //~ ERROR variant is never used - Y { //~ ERROR variant is never used + X, //~ ERROR variant is never constructed + Y { //~ ERROR variant is never constructed a: String, b: i32, c: i32, @@ -43,13 +43,13 @@ enum ABC { //~ ERROR enum is never used // ensure struct variants get warning for their fields enum IJK { - I, //~ ERROR variant is never used + I, //~ ERROR variant is never constructed J { a: String, b: i32, //~ ERROR field is never used c: i32, //~ ERROR field is never used }, - K //~ ERROR variant is never used + K //~ ERROR variant is never constructed } diff --git a/src/test/compile-fail/lint-dead-code-5.rs b/src/test/compile-fail/lint-dead-code-5.rs index 04d6547d93812..ee5cf24823d40 100644 --- a/src/test/compile-fail/lint-dead-code-5.rs +++ b/src/test/compile-fail/lint-dead-code-5.rs @@ -13,15 +13,15 @@ enum Enum1 { Variant1(isize), - Variant2 //~ ERROR: variant is never used + Variant2 //~ ERROR: variant is never constructed } enum Enum2 { Variant3(bool), #[allow(dead_code)] Variant4(isize), - Variant5 { _x: isize }, //~ ERROR: variant is never used: `Variant5` - Variant6(isize), //~ ERROR: variant is never used: `Variant6` + Variant5 { _x: isize }, //~ ERROR: variant is never constructed: `Variant5` + Variant6(isize), //~ ERROR: variant is never constructed: `Variant6` _Variant7, } diff --git a/src/test/compile-fail/lint-dead-code-variant.rs b/src/test/compile-fail/lint-dead-code-variant.rs index 0116d63caf230..3301560c31500 100644 --- a/src/test/compile-fail/lint-dead-code-variant.rs +++ b/src/test/compile-fail/lint-dead-code-variant.rs @@ -12,7 +12,7 @@ #[derive(Clone)] enum Enum { - Variant1, //~ ERROR: variant is never used + Variant1, //~ ERROR: variant is never constructed Variant2, } diff --git a/src/test/compile-fail/lint-output-format-2.rs b/src/test/compile-fail/lint-output-format-2.rs index ef072d4bbb391..29d7c6caec468 100644 --- a/src/test/compile-fail/lint-output-format-2.rs +++ b/src/test/compile-fail/lint-output-format-2.rs @@ -18,13 +18,12 @@ extern crate lint_output_format; use lint_output_format::{foo, bar}; -//~^ WARNING use of deprecated item: text +//~^ WARNING use of deprecated item 'lint_output_format::foo': text //~| NOTE #[warn(deprecated)] on by default #[rustc_error] fn main() { //~ ERROR: compilation successful let _x = foo(); - //~^ WARNING use of deprecated item: text - //~| NOTE #[warn(deprecated)] on by default + //~^ WARNING use of deprecated item 'lint_output_format::foo': text let _y = bar(); } diff --git a/src/test/compile-fail/lint-stability-deprecated.rs b/src/test/compile-fail/lint-stability-deprecated.rs index de455afbd6629..df5c3dddcde32 100644 --- a/src/test/compile-fail/lint-stability-deprecated.rs +++ b/src/test/compile-fail/lint-stability-deprecated.rs @@ -12,6 +12,7 @@ // aux-build:inherited_stability.rs // aux-build:stability_cfg1.rs // aux-build:stability_cfg2.rs +// ignore-tidy-linelength #![warn(deprecated)] #![allow(dead_code, unused_extern_crates)] @@ -32,41 +33,41 @@ mod cross_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ WARN use of deprecated item - foo.method_deprecated(); //~ WARN use of deprecated item - Foo::method_deprecated(&foo); //~ WARN use of deprecated item - ::method_deprecated(&foo); //~ WARN use of deprecated item - foo.trait_deprecated(); //~ WARN use of deprecated item - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - - deprecated_text(); //~ WARN use of deprecated item: text - foo.method_deprecated_text(); //~ WARN use of deprecated item: text - Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::method_deprecated_text(&foo); //~ WARN use of deprecated item: text - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - - deprecated_unstable(); //~ WARN use of deprecated item - foo.method_deprecated_unstable(); //~ WARN use of deprecated item - Foo::method_deprecated_unstable(&foo); //~ WARN use of deprecated item - ::method_deprecated_unstable(&foo); //~ WARN use of deprecated item - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item - Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - - deprecated_unstable_text(); //~ WARN use of deprecated item: text - foo.method_deprecated_unstable_text(); //~ WARN use of deprecated item: text - Foo::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - ::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text + deprecated(); //~ WARN use of deprecated item 'lint_stability::deprecated' + foo.method_deprecated(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' + Foo::method_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' + ::method_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' + foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + + deprecated_text(); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text + foo.method_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text + Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text + ::method_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + + deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable' + foo.method_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' + Foo::method_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' + ::method_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' + foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + + deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable_text': text + foo.method_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text + Foo::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text + ::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text unstable(); foo.method_unstable(); @@ -106,31 +107,30 @@ mod cross_crate { struct S1(T::TypeUnstable); struct S2(T::TypeDeprecated); - //~^ WARN use of deprecated item - //~| WARN use of deprecated item + //~^ WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text - let _ = DeprecatedStruct { //~ WARN use of deprecated item - i: 0 //~ WARN use of deprecated item + let _ = DeprecatedStruct { //~ WARN use of deprecated item 'lint_stability::DeprecatedStruct' + i: 0 //~ WARN use of deprecated item 'lint_stability::DeprecatedStruct::i' }; let _ = DeprecatedUnstableStruct { - //~^ WARN use of deprecated item - i: 0 //~ WARN use of deprecated item + //~^ WARN use of deprecated item 'lint_stability::DeprecatedUnstableStruct' + i: 0 //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableStruct::i' }; let _ = UnstableStruct { i: 0 }; let _ = StableStruct { i: 0 }; - let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item - let _ = DeprecatedUnstableUnitStruct; //~ WARN use of deprecated item + let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item 'lint_stability::DeprecatedUnitStruct' + let _ = DeprecatedUnstableUnitStruct; //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableUnitStruct' let _ = UnstableUnitStruct; let _ = StableUnitStruct; - let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item - let _ = Enum::DeprecatedUnstableVariant; //~ WARN use of deprecated item + let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item 'lint_stability::Enum::DeprecatedVariant' + let _ = Enum::DeprecatedUnstableVariant; //~ WARN use of deprecated item 'lint_stability::Enum::DeprecatedUnstableVariant' let _ = Enum::UnstableVariant; let _ = Enum::StableVariant; - let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item - let _ = DeprecatedUnstableTupleStruct (1); //~ WARN use of deprecated item + let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item 'lint_stability::DeprecatedTupleStruct' + let _ = DeprecatedUnstableTupleStruct (1); //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableTupleStruct' let _ = UnstableTupleStruct (1); let _ = StableTupleStruct (1); @@ -139,28 +139,28 @@ mod cross_crate { // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels // on macros themselves are not yet linted. - macro_test_arg!(deprecated_text()); //~ WARN use of deprecated item: text - macro_test_arg!(deprecated_unstable_text()); //~ WARN use of deprecated item: text - macro_test_arg!(macro_test_arg!(deprecated_text())); //~ WARN use of deprecated item: text + macro_test_arg!(deprecated_text()); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text + macro_test_arg!(deprecated_unstable_text()); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable_text': text + macro_test_arg!(macro_test_arg!(deprecated_text())); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ WARN use of deprecated item - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item - Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text + foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text foo.trait_unstable(); Trait::trait_unstable(&foo); ::trait_unstable(&foo); @@ -176,10 +176,10 @@ mod cross_crate { } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ WARN use of deprecated item - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text + foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text + foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text foo.trait_unstable(); foo.trait_unstable_text(); foo.trait_stable(); @@ -188,9 +188,9 @@ mod cross_crate { struct S; impl UnstableTrait for S { } - impl DeprecatedTrait for S {} //~ WARN use of deprecated item: text + impl DeprecatedTrait for S {} //~ WARN use of deprecated item 'lint_stability::DeprecatedTrait': text trait LocalTrait : UnstableTrait { } - trait LocalTrait2 : DeprecatedTrait { } //~ WARN use of deprecated item: text + trait LocalTrait2 : DeprecatedTrait { } //~ WARN use of deprecated item 'lint_stability::DeprecatedTrait': text impl Trait for S { fn trait_stable(&self) {} @@ -209,7 +209,7 @@ mod inheritance { stable_mod::unstable(); stable_mod::stable(); - unstable_mod::deprecated(); //~ WARN use of deprecated item + unstable_mod::deprecated(); //~ WARN use of deprecated item 'inheritance::inherited_stability::unstable_mod::deprecated': text unstable_mod::unstable(); let _ = Unstable::UnstableVariant; @@ -331,23 +331,23 @@ mod this_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ WARN use of deprecated item - foo.method_deprecated(); //~ WARN use of deprecated item - Foo::method_deprecated(&foo); //~ WARN use of deprecated item - ::method_deprecated(&foo); //~ WARN use of deprecated item - foo.trait_deprecated(); //~ WARN use of deprecated item - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - - deprecated_text(); //~ WARN use of deprecated item: text - foo.method_deprecated_text(); //~ WARN use of deprecated item: text - Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::method_deprecated_text(&foo); //~ WARN use of deprecated item: text - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text + deprecated(); //~ WARN use of deprecated item 'this_crate::deprecated' + foo.method_deprecated(); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' + Foo::method_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' + ::method_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' + foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + + deprecated_text(); //~ WARN use of deprecated item 'this_crate::deprecated_text': text + foo.method_deprecated_text(); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + ::method_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text unstable(); foo.method_unstable(); @@ -386,34 +386,34 @@ mod this_crate { ::trait_stable_text(&foo); let _ = DeprecatedStruct { - //~^ WARN use of deprecated item - i: 0 //~ WARN use of deprecated item + //~^ WARN use of deprecated item 'this_crate::DeprecatedStruct' + i: 0 //~ WARN use of deprecated item 'this_crate::DeprecatedStruct::i' }; let _ = UnstableStruct { i: 0 }; let _ = StableStruct { i: 0 }; - let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item + let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item 'this_crate::DeprecatedUnitStruct' let _ = UnstableUnitStruct; let _ = StableUnitStruct; - let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item + let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item 'this_crate::Enum::DeprecatedVariant' let _ = Enum::UnstableVariant; let _ = Enum::StableVariant; - let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item + let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item 'this_crate::DeprecatedTupleStruct' let _ = UnstableTupleStruct (1); let _ = StableTupleStruct (1); } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ WARN use of deprecated item - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - ::trait_deprecated(&foo); //~ WARN use of deprecated item - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text + foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text foo.trait_unstable(); Trait::trait_unstable(&foo); ::trait_unstable(&foo); @@ -429,8 +429,8 @@ mod this_crate { } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ WARN use of deprecated item - foo.trait_deprecated_text(); //~ WARN use of deprecated item: text + foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' + foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text foo.trait_unstable(); foo.trait_unstable_text(); foo.trait_stable(); @@ -440,7 +440,7 @@ mod this_crate { #[rustc_deprecated(since = "1.0.0", reason = "text")] fn test_fn_body() { fn fn_in_body() {} - fn_in_body(); //~ WARN use of deprecated item: text + fn_in_body(); //~ WARN use of deprecated item 'this_crate::test_fn_body::fn_in_body': text } impl MethodTester { @@ -448,7 +448,7 @@ mod this_crate { #[rustc_deprecated(since = "1.0.0", reason = "text")] fn test_method_body(&self) { fn fn_in_body() {} - fn_in_body(); //~ WARN use of deprecated item: text + fn_in_body(); //~ WARN use of deprecated item 'this_crate::MethodTester::test_method_body::fn_in_body': text } } @@ -460,9 +460,9 @@ mod this_crate { struct S; - impl DeprecatedTrait for S { } //~ WARN use of deprecated item + impl DeprecatedTrait for S { } //~ WARN use of deprecated item 'this_crate::DeprecatedTrait' - trait LocalTrait : DeprecatedTrait { } //~ WARN use of deprecated item + trait LocalTrait : DeprecatedTrait { } //~ WARN use of deprecated item 'this_crate::DeprecatedTrait' } #[rustc_error] fn main() {} //~ ERROR: compilation successful diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs index 94e189aa47f6f..bee5a2c45be6d 100644 --- a/src/test/compile-fail/lint-unconditional-recursion.rs +++ b/src/test/compile-fail/lint-unconditional-recursion.rs @@ -10,19 +10,7 @@ #![deny(unconditional_recursion)] //~^ NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here + #![allow(dead_code)] fn foo() { //~ ERROR function cannot return without recurring foo(); //~ NOTE recursive call site diff --git a/src/test/compile-fail/lint-unexported-no-mangle.rs b/src/test/compile-fail/lint-unexported-no-mangle.rs index 216fcf9353578..cd64dfa7a47dd 100644 --- a/src/test/compile-fail/lint-unexported-no-mangle.rs +++ b/src/test/compile-fail/lint-unexported-no-mangle.rs @@ -10,9 +10,8 @@ // compile-flags:-F private_no_mangle_fns -F no_mangle_const_items -F private_no_mangle_statics -// FIXME(#19495) no_mangle'ing main ICE's. #[no_mangle] -fn foo() { //~ ERROR function foo is marked #[no_mangle], but not exported +fn foo() { //~ ERROR function is marked #[no_mangle], but not exported } #[allow(dead_code)] @@ -31,7 +30,7 @@ pub static BAR: u64 = 1; #[allow(dead_code)] #[no_mangle] -static PRIVATE_BAR: u64 = 1; //~ ERROR static PRIVATE_BAR is marked #[no_mangle], but not exported +static PRIVATE_BAR: u64 = 1; //~ ERROR static is marked #[no_mangle], but not exported fn main() { diff --git a/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs b/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs index 9d246f8ea5e0e..f28906ddb955f 100644 --- a/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs +++ b/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs @@ -8,11 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + fn test() { let v: isize; loop { - v = 1; //~ ERROR re-assignment of immutable variable - //~^ NOTE re-assignment of immutable variable + v = 1; //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `v` + //[ast]~| NOTE cannot assign twice to immutable variable + //[mir]~| NOTE cannot assign twice to immutable variable v.clone(); // just to prevent liveness warnings } } diff --git a/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs b/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs index e1eb3246137d2..594cc0761214a 100644 --- a/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs +++ b/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs @@ -8,11 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + fn test() { let v: isize; - v = 2; //~ NOTE first assignment - v += 1; //~ ERROR re-assignment of immutable variable - //~| NOTE re-assignment of immutable + v = 2; //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment + v += 1; //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `v` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable v.clone(); } diff --git a/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs b/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs new file mode 100644 index 0000000000000..b4fb33ca15e26 --- /dev/null +++ b/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs @@ -0,0 +1,26 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + +fn test() { + let b = Box::new(1); //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment + drop(b); + b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `b` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable + drop(b); +} + +fn main() { +} diff --git a/src/test/compile-fail/liveness-assign-imm-local-with-init.rs b/src/test/compile-fail/liveness-assign-imm-local-with-init.rs index 2468c91f34bbd..7204b5d5f2e16 100644 --- a/src/test/compile-fail/liveness-assign-imm-local-with-init.rs +++ b/src/test/compile-fail/liveness-assign-imm-local-with-init.rs @@ -8,11 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Zborrowck=mir + fn test() { - let v: isize = 1; //~ NOTE first assignment + let v: isize = 1; //[ast]~ NOTE first assignment + //[mir]~^ NOTE first assignment v.clone(); - v = 2; //~ ERROR re-assignment of immutable variable - //~| NOTE re-assignment of immutable + v = 2; //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `v` + //[ast]~| NOTE cannot assign twice to immutable + //[mir]~| NOTE cannot assign twice to immutable v.clone(); } diff --git a/src/test/compile-fail/macro-expanded-include/test.rs b/src/test/compile-fail/macro-expanded-include/test.rs index bcc2c10653f9c..4afb61ab76cf7 100644 --- a/src/test/compile-fail/macro-expanded-include/test.rs +++ b/src/test/compile-fail/macro-expanded-include/test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no asm! support #![feature(asm, rustc_attrs)] #![allow(unused)] diff --git a/src/test/compile-fail/match-range-fail.rs b/src/test/compile-fail/match-range-fail.rs index f89b3e39390d3..355ff6404cea7 100644 --- a/src/test/compile-fail/match-range-fail.rs +++ b/src/test/compile-fail/match-range-fail.rs @@ -15,6 +15,7 @@ fn main() { //~^^ ERROR only char and numeric types are allowed in range //~| start type: &'static str //~| end type: &'static str + //~| ERROR non-reference pattern used to match a reference match "wow" { 10 ... "what" => () @@ -22,6 +23,7 @@ fn main() { //~^^ ERROR only char and numeric types are allowed in range //~| start type: {integer} //~| end type: &'static str + //~| ERROR non-reference pattern used to match a reference match 5 { 'c' ... 100 => { } diff --git a/src/test/compile-fail/match-vec-mismatch.rs b/src/test/compile-fail/match-vec-mismatch.rs index 11c04e3803538..fed68da006889 100644 --- a/src/test/compile-fail/match-vec-mismatch.rs +++ b/src/test/compile-fail/match-vec-mismatch.rs @@ -17,8 +17,9 @@ fn main() { _ => { } }; + // Note that this one works with default binding modes. match &[0, 1, 2] { - [..] => {} //~ ERROR expected an array or slice, found `&[{integer}; 3]` + [..] => {} //~ ERROR non-reference pattern used to match a reference }; match &[0, 1, 2] { diff --git a/src/test/compile-fail/mut-pattern-internal-mutability.rs b/src/test/compile-fail/mut-pattern-internal-mutability.rs index 3a84bd6565e8d..72727cdfe54d2 100644 --- a/src/test/compile-fail/mut-pattern-internal-mutability.rs +++ b/src/test/compile-fail/mut-pattern-internal-mutability.rs @@ -9,15 +9,14 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir fn main() { let foo = &mut 1; let &mut x = foo; - x += 1; //[ast]~ ERROR re-assignment of immutable variable - //[mir]~^ ERROR re-assignment of immutable variable `x` (Ast) - //[mir]~| ERROR re-assignment of immutable variable `x` (Mir) + x += 1; //[ast]~ ERROR cannot assign twice to immutable variable + //[mir]~^ ERROR cannot assign twice to immutable variable `x` // explicitly mut-ify internals let &mut mut x = foo; @@ -26,6 +25,5 @@ fn main() { // check borrowing is detected successfully let &mut ref x = foo; *foo += 1; //[ast]~ ERROR cannot assign to `*foo` because it is borrowed - //[mir]~^ ERROR cannot assign to `*foo` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `(*foo)` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `*foo` because it is borrowed } diff --git a/src/test/compile-fail/name-clash-nullary.rs b/src/test/compile-fail/name-clash-nullary.rs deleted file mode 100644 index bfcb0f4e86329..0000000000000 --- a/src/test/compile-fail/name-clash-nullary.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::option::*; - -fn main() { - let None: isize = 42; //~ ERROR let bindings cannot shadow unit variants - log(debug, None); - //~^ ERROR cannot find function `log` in this scope - //~| ERROR cannot find value `debug` in this scope -} diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs new file mode 100644 index 0000000000000..fdc650a072131 --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs @@ -0,0 +1,50 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// compile-flags:-Zborrowck=compare -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + + +fn main() { +} + +fn nll_fail() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + data.0 = 'f'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + data.0 = 'g'; + //~^ ERROR (Ast) [E0506] + //~| ERROR (Mir) [E0506] + capitalize(c); +} + +fn nll_ok() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + //~^ ERROR (Ast) [E0506] + data.0 = 'f'; + //~^ ERROR (Ast) [E0506] + data.0 = 'g'; + //~^ ERROR (Ast) [E0506] +} + +fn capitalize(_: &mut char) { +} diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs new file mode 100644 index 0000000000000..f22d2fc23e057 --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs @@ -0,0 +1,49 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// compile-flags:-Zborrowck=compare -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +fn main() { +} + +fn nll_fail() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + data.push('e'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + data.push('f'); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + capitalize(slice); +} + +fn nll_ok() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + //~^ ERROR (Ast) [E0499] + data.push('e'); + //~^ ERROR (Ast) [E0499] + data.push('f'); + //~^ ERROR (Ast) [E0499] +} + +fn capitalize(_: &mut [char]) { +} diff --git a/src/test/compile-fail/nll/reference-carried-through-struct-field.rs b/src/test/compile-fail/nll/reference-carried-through-struct-field.rs new file mode 100644 index 0000000000000..1c1fc4799c3d1 --- /dev/null +++ b/src/test/compile-fail/nll/reference-carried-through-struct-field.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//revisions: ast mir +//[mir] compile-flags: -Z borrowck=mir -Z nll + +#![allow(unused_assignments)] + +struct Wrap<'a> { w: &'a mut u32 } + +fn foo() { + let mut x = 22; + let wrapper = Wrap { w: &mut x }; + x += 1; //[ast]~ ERROR cannot assign to `x` because it is borrowed [E0506] + //[mir]~^ ERROR cannot assign to `x` because it is borrowed [E0506] + //[mir]~^^ ERROR cannot use `x` because it was mutably borrowed [E0503] + *wrapper.w += 1; +} + +fn main() { } diff --git a/src/test/compile-fail/nll/region-ends-after-if-condition.rs b/src/test/compile-fail/nll/region-ends-after-if-condition.rs new file mode 100644 index 0000000000000..1128d65af95c5 --- /dev/null +++ b/src/test/compile-fail/nll/region-ends-after-if-condition.rs @@ -0,0 +1,46 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// compile-flags:-Zborrowck=compare -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn foo1() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0502] + } +} + +fn foo2() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0502] + //~| ERROR (Mir) [E0502] + } + drop(value); +} + +fn main() { } diff --git a/src/test/compile-fail/nll/return_from_loop.rs b/src/test/compile-fail/nll/return_from_loop.rs new file mode 100644 index 0000000000000..7ed59ef2a879b --- /dev/null +++ b/src/test/compile-fail/nll/return_from_loop.rs @@ -0,0 +1,49 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// compile-flags:-Zborrowck=compare -Znll + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn main() { +} + +fn nll_fail() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0499] + //~| ERROR (Mir) [E0499] + value.len(); + return; + } +} + +fn nll_ok() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR (Ast) [E0499] + return; + } +} diff --git a/src/test/compile-fail/no-patterns-in-args-2.rs b/src/test/compile-fail/no-patterns-in-args-2.rs index 967c292fa68d2..4d2412c34a5fa 100644 --- a/src/test/compile-fail/no-patterns-in-args-2.rs +++ b/src/test/compile-fail/no-patterns-in-args-2.rs @@ -14,7 +14,6 @@ trait Tr { fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in methods without bodies //~^ WARN was previously accepted fn f2(&arg: u8); //~ ERROR patterns aren't allowed in methods without bodies - //~^ WARN was previously accepted fn g1(arg: u8); // OK fn g2(_: u8); // OK #[allow(anonymous_parameters)] diff --git a/src/test/compile-fail/no-patterns-in-args-macro.rs b/src/test/compile-fail/no-patterns-in-args-macro.rs new file mode 100644 index 0000000000000..f85ce8f57ea71 --- /dev/null +++ b/src/test/compile-fail/no-patterns-in-args-macro.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! m { + ($pat: pat) => { + trait Tr { + fn trait_method($pat: u8); + } + + type A = fn($pat: u8); + + extern { + fn foreign_fn($pat: u8); + } + } +} + +mod good_pat { + m!(good_pat); // OK +} + +mod bad_pat { + m!((bad, pat)); + //~^ ERROR patterns aren't allowed in function pointer types + //~| ERROR patterns aren't allowed in foreign function declarations + //~| ERROR patterns aren't allowed in methods without bodies +} + +fn main() {} diff --git a/src/test/compile-fail/no-patterns-in-args.rs b/src/test/compile-fail/no-patterns-in-args.rs index b0278476998dd..081d6caaa13c9 100644 --- a/src/test/compile-fail/no-patterns-in-args.rs +++ b/src/test/compile-fail/no-patterns-in-args.rs @@ -11,21 +11,17 @@ extern { fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in foreign function declarations //~^ NOTE pattern not allowed in foreign function - //~| NOTE this is a recent error fn f2(&arg: u8); //~ ERROR patterns aren't allowed in foreign function declarations //~^ NOTE pattern not allowed in foreign function fn f3(arg @ _: u8); //~ ERROR patterns aren't allowed in foreign function declarations //~^ NOTE pattern not allowed in foreign function - //~| NOTE this is a recent error fn g1(arg: u8); // OK fn g2(_: u8); // OK // fn g3(u8); // Not yet } type A1 = fn(mut arg: u8); //~ ERROR patterns aren't allowed in function pointer types - //~^ NOTE this is a recent error type A2 = fn(&arg: u8); //~ ERROR patterns aren't allowed in function pointer types - //~^ NOTE this is a recent error type B1 = fn(arg: u8); // OK type B2 = fn(_: u8); // OK type B3 = fn(u8); // OK diff --git a/src/test/compile-fail/nolink-with-link-args.rs b/src/test/compile-fail/nolink-with-link-args.rs index c4c75bc760f4c..6dfd74f541e6e 100644 --- a/src/test/compile-fail/nolink-with-link-args.rs +++ b/src/test/compile-fail/nolink-with-link-args.rs @@ -9,6 +9,7 @@ // except according to those terms. // error-pattern:aFdEfSeVEE +// compile-flags: -Z linker-flavor=ld /* We're testing that link_args are indeed passed when nolink is specified. So we try to compile with junk link_args and make sure they are visible in diff --git a/src/test/compile-fail/non-copyable-void.rs b/src/test/compile-fail/non-copyable-void.rs index 4383f3ede0db5..63e5f963754e2 100644 --- a/src/test/compile-fail/non-copyable-void.rs +++ b/src/test/compile-fail/non-copyable-void.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #![feature(libc)] extern crate libc; diff --git a/src/test/compile-fail/non-interger-atomic.rs b/src/test/compile-fail/non-interger-atomic.rs index 50240b475578c..a51a9e518ce5f 100644 --- a/src/test/compile-fail/non-interger-atomic.rs +++ b/src/test/compile-fail/non-interger-atomic.rs @@ -10,92 +10,91 @@ #![feature(core_intrinsics)] #![allow(warnings)] +#![crate_type = "rlib"] use std::intrinsics; #[derive(Copy, Clone)] -struct Foo(i64); -type Bar = &'static Fn(); -type Quux = [u8; 100]; +pub struct Foo(i64); +pub type Bar = &'static Fn(); +pub type Quux = [u8; 100]; -unsafe fn test_bool_load(p: &mut bool, v: bool) { +pub unsafe fn test_bool_load(p: &mut bool, v: bool) { intrinsics::atomic_load(p); //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `bool` } -unsafe fn test_bool_store(p: &mut bool, v: bool) { +pub unsafe fn test_bool_store(p: &mut bool, v: bool) { intrinsics::atomic_store(p, v); //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `bool` } -unsafe fn test_bool_xchg(p: &mut bool, v: bool) { +pub unsafe fn test_bool_xchg(p: &mut bool, v: bool) { intrinsics::atomic_xchg(p, v); //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `bool` } -unsafe fn test_bool_cxchg(p: &mut bool, v: bool) { +pub unsafe fn test_bool_cxchg(p: &mut bool, v: bool) { intrinsics::atomic_cxchg(p, v, v); //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `bool` } -unsafe fn test_Foo_load(p: &mut Foo, v: Foo) { +pub unsafe fn test_Foo_load(p: &mut Foo, v: Foo) { intrinsics::atomic_load(p); //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `Foo` } -unsafe fn test_Foo_store(p: &mut Foo, v: Foo) { +pub unsafe fn test_Foo_store(p: &mut Foo, v: Foo) { intrinsics::atomic_store(p, v); //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `Foo` } -unsafe fn test_Foo_xchg(p: &mut Foo, v: Foo) { +pub unsafe fn test_Foo_xchg(p: &mut Foo, v: Foo) { intrinsics::atomic_xchg(p, v); //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `Foo` } -unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { +pub unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { intrinsics::atomic_cxchg(p, v, v); //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `Foo` } -unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { +pub unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { intrinsics::atomic_load(p); //~^ ERROR expected basic integer type, found `&std::ops::Fn()` } -unsafe fn test_Bar_store(p: &mut Bar, v: Bar) { +pub unsafe fn test_Bar_store(p: &mut Bar, v: Bar) { intrinsics::atomic_store(p, v); //~^ ERROR expected basic integer type, found `&std::ops::Fn()` } -unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) { +pub unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) { intrinsics::atomic_xchg(p, v); //~^ ERROR expected basic integer type, found `&std::ops::Fn()` } -unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { +pub unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { intrinsics::atomic_cxchg(p, v, v); //~^ ERROR expected basic integer type, found `&std::ops::Fn()` } -unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { +pub unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { intrinsics::atomic_load(p); //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` } -unsafe fn test_Quux_store(p: &mut Quux, v: Quux) { +pub unsafe fn test_Quux_store(p: &mut Quux, v: Quux) { intrinsics::atomic_store(p, v); //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `[u8; 100]` } -unsafe fn test_Quux_xchg(p: &mut Quux, v: Quux) { +pub unsafe fn test_Quux_xchg(p: &mut Quux, v: Quux) { intrinsics::atomic_xchg(p, v); //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `[u8; 100]` } -unsafe fn test_Quux_cxchg(p: &mut Quux, v: Quux) { +pub unsafe fn test_Quux_cxchg(p: &mut Quux, v: Quux) { intrinsics::atomic_cxchg(p, v, v); //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `[u8; 100]` } - -fn main() {} diff --git a/src/test/compile-fail/object-lifetime-default-from-box-error.rs b/src/test/compile-fail/object-lifetime-default-from-box-error.rs index c0dd5200f6cb4..c50f425b2c01d 100644 --- a/src/test/compile-fail/object-lifetime-default-from-box-error.rs +++ b/src/test/compile-fail/object-lifetime-default-from-box-error.rs @@ -38,7 +38,7 @@ fn store(ss: &mut SomeStruct, b: Box) { fn store1<'b>(ss: &mut SomeStruct, b: Box) { // Here we override the lifetimes explicitly, and so naturally we get an error. - ss.r = b; //~ ERROR cannot infer an appropriate lifetime + ss.r = b; //~ ERROR 41:12: 41:13: explicit lifetime required in the type of `ss` [E0621] } fn main() { diff --git a/src/test/compile-fail/object-lifetime-default-mybox.rs b/src/test/compile-fail/object-lifetime-default-mybox.rs index 014b0c1e80e71..54657e76e9702 100644 --- a/src/test/compile-fail/object-lifetime-default-mybox.rs +++ b/src/test/compile-fail/object-lifetime-default-mybox.rs @@ -34,7 +34,7 @@ fn load1<'a,'b>(a: &'a MyBox, b: &'b MyBox) -> &'b MyBox { - a //~ ERROR E0312 + a //~ ERROR lifetime mismatch } fn load2<'a>(ss: &MyBox) -> MyBox { diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs index a276af83dee25..5d162fe944ec8 100644 --- a/src/test/compile-fail/occurs-check-2.rs +++ b/src/test/compile-fail/occurs-check-2.rs @@ -16,7 +16,5 @@ fn main() { g = f; f = box g; //~^ ERROR mismatched types - //~| expected type `_` - //~| found type `std::boxed::Box<_>` //~| cyclic type of infinite size } diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs index 5b6a11e58c27c..2c784365ea989 100644 --- a/src/test/compile-fail/occurs-check.rs +++ b/src/test/compile-fail/occurs-check.rs @@ -14,7 +14,5 @@ fn main() { let f; f = box f; //~^ ERROR mismatched types - //~| expected type `_` - //~| found type `std::boxed::Box<_>` //~| cyclic type of infinite size } diff --git a/src/test/compile-fail/const-fn-feature-flags.rs b/src/test/compile-fail/outlives-associated-types.rs similarity index 60% rename from src/test/compile-fail/const-fn-feature-flags.rs rename to src/test/compile-fail/outlives-associated-types.rs index 823cb89b365ca..778394c9fc8ad 100644 --- a/src/test/compile-fail/const-fn-feature-flags.rs +++ b/src/test/compile-fail/outlives-associated-types.rs @@ -8,17 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test use of const fns in std using individual feature gates. +// Test that the outlives computation runs for now... -use std::cell::Cell; +#![feature(rustc_attrs)] -const CELL: Cell = Cell::new(42); //~ERROR not yet stable as a const fn - //~^HELP #![feature(const_cell_new)] +//todo add all the test cases +// https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md#example-1-a-reference -fn main() { - let v = CELL.get(); - CELL.set(v+1); - - assert_eq!(CELL.get(), v); +#[rustc_outlives] +struct Direct<'a, T> { //~ ERROR 19:1: 21:2: [] [E0640] + field: &'a T } +fn main() { } diff --git a/src/test/compile-fail/panic-runtime/libtest-unwinds.rs b/src/test/compile-fail/panic-runtime/libtest-unwinds.rs index 5f6f4ecbd6f6e..71751034c39b9 100644 --- a/src/test/compile-fail/panic-runtime/libtest-unwinds.rs +++ b/src/test/compile-fail/panic-runtime/libtest-unwinds.rs @@ -10,6 +10,7 @@ // error-pattern:is not compiled with this crate's panic strategy `abort` // compile-flags:-C panic=abort +// ignore-wasm32-bare compiled with panic=abort by default #![feature(test)] diff --git a/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs b/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs index 885b3e6dbb941..f886aac9a1041 100644 --- a/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs +++ b/src/test/compile-fail/panic-runtime/transitive-link-a-bunch.rs @@ -14,6 +14,7 @@ // aux-build:wants-panic-runtime-abort.rs // aux-build:panic-runtime-lang-items.rs // error-pattern: is not compiled with this crate's panic strategy `unwind` +// ignore-wasm32-bare compiled with panic=abort by default #![no_std] diff --git a/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs b/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs index 88ad36f310eb5..dda92d9a56074 100644 --- a/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs +++ b/src/test/compile-fail/panic-runtime/want-unwind-got-abort.rs @@ -11,6 +11,7 @@ // error-pattern:is incompatible with this crate's strategy of `unwind` // aux-build:panic-runtime-abort.rs // aux-build:panic-runtime-lang-items.rs +// ignore-wasm32-bare compiled with panic=abort by default #![no_std] diff --git a/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs b/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs index c42a25a553a7f..49f719057d2ff 100644 --- a/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs +++ b/src/test/compile-fail/panic-runtime/want-unwind-got-abort2.rs @@ -12,6 +12,7 @@ // aux-build:panic-runtime-abort.rs // aux-build:wants-panic-runtime-abort.rs // aux-build:panic-runtime-lang-items.rs +// ignore-wasm32-bare compiled with panic=abort by default #![no_std] diff --git a/src/test/compile-fail/pat-slice-old-style.rs b/src/test/compile-fail/pat-slice-old-style.rs index ccb25d859acba..d49ce56ccf6e7 100644 --- a/src/test/compile-fail/pat-slice-old-style.rs +++ b/src/test/compile-fail/pat-slice-old-style.rs @@ -10,13 +10,20 @@ #![feature(slice_patterns)] +// NB: this test was introduced in #23121 and will have to change when default match binding modes +// stabilizes. + fn slice_pat(x: &[u8]) { // OLD! match x { - [a, b..] => {} - //~^ ERROR expected an array or slice, found `&[u8]` - //~| HELP the semantics of slice patterns changed recently; see issue #23121 + [a, b..] => {}, + //~^ ERROR non-reference pattern used to match a reference + //~| HELP add #![feature(match_default_bindings)] to the crate attributes to enable + //~| HELP consider using + _ => panic!(), } } -fn main() {} +fn main() { + slice_pat("foo".as_bytes()); +} diff --git a/src/test/compile-fail/patkind-litrange-no-expr.rs b/src/test/compile-fail/patkind-litrange-no-expr.rs index afb2cbb7db397..d57a23f26c479 100644 --- a/src/test/compile-fail/patkind-litrange-no-expr.rs +++ b/src/test/compile-fail/patkind-litrange-no-expr.rs @@ -28,8 +28,7 @@ enum_number!(Change { Pos = 1, Neg = -1, Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns - //~^ ERROR arbitrary expressions aren't allowed in patterns - //~^^ ERROR only char and numeric types are allowed in range patterns + //~^ ERROR only char and numeric types are allowed in range patterns }); fn main() {} diff --git a/src/test/compile-fail/pattern-binding-disambiguation.rs b/src/test/compile-fail/pattern-binding-disambiguation.rs new file mode 100644 index 0000000000000..c740f6bb47c3f --- /dev/null +++ b/src/test/compile-fail/pattern-binding-disambiguation.rs @@ -0,0 +1,67 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct UnitStruct; +struct TupleStruct(); +struct BracedStruct{} + +enum E { + UnitVariant, + TupleVariant(), + BracedVariant{}, +} +use E::*; + +const CONST: () = (); +static STATIC: () = (); + +fn function() {} + +fn main() { + let doesnt_matter = 0; + + match UnitStruct { + UnitStruct => {} // OK, `UnitStruct` is a unit struct pattern + } + match doesnt_matter { + TupleStruct => {} //~ ERROR match bindings cannot shadow tuple structs + } + match doesnt_matter { + BracedStruct => {} // OK, `BracedStruct` is a fresh binding + } + match UnitVariant { + UnitVariant => {} // OK, `UnitVariant` is a unit variant pattern + } + match doesnt_matter { + TupleVariant => {} //~ ERROR match bindings cannot shadow tuple variants + } + match doesnt_matter { + BracedVariant => {} //~ ERROR match bindings cannot shadow struct variants + } + match CONST { + CONST => {} // OK, `CONST` is a const pattern + } + match doesnt_matter { + STATIC => {} //~ ERROR match bindings cannot shadow statics + } + match doesnt_matter { + function => {} // OK, `function` is a fresh binding + } + + let UnitStruct = UnitStruct; // OK, `UnitStruct` is a unit struct pattern + let TupleStruct = doesnt_matter; //~ ERROR let bindings cannot shadow tuple structs + let BracedStruct = doesnt_matter; // OK, `BracedStruct` is a fresh binding + let UnitVariant = UnitVariant; // OK, `UnitVariant` is a unit variant pattern + let TupleVariant = doesnt_matter; //~ ERROR let bindings cannot shadow tuple variants + let BracedVariant = doesnt_matter; //~ ERROR let bindings cannot shadow struct variants + let CONST = CONST; // OK, `CONST` is a const pattern + let STATIC = doesnt_matter; //~ ERROR let bindings cannot shadow statics + let function = doesnt_matter; // OK, `function` is a fresh binding +} diff --git a/src/test/compile-fail/phantom-oibit.rs b/src/test/compile-fail/phantom-oibit.rs index c84927ea26639..1c1cb396a54f2 100644 --- a/src/test/compile-fail/phantom-oibit.rs +++ b/src/test/compile-fail/phantom-oibit.rs @@ -18,6 +18,7 @@ use std::marker::{PhantomData}; unsafe trait Zen {} +#[allow(auto_impl)] unsafe impl Zen for .. {} unsafe impl<'a, T: 'a> Zen for &'a T where T: Sync {} diff --git a/src/test/compile-fail/privacy-sanity.rs b/src/test/compile-fail/privacy-sanity.rs index 933ec3837dfc7..34082adb8f9a5 100644 --- a/src/test/compile-fail/privacy-sanity.rs +++ b/src/test/compile-fail/privacy-sanity.rs @@ -21,6 +21,7 @@ pub struct S { } struct Ts(pub u8); +#[allow(auto_impl)] pub impl MarkerTr for .. {} //~ ERROR unnecessary visibility qualifier pub impl Tr for S { //~ ERROR unnecessary visibility qualifier pub fn f() {} //~ ERROR unnecessary visibility qualifier @@ -49,6 +50,7 @@ const MAIN: u8 = { } struct Ts(pub u8); + #[allow(auto_impl)] pub impl MarkerTr for .. {} //~ ERROR unnecessary visibility qualifier pub impl Tr for S { //~ ERROR unnecessary visibility qualifier pub fn f() {} //~ ERROR unnecessary visibility qualifier @@ -80,6 +82,7 @@ fn main() { } struct Ts(pub u8); + #[allow(auto_impl)] pub impl MarkerTr for .. {} //~ ERROR unnecessary visibility qualifier pub impl Tr for S { //~ ERROR unnecessary visibility qualifier pub fn f() {} //~ ERROR unnecessary visibility qualifier diff --git a/src/test/compile-fail/privacy/restricted/auxiliary/pub_restricted.rs b/src/test/compile-fail/privacy/restricted/auxiliary/pub_restricted.rs index 82d14ddb502b3..c4ab96c845619 100644 --- a/src/test/compile-fail/privacy/restricted/auxiliary/pub_restricted.rs +++ b/src/test/compile-fail/privacy/restricted/auxiliary/pub_restricted.rs @@ -8,14 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(crate_visibility_modifier)] + pub(crate) struct Crate; + #[derive(Default)] pub struct Universe { pub x: i32, - pub(crate) y: i32 + pub(crate) y: i32, + crate z: i32, } impl Universe { pub fn f(&self) {} pub(crate) fn g(&self) {} + crate fn h(&self) {} } diff --git a/src/test/compile-fail/privacy/restricted/private-in-public.rs b/src/test/compile-fail/privacy/restricted/private-in-public.rs index 0fdfbaa84bb5a..4d3f537779740 100644 --- a/src/test/compile-fail/privacy/restricted/private-in-public.rs +++ b/src/test/compile-fail/privacy/restricted/private-in-public.rs @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(crate_visibility_modifier)] + mod foo { struct Priv; mod bar { use foo::Priv; pub(super) fn f(_: Priv) {} pub(crate) fn g(_: Priv) {} //~ ERROR E0446 + crate fn h(_: Priv) {} //~ ERROR E0446 } } diff --git a/src/test/compile-fail/privacy/restricted/test.rs b/src/test/compile-fail/privacy/restricted/test.rs index 2e065ac051b20..7f076ebf287e2 100644 --- a/src/test/compile-fail/privacy/restricted/test.rs +++ b/src/test/compile-fail/privacy/restricted/test.rs @@ -50,8 +50,10 @@ fn main() { let u = Universe::default(); let _ = u.x; let _ = u.y; //~ ERROR private + let _ = u.z; //~ ERROR private u.f(); u.g(); //~ ERROR private + u.h(); //~ ERROR private } mod pathological { diff --git a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test.rs b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test.rs index d17b604717e70..d4ea76d6c2655 100644 --- a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test.rs +++ b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test.rs @@ -13,5 +13,5 @@ mod foo { struct S1(pub(in foo) (), pub(T), pub(crate) (), pub(((), T))); struct S2(pub((foo)) ()); //~^ ERROR expected `,`, found `(` - //~| ERROR expected one of `;` or `where`, found `(` + //~| ERROR cannot find type `foo` in this scope } diff --git a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test2.rs b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test2.rs index 166d5e27e8d96..fed9432c6a0e9 100644 --- a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test2.rs +++ b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test2.rs @@ -14,7 +14,6 @@ macro_rules! define_struct { struct S2(pub (in foo) ()); struct S3(pub $t ()); //~^ ERROR expected `,`, found `(` - //~| ERROR expected one of `;` or `where`, found `(` } } diff --git a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test3.rs b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test3.rs index edab175f4cd91..dd2cb0e218422 100644 --- a/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test3.rs +++ b/src/test/compile-fail/privacy/restricted/tuple-struct-fields/test3.rs @@ -14,7 +14,6 @@ macro_rules! define_struct { struct S2(pub (in foo) ()); struct S3(pub($t) ()); //~^ ERROR expected `,`, found `(` - //~| ERROR expected one of `;` or `where`, found `(` } } diff --git a/src/test/compile-fail/private-inferred-type-3.rs b/src/test/compile-fail/private-inferred-type-3.rs index fdd9166ef2999..c0ba38b240202 100644 --- a/src/test/compile-fail/private-inferred-type-3.rs +++ b/src/test/compile-fail/private-inferred-type-3.rs @@ -15,7 +15,7 @@ // error-pattern:type `fn() {::method}` is private // error-pattern:type `fn(u8) -> ext::PrivTupleStruct {ext::PrivTupleStruct::{{constructor}}}` is pr // error-pattern:type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct::{{constructor}}}` is priv -// error-pattern:type `fn(&ext::Pub) {>::priv_method}` is private +// error-pattern:type `for<'r> fn(&'r ext::Pub) {>::priv_method}` is private #![feature(decl_macro)] diff --git a/src/test/compile-fail/private-inferred-type.rs b/src/test/compile-fail/private-inferred-type.rs index 973d467b11226..95e3732d61342 100644 --- a/src/test/compile-fail/private-inferred-type.rs +++ b/src/test/compile-fail/private-inferred-type.rs @@ -56,7 +56,7 @@ mod m { PubTupleStruct; //~^ ERROR type `fn(u8) -> m::PubTupleStruct {m::PubTupleStruct::{{constructor}}}` is privat Pub(0u8).priv_method(); - //~^ ERROR type `fn(&m::Pub) {>::priv_method}` is private + //~^ ERROR type `for<'r> fn(&'r m::Pub) {>::priv_method}` is private } trait Trait {} diff --git a/src/test/compile-fail/range_traits-1.rs b/src/test/compile-fail/range_traits-1.rs index cf5c40bd1761d..f1ea8b04e5ace 100644 --- a/src/test/compile-fail/range_traits-1.rs +++ b/src/test/compile-fail/range_traits-1.rs @@ -12,75 +12,38 @@ use std::ops::*; -// FIXME #34229 duplicated errors #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] struct AllTheRanges { a: Range, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type b: RangeTo, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type c: RangeFrom, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type d: RangeFull, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type e: RangeInclusive, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type f: RangeToInclusive, //~^ ERROR PartialOrd //~^^ ERROR Ord - //~^^^ ERROR binary operation - //~^^^^ ERROR binary operation - //~^^^^^ ERROR binary operation - //~^^^^^^ ERROR binary operation - //~^^^^^^^ ERROR binary operation - //~^^^^^^^^ ERROR binary operation - //~^^^^^^^^^ ERROR binary operation - //~^^^^^^^^^^ ERROR binary operation + //~^^^ ERROR binary operation `<` cannot be applied to type + //~^^^^ ERROR binary operation `>` cannot be applied to type } fn main() {} diff --git a/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs b/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs index f886c0255ccbf..e3d96f52e817b 100644 --- a/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs +++ b/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs @@ -21,7 +21,7 @@ fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) { fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) { // Here we try to call `foo` but do not know that `'a` and `'b` are // related as required. - a(x, y); //~ ERROR cannot infer + a(x, y); //~ ERROR 24:7: 24:8: lifetime mismatch [E0623] } fn d() { diff --git a/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs b/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs index bae9608c3f05a..d8d12444dddb3 100644 --- a/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs +++ b/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs @@ -23,7 +23,7 @@ fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) { fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) { // Here we try to call `foo` but do not know that `'a` and `'b` are // related as required. - a(x, y, z); //~ ERROR cannot infer + a(x, y, z); //~ ERROR 26:7: 26:8: lifetime mismatch [E0623] } fn d() { diff --git a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs index 1d4ffe0690d2f..617de2c5dfe84 100644 --- a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs +++ b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs @@ -35,7 +35,6 @@ impl<'a, 't> Foo<'a, 't> for &'a isize { fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) { //~^ ERROR method not compatible with trait - //~^^ ERROR method not compatible with trait // // Note: This is a terrible error message. It is caused // because, in the trait, 'b is early bound, and in the impl, diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs index 1eb36e34ab32e..24e4c5fbd91be 100644 --- a/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs +++ b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs @@ -27,7 +27,7 @@ fn call_into_maybe_owned<'x,F:IntoMaybeOwned<'x>>(f: F) { fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) { // Here the value provided for 'y is 'y, and hence 'y:'x does not hold. - a.bigger_region(b) //~ ERROR cannot infer + a.bigger_region(b) //~ ERROR 30:7: 30:20: lifetime mismatch [E0623] } fn main() { } diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs b/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs index f13d8a60894cb..3e9d2aa6c3bff 100644 --- a/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs +++ b/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs @@ -27,7 +27,7 @@ fn caller1<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { // Here the value provided for 'y is 'b, and hence 'b:'a does not hold. - f.method(b); //~ ERROR cannot infer + f.method(b); //~ ERROR 30:7: 30:13: lifetime mismatch [E0623] } fn caller3<'a,'b:'a,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { diff --git a/src/test/compile-fail/regions-close-object-into-object-5.rs b/src/test/compile-fail/regions-close-object-into-object-5.rs index 152c65cb69bf2..6cbe5234ce0e8 100644 --- a/src/test/compile-fail/regions-close-object-into-object-5.rs +++ b/src/test/compile-fail/regions-close-object-into-object-5.rs @@ -31,7 +31,6 @@ fn f<'a, T, U>(v: Box+'static>) -> Box { //~| ERROR the parameter type `T` may not live long enough //~| ERROR the parameter type `T` may not live long enough //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough } fn main() {} diff --git a/src/test/compile-fail/regions-creating-enums3.rs b/src/test/compile-fail/regions-creating-enums3.rs index 4c8484540aabb..dcc579d26c18f 100644 --- a/src/test/compile-fail/regions-creating-enums3.rs +++ b/src/test/compile-fail/regions-creating-enums3.rs @@ -14,7 +14,7 @@ enum ast<'a> { } fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> { - ast::add(x, y) //~ ERROR cannot infer + ast::add(x, y) //~ ERROR 17:5: 17:19: lifetime mismatch [E0623] } fn main() { diff --git a/src/test/compile-fail/regions-fn-subtyping-return-static.rs b/src/test/compile-fail/regions-fn-subtyping-return-static.rs index ac7dd022c7c46..6be65a5e35905 100644 --- a/src/test/compile-fail/regions-fn-subtyping-return-static.rs +++ b/src/test/compile-fail/regions-fn-subtyping-return-static.rs @@ -58,8 +58,8 @@ fn supply_G() { want_G(bar); want_G(baz); //~^ ERROR mismatched types - //~| expected type `fn(&'cx S) -> &'static S` - //~| found type `fn(&S) -> &S {baz}` + //~| expected type `for<'cx> fn(&'cx S) -> &'static S` + //~| found type `for<'r> fn(&'r S) -> &'r S {baz}` //~| expected concrete lifetime, found bound lifetime parameter 'cx } diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs index 1893395e2b007..073a4f79b05f0 100644 --- a/src/test/compile-fail/regions-free-region-ordering-callee.rs +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -20,13 +20,13 @@ fn ordering1<'a, 'b>(x: &'a &'b usize) -> &'a usize { fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize { // However, it is not safe to assume that 'b <= 'a - &*y //~ ERROR cannot infer + &*y //~ ERROR 23:5: 23:8: lifetime mismatch [E0623] } fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize { // Do not infer an ordering from the return value. let z: &'b usize = &*x; - //~^ ERROR cannot infer + //~^ ERROR 28:24: 28:27: lifetime mismatch [E0623] panic!(); } diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs index 586a8a183a4e6..843c5f512f8f1 100644 --- a/src/test/compile-fail/regions-glb-free-free.rs +++ b/src/test/compile-fail/regions-glb-free-free.rs @@ -22,7 +22,7 @@ mod argparse { impl<'a> Flag<'a> { pub fn set_desc(self, s: &str) -> Flag<'a> { - Flag { //~ ERROR cannot infer + Flag { //~ ERROR 25:13: 30:14: explicit lifetime required in the type of `s` [E0621] name: self.name, desc: s, max_count: self.max_count, diff --git a/src/test/compile-fail/regions-infer-at-fn-not-param.rs b/src/test/compile-fail/regions-infer-at-fn-not-param.rs index 0c250e38258ce..ec73bf90b6e52 100644 --- a/src/test/compile-fail/regions-infer-at-fn-not-param.rs +++ b/src/test/compile-fail/regions-infer-at-fn-not-param.rs @@ -21,7 +21,7 @@ struct not_parameterized2 { } fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p } -//~^ ERROR mismatched types +//~^ ERROR explicit lifetime required in the type of `p` fn take3(p: not_parameterized1) -> not_parameterized1 { p } fn take4(p: not_parameterized2) -> not_parameterized2 { p } diff --git a/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs index ef1c58bf972e3..5955619ea92ad 100644 --- a/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs +++ b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs @@ -21,7 +21,7 @@ fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) { fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) { // Here we try to call `foo` but do not know that `'a` and `'b` are // related as required. - a(x, y); //~ ERROR E0495 + a(x, y); //~ ERROR 24:7: 24:8: lifetime mismatch [E0623] } fn d() { diff --git a/src/test/compile-fail/regions-normalize-in-where-clause-list.rs b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs new file mode 100644 index 0000000000000..68642598ed2df --- /dev/null +++ b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs @@ -0,0 +1,37 @@ +// 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. + +// Test that we are able to normalize in the list of where-clauses, +// even if `'a: 'b` is required. + +trait Project<'a, 'b> { + type Item; +} + +impl<'a, 'b> Project<'a, 'b> for () + where 'a: 'b +{ + type Item = (); +} + +// No error here, we have 'a: 'b. We used to report an error here +// though, see https://github.com/rust-lang/rust/issues/45937. +fn foo<'a: 'b, 'b>() + where <() as Project<'a, 'b>>::Item : Eq +{ +} + +// Here we get an error: we need `'a: 'b`. +fn bar<'a, 'b>() //~ ERROR cannot infer + where <() as Project<'a, 'b>>::Item : Eq +{ +} + +fn main() { } diff --git a/src/test/compile-fail/regions-pattern-typing-issue-19997.rs b/src/test/compile-fail/regions-pattern-typing-issue-19997.rs index 91f5f048bc1c0..6fbc65ce6a71f 100644 --- a/src/test/compile-fail/regions-pattern-typing-issue-19997.rs +++ b/src/test/compile-fail/regions-pattern-typing-issue-19997.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir +//[mir]compile-flags: -Z borrowck=mir fn main() { let a0 = 0; @@ -18,8 +18,7 @@ fn main() { match (&a1,) { (&ref b0,) => { a1 = &f; //[ast]~ ERROR cannot assign - //[mir]~^ ERROR cannot assign to `a1` because it is borrowed (Ast) - //[mir]~| ERROR cannot assign to `a1` because it is borrowed (Mir) + //[mir]~^ ERROR cannot assign to `a1` because it is borrowed } } } diff --git a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs index 9743f11c9667b..f6f1a189e5ee1 100644 --- a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs +++ b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs @@ -11,7 +11,7 @@ // Issue #8624. Test for reborrowing with 3 levels, not just two. fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize { - &mut ***p //~ ERROR cannot infer + &mut ***p //~ ERROR 14:5: 14:14: lifetime mismatch [E0623] } fn main() { diff --git a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs index 399ebd6a2a726..7270b477d2d88 100644 --- a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs +++ b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs @@ -13,7 +13,7 @@ // for `'a` (which must be a sublifetime of `'b`). fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize { - &mut **p //~ ERROR cannot infer + &mut **p //~ ERROR 16:5: 16:13: lifetime mismatch [E0623] } fn main() { diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/enums.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/enums.rs new file mode 100644 index 0000000000000..12d1bf9ea9104 --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/enums.rs @@ -0,0 +1,19 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub enum NonExhaustiveEnum { + Unit, + Tuple(u32), + Struct { field: u32 } +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/structs.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/structs.rs new file mode 100644 index 0000000000000..4d083cc5315aa --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/structs.rs @@ -0,0 +1,37 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub struct NormalStruct { + pub first_field: u16, + pub second_field: u16, +} + +#[non_exhaustive] +pub struct UnitStruct; + +#[non_exhaustive] +pub struct TupleStruct(pub u16, pub u16); + +#[derive(Debug)] +#[non_exhaustive] +pub struct FunctionalRecord { + pub first_field: u16, + pub second_field: u16, + pub third_field: bool +} + +impl Default for FunctionalRecord { + fn default() -> FunctionalRecord { + FunctionalRecord { first_field: 640, second_field: 480, third_field: false } + } +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/variants.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/variants.rs new file mode 100644 index 0000000000000..d04c1073ad9b3 --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/auxiliary/variants.rs @@ -0,0 +1,18 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] +#![feature(non_exhaustive)] + +pub enum NonExhaustiveVariants { + #[non_exhaustive] Unit, + #[non_exhaustive] Tuple(u32), + #[non_exhaustive] Struct { field: u32 } +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/enum.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/enum.rs new file mode 100644 index 0000000000000..0c19210e4a0ed --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/enum.rs @@ -0,0 +1,25 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:enums.rs +extern crate enums; + +use enums::NonExhaustiveEnum; + +fn main() { + let enum_unit = NonExhaustiveEnum::Unit; + + match enum_unit { + //~^ ERROR non-exhaustive patterns: `_` not covered [E0004] + NonExhaustiveEnum::Unit => "first", + NonExhaustiveEnum::Tuple(_) => "second", + NonExhaustiveEnum::Struct { .. } => "third" + }; +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/structs.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/structs.rs new file mode 100644 index 0000000000000..74c9c7c61ace8 --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/structs.rs @@ -0,0 +1,47 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:structs.rs +extern crate structs; + +use structs::{NormalStruct, UnitStruct, TupleStruct, FunctionalRecord}; + +fn main() { + let fr = FunctionalRecord { + //~^ ERROR cannot create non-exhaustive struct + first_field: 1920, + second_field: 1080, + ..FunctionalRecord::default() + }; + + let ns = NormalStruct { first_field: 640, second_field: 480 }; + //~^ ERROR cannot create non-exhaustive struct + + let NormalStruct { first_field, second_field } = ns; + //~^ ERROR `..` required with struct marked as non-exhaustive + + let ts = TupleStruct(640, 480); + //~^ ERROR expected function, found struct `TupleStruct` [E0423] + + let ts_explicit = structs::TupleStruct(640, 480); + //~^ ERROR tuple struct `TupleStruct` is private [E0603] + + let TupleStruct { 0: first_field, 1: second_field } = ts; + //~^ ERROR `..` required with struct marked as non-exhaustive + + let us = UnitStruct; + //~^ ERROR expected value, found struct `UnitStruct` [E0423] + + let us_explicit = structs::UnitStruct; + //~^ ERROR unit struct `UnitStruct` is private [E0603] + + let UnitStruct { } = us; + //~^ ERROR `..` required with struct marked as non-exhaustive +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs new file mode 100644 index 0000000000000..d1b65ac1f3e52 --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/variants.rs @@ -0,0 +1,36 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:variants.rs +extern crate variants; + +use variants::NonExhaustiveVariants; + +/* + * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for + * variants. See issue #44109 and PR 45394. + */ +// ignore-test + +fn main() { + let variant_struct = NonExhaustiveVariants::Struct { field: 640 }; + //~^ ERROR cannot create non-exhaustive variant + + let variant_tuple = NonExhaustiveVariants::Tuple { 0: 640 }; + //~^ ERROR cannot create non-exhaustive variant + + match variant_struct { + NonExhaustiveVariants::Unit => "", + NonExhaustiveVariants::Tuple(fe_tpl) => "", + //~^ ERROR `..` required with variant marked as non-exhaustive + NonExhaustiveVariants::Struct { field } => "" + //~^ ERROR `..` required with variant marked as non-exhaustive + }; +} diff --git a/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs b/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs new file mode 100644 index 0000000000000..f4e4b1bb84b8b --- /dev/null +++ b/src/test/compile-fail/rfc-2008-non-exhaustive/variants_create.rs @@ -0,0 +1,27 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +/* + * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for + * variants. See issue #44109 and PR 45394. + */ + +pub enum NonExhaustiveVariants { + #[non_exhaustive] Unit, + //~^ ERROR #[non_exhaustive] is not yet supported on variants + #[non_exhaustive] Tuple(u32), + //~^ ERROR #[non_exhaustive] is not yet supported on variants + #[non_exhaustive] Struct { field: u32 } + //~^ ERROR #[non_exhaustive] is not yet supported on variants +} + +fn main() { } diff --git a/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs b/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs new file mode 100644 index 0000000000000..75c2a5f5bc477 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(crate_in_paths)] + +struct S; + +mod m { + fn f() { + let s = crate::S; //~ ERROR `crate` can only be used in absolute paths + } +} + +fn main() {} diff --git a/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.rs b/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.rs new file mode 100644 index 0000000000000..8c5a971c2f756 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(crate_in_paths)] +#![feature(crate_visibility_modifier)] + +mod m { + pub struct Z; + pub struct S1(crate (::m::Z)); // OK + pub struct S2(::crate ::m::Z); // OK + pub struct S3(crate ::m::Z); //~ ERROR `crate` can only be used in absolute paths +} + +fn main() { + crate struct S; // OK (item in statement position) +} diff --git a/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_in_paths.rs b/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_in_paths.rs new file mode 100644 index 0000000000000..830ec5959b702 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_in_paths.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct S; + +fn main() { + let _ = ::crate::S; //~ ERROR `crate` in paths is experimental +} diff --git a/src/test/compile-fail/E0308-2.rs b/src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-as-identifier.rs similarity index 82% rename from src/test/compile-fail/E0308-2.rs rename to src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-as-identifier.rs index 8c9fc9551561d..2c94f7b0f59df 100644 --- a/src/test/compile-fail/E0308-2.rs +++ b/src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-as-identifier.rs @@ -8,13 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rc::Rc; - -struct Foo; - -impl Foo { - fn x(self: Rc) {} //~ ERROR E0308 -} +#![feature(crate_in_paths)] fn main() { + let crate = 0; //~ ERROR `crate` can only be used in absolute paths } diff --git a/src/test/compile-fail/self-vs-path-ambiguity.rs b/src/test/compile-fail/self-vs-path-ambiguity.rs index 9753014e7810a..b2dba3bd61b36 100644 --- a/src/test/compile-fail/self-vs-path-ambiguity.rs +++ b/src/test/compile-fail/self-vs-path-ambiguity.rs @@ -17,7 +17,6 @@ impl S { fn g(&self::S: &S) {} fn h(&mut self::S: &mut S) {} fn i(&'a self::S: &S) {} //~ ERROR unexpected lifetime `'a` in pattern - //~^ ERROR expected one of `)` or `mut`, found `'a` } fn main() {} diff --git a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs index 35c368f4cbedb..ee08ca05e803f 100644 --- a/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/compile-fail/simd-intrinsic-generic-arithmetic.rs @@ -27,6 +27,7 @@ extern "platform-intrinsic" { fn simd_sub(x: T, y: T) -> T; fn simd_mul(x: T, y: T) -> T; fn simd_div(x: T, y: T) -> T; + fn simd_rem(x: T, y: T) -> T; fn simd_shl(x: T, y: T) -> T; fn simd_shr(x: T, y: T) -> T; fn simd_and(x: T, y: T) -> T; @@ -49,8 +50,12 @@ fn main() { simd_mul(x, x); simd_mul(y, y); simd_mul(z, z); - + simd_div(x, x); + simd_div(y, y); simd_div(z, z); + simd_rem(x, x); + simd_rem(y, y); + simd_rem(z, z); simd_shl(x, x); simd_shl(y, y); @@ -84,10 +89,6 @@ fn main() { //~^ ERROR expected SIMD input type, found non-SIMD `i32` - simd_div(x, x); -//~^ ERROR unsupported operation on `i32x4` with element `i32` - simd_div(y, y); -//~^ ERROR unsupported operation on `u32x4` with element `u32` simd_shl(z, z); //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_shr(z, z); diff --git a/src/test/compile-fail/specialization/defaultimpl/specialization-no-default-trait-implementations.rs b/src/test/compile-fail/specialization/defaultimpl/specialization-no-default-trait-implementations.rs index c1746d765dd9f..cad43ffeacec9 100644 --- a/src/test/compile-fail/specialization/defaultimpl/specialization-no-default-trait-implementations.rs +++ b/src/test/compile-fail/specialization/defaultimpl/specialization-no-default-trait-implementations.rs @@ -13,7 +13,8 @@ trait Foo {} +#[allow(auto_impl)] default impl Foo for .. {} -//~^ ERROR `default impl` is not allowed for default trait implementations +//~^ ERROR `default impl` is not allowed for auto trait implementations fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-polarity.rs b/src/test/compile-fail/specialization/specialization-polarity.rs index 27a3e31491b82..c97cb3f6bb70b 100644 --- a/src/test/compile-fail/specialization/specialization-polarity.rs +++ b/src/test/compile-fail/specialization/specialization-polarity.rs @@ -15,6 +15,7 @@ trait Foo {} +#[allow(auto_impl)] impl Foo for .. {} impl Foo for T {} @@ -22,6 +23,7 @@ impl !Foo for u8 {} //~ ERROR E0119 trait Bar {} +#[allow(auto_impl)] impl Bar for .. {} impl !Bar for T {} diff --git a/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs b/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs index f52b128e7e54a..c6d744fa64d36 100644 --- a/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs +++ b/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs @@ -8,12 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(libc)] - -extern crate libc; - extern { - static mut a: libc::c_int; + static mut a: i32; } fn main() { diff --git a/src/test/compile-fail/super-at-top-level.rs b/src/test/compile-fail/super-at-top-level.rs index 4db673e2006f5..c607711c44f37 100644 --- a/src/test/compile-fail/super-at-top-level.rs +++ b/src/test/compile-fail/super-at-top-level.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::f; //~ ERROR unresolved import `super` [E0432] - //~^ There are too many initial `super`s. +use super::f; //~ ERROR There are too many initial `super`s fn main() { } diff --git a/src/test/compile-fail/svh-change-lit.rs b/src/test/compile-fail/svh-change-lit.rs index 1638caaa92337..f24a3905cc3c8 100644 --- a/src/test/compile-fail/svh-change-lit.rs +++ b/src/test/compile-fail/svh-change-lit.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-change-significant-cfg.rs b/src/test/compile-fail/svh-change-significant-cfg.rs index 99523ca699f0e..7a197fc6ae92e 100644 --- a/src/test/compile-fail/svh-change-significant-cfg.rs +++ b/src/test/compile-fail/svh-change-significant-cfg.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-change-trait-bound.rs b/src/test/compile-fail/svh-change-trait-bound.rs index dcf4859792d28..560feb960f6f0 100644 --- a/src/test/compile-fail/svh-change-trait-bound.rs +++ b/src/test/compile-fail/svh-change-trait-bound.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-change-type-arg.rs b/src/test/compile-fail/svh-change-type-arg.rs index 7e51ca456b21a..b8928c09562b6 100644 --- a/src/test/compile-fail/svh-change-type-arg.rs +++ b/src/test/compile-fail/svh-change-type-arg.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-change-type-ret.rs b/src/test/compile-fail/svh-change-type-ret.rs index 54ca87d84c1ec..14973baafbd61 100644 --- a/src/test/compile-fail/svh-change-type-ret.rs +++ b/src/test/compile-fail/svh-change-type-ret.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-change-type-static.rs b/src/test/compile-fail/svh-change-type-static.rs index ea90faaf61088..cac95b4df8c97 100644 --- a/src/test/compile-fail/svh-change-type-static.rs +++ b/src/test/compile-fail/svh-change-type-static.rs @@ -18,8 +18,7 @@ extern crate a; extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on //~| NOTE: perhaps that crate needs to be recompiled -//~| NOTE: crate `a` path #1: -//~| NOTE: crate `b` path #1: +//~| NOTE: the following crate versions were found: fn main() { b::foo() diff --git a/src/test/compile-fail/svh-use-trait.rs b/src/test/compile-fail/svh-use-trait.rs index c0a5a0a17eb26..c875fa8a0b2b9 100644 --- a/src/test/compile-fail/svh-use-trait.rs +++ b/src/test/compile-fail/svh-use-trait.rs @@ -23,8 +23,7 @@ extern crate uta; extern crate utb; //~ ERROR: found possibly newer version of crate `uta` which `utb` depends //~| NOTE: perhaps that crate needs to be recompiled? -//~| NOTE: crate `uta` path #1: -//~| NOTE: crate `utb` path #1: +//~| NOTE: the following crate versions were found: fn main() { utb::foo() diff --git a/src/test/compile-fail/syntaxt-default-trait-impls.rs b/src/test/compile-fail/syntaxt-default-trait-impls.rs index 7d6a1c9c1544c..45303cbf70025 100644 --- a/src/test/compile-fail/syntaxt-default-trait-impls.rs +++ b/src/test/compile-fail/syntaxt-default-trait-impls.rs @@ -10,9 +10,10 @@ #![feature(optin_builtin_traits)] -trait MyDefaultImpl {} +trait MyAutoImpl {} -impl MyDefaultImpl for .. {} -//~^ ERROR default trait implementations are not allowed to have generics +#[allow(auto_impl)] +impl MyAutoImpl for .. {} +//~^ ERROR auto trait implementations are not allowed to have generics fn main() {} diff --git a/src/test/compile-fail/synthetic-param.rs b/src/test/compile-fail/synthetic-param.rs new file mode 100644 index 0000000000000..a9762e383fe4e --- /dev/null +++ b/src/test/compile-fail/synthetic-param.rs @@ -0,0 +1,38 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_param_attrs, rustc_attrs)] + +fn func<#[rustc_synthetic] T>(_: T) {} + +struct Foo; + +impl Foo { + pub fn func<#[rustc_synthetic] T>(_: T) {} +} + +struct Bar { + t: S +} + +impl Bar { + pub fn func<#[rustc_synthetic] T>(_: T) {} +} + +fn main() { + func::(42); //~ ERROR cannot provide explicit type parameters + func(42); // Ok + + Foo::func::(42); //~ ERROR cannot provide explicit type parameters + Foo::func(42); // Ok + + Bar::::func::(42); //~ ERROR cannot provide explicit type parameters + Bar::::func(42); // Ok +} diff --git a/src/test/compile-fail/trait-bounds-not-on-struct.rs b/src/test/compile-fail/trait-bounds-not-on-struct.rs index cabe0fd48edf9..6cd439167314b 100644 --- a/src/test/compile-fail/trait-bounds-not-on-struct.rs +++ b/src/test/compile-fail/trait-bounds-not-on-struct.rs @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(dyn_trait)] struct Foo; fn foo(_x: Box) { } //~ ERROR expected trait, found struct `Foo` +type A = Box>; //~ ERROR expected trait, found struct `Vec` + fn main() { } diff --git a/src/test/compile-fail/traits-inductive-overflow-supertrait-oibit.rs b/src/test/compile-fail/traits-inductive-overflow-supertrait-oibit.rs index fe0e583b20a38..6c7928f13f894 100644 --- a/src/test/compile-fail/traits-inductive-overflow-supertrait-oibit.rs +++ b/src/test/compile-fail/traits-inductive-overflow-supertrait-oibit.rs @@ -15,6 +15,7 @@ #![feature(optin_builtin_traits)] trait Magic: Copy {} //~ ERROR E0568 +#[allow(auto_impl)] impl Magic for .. {} fn copy(x: T) -> (T, T) { (x, x) } diff --git a/src/test/compile-fail/type-path-err-node-types.rs b/src/test/compile-fail/type-path-err-node-types.rs index 8f26777b441d4..7ef099d0410a6 100644 --- a/src/test/compile-fail/type-path-err-node-types.rs +++ b/src/test/compile-fail/type-path-err-node-types.rs @@ -8,10 +8,29 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Type arguments of unresolved types should have their types recorded +// Type arguments in unresolved entities (reporting errors before type checking) +// should have their types recorded. -fn main() { +trait Tr {} + +fn local_type() { let _: Nonexistent; //~ ERROR cannot find type `Nonexistent` in this scope +} - let _ = |a, b: _| -> _ { 0 }; +fn ufcs_trait() { + >::nonexistent(); //~ ERROR cannot find method or associated constant `nonexistent` } + +fn ufcs_item() { + NonExistent::Assoc::; //~ ERROR undeclared type or module `NonExistent` +} + +fn method() { + nonexistent.nonexistent::(); //~ ERROR cannot find value `nonexistent` +} + +fn closure() { + let _ = |a, b: _| -> _ { 0 }; // OK +} + +fn main() {} diff --git a/src/test/compile-fail/typeck-auto-trait-no-supertraits-2.rs b/src/test/compile-fail/typeck-auto-trait-no-supertraits-2.rs index f6678ac7c2d8c..173582ed22fdc 100644 --- a/src/test/compile-fail/typeck-auto-trait-no-supertraits-2.rs +++ b/src/test/compile-fail/typeck-auto-trait-no-supertraits-2.rs @@ -11,6 +11,7 @@ #![feature(optin_builtin_traits)] trait Magic : Sized where Option : Magic {} //~ ERROR E0568 +#[allow(auto_impl)] impl Magic for .. {} impl Magic for T {} diff --git a/src/test/compile-fail/typeck-auto-trait-no-supertraits.rs b/src/test/compile-fail/typeck-auto-trait-no-supertraits.rs index 9497dfb39d7d0..6802f72504b7d 100644 --- a/src/test/compile-fail/typeck-auto-trait-no-supertraits.rs +++ b/src/test/compile-fail/typeck-auto-trait-no-supertraits.rs @@ -35,6 +35,7 @@ #![feature(optin_builtin_traits)] trait Magic: Copy {} //~ ERROR E0568 +#[allow(auto_impl)] impl Magic for .. {} impl Magic for T {} diff --git a/src/test/compile-fail/typeck-auto-trait-no-typeparams.rs b/src/test/compile-fail/typeck-auto-trait-no-typeparams.rs index 5a852c54869a5..3c409d1b371eb 100644 --- a/src/test/compile-fail/typeck-auto-trait-no-typeparams.rs +++ b/src/test/compile-fail/typeck-auto-trait-no-typeparams.rs @@ -11,4 +11,5 @@ #![feature(optin_builtin_traits)] trait Magic {} //~ ERROR E0567 +#[allow(auto_impl)] impl Magic for .. {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs b/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs index 8a46d6c76c30f..a837d8c9ca74e 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-constituent-types-2.rs @@ -12,6 +12,7 @@ trait MyTrait {} +#[allow(auto_impl)] impl MyTrait for .. {} struct MyS; diff --git a/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs b/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs index 3d7746b369cc0..bed184eb4ccca 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-constituent-types.rs @@ -12,6 +12,7 @@ trait MyTrait {} +#[allow(auto_impl)] impl MyTrait for .. {} impl !MyTrait for *mut T {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-negation.rs b/src/test/compile-fail/typeck-default-trait-impl-negation.rs index 8c2658b89a506..f3a6d8a342e22 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-negation.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-negation.rs @@ -12,10 +12,12 @@ trait MyTrait {} +#[allow(auto_impl)] impl MyTrait for .. {} unsafe trait MyUnsafeTrait {} +#[allow(auto_impl)] unsafe impl MyUnsafeTrait for .. {} struct ThisImplsTrait; diff --git a/src/test/compile-fail/typeck-default-trait-impl-outside-crate.rs b/src/test/compile-fail/typeck-default-trait-impl-outside-crate.rs index 4d71517e06058..da3e926d6fc1f 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-outside-crate.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-outside-crate.rs @@ -10,6 +10,7 @@ #![feature(optin_builtin_traits)] +#[allow(auto_impl)] impl Copy for .. {} //~ ERROR E0318 //~^ NOTE `Copy` trait not defined in this crate fn main() {} diff --git a/src/test/compile-fail/typeck-default-trait-impl-precedence.rs b/src/test/compile-fail/typeck-default-trait-impl-precedence.rs index 66c7a1c75ffe4..bdd6487b86d74 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-precedence.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-precedence.rs @@ -16,6 +16,7 @@ #![feature(optin_builtin_traits)] trait Defaulted { } +#[allow(auto_impl)] impl Defaulted for .. { } impl<'a,T:Signed> Defaulted for &'a T { } impl<'a,T:Signed> Defaulted for &'a mut T { } diff --git a/src/test/compile-fail/ufcs-explicit-self-bad.rs b/src/test/compile-fail/ufcs-explicit-self-bad.rs index a98b7cd43090f..a0d1f2dc3312e 100644 --- a/src/test/compile-fail/ufcs-explicit-self-bad.rs +++ b/src/test/compile-fail/ufcs-explicit-self-bad.rs @@ -15,7 +15,8 @@ struct Foo { } impl Foo { - fn foo(self: isize, x: isize) -> isize { //~ ERROR mismatched method receiver + fn foo(self: isize, x: isize) -> isize { + //~^ ERROR invalid `self` type self.f + x } } @@ -25,10 +26,12 @@ struct Bar { } impl Bar { - fn foo(self: Bar, x: isize) -> isize { //~ ERROR mismatched method receiver + fn foo(self: Bar, x: isize) -> isize { + //~^ ERROR invalid `self` type x } - fn bar(self: &Bar, x: isize) -> isize { //~ ERROR mismatched method receiver + fn bar(self: &Bar, x: isize) -> isize { + //~^ ERROR invalid `self` type x } } @@ -45,12 +48,12 @@ impl<'a, T> SomeTrait for &'a Bar { //~^ ERROR mismatched method receiver fn dummy3(self: &&Bar) {} //~^ ERROR mismatched method receiver - //~| expected type `&&'a Bar` - //~| found type `&&Bar` + //~| expected type `&'a Bar` + //~| found type `&Bar` //~| lifetime mismatch //~| ERROR mismatched method receiver - //~| expected type `&&'a Bar` - //~| found type `&&Bar` + //~| expected type `&'a Bar` + //~| found type `&Bar` //~| lifetime mismatch } diff --git a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs index 432c7fa5d1b20..0dbd61413e053 100644 --- a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs +++ b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + // Test that a by-ref `FnMut` closure gets an error when it tries to // mutate a value. @@ -19,6 +22,7 @@ fn main() { let mut counter = 0; call(|| { counter += 1; - //~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure + //[ast]~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure + //[mir]~^^ ERROR cannot assign to immutable item `counter` }); } diff --git a/src/test/compile-fail/union/union-const-eval.rs b/src/test/compile-fail/union/union-const-eval.rs index ee4d9fe99eeb8..73b7743fc45c7 100644 --- a/src/test/compile-fail/union/union-const-eval.rs +++ b/src/test/compile-fail/union/union-const-eval.rs @@ -20,5 +20,7 @@ fn main() { let a: [u8; C.a]; // OK let b: [u8; C.b]; //~ ERROR constant evaluation error //~^ NOTE nonexistent struct field + //~| WARNING constant evaluation error + //~| NOTE on by default } } diff --git a/src/test/compile-fail/unsupported-cast.rs b/src/test/compile-fail/unsupported-cast.rs index 8b63dd51729b8..5137663a269f4 100644 --- a/src/test/compile-fail/unsupported-cast.rs +++ b/src/test/compile-fail/unsupported-cast.rs @@ -10,10 +10,8 @@ // error-pattern:casting -#![feature(libc)] - -extern crate libc; +struct A; fn main() { - println!("{:?}", 1.0 as *const libc::FILE); // Can't cast float to foreign. + println!("{:?}", 1.0 as *const A); // Can't cast float to foreign. } diff --git a/src/test/compile-fail/use-super-global-path.rs b/src/test/compile-fail/use-super-global-path.rs index 4162e037cf32e..fc1a72f6f2b90 100644 --- a/src/test/compile-fail/use-super-global-path.rs +++ b/src/test/compile-fail/use-super-global-path.rs @@ -18,7 +18,7 @@ mod foo { pub fn g() { use ::super::main; //~ ERROR global paths cannot start with `super` - main(); + main(); //~ ERROR cannot find function `main` in this scope } } diff --git a/src/test/compile-fail/variance-trait-matching.rs b/src/test/compile-fail/variance-trait-matching.rs index 2d78940ce4b9d..bc9eab2be821b 100644 --- a/src/test/compile-fail/variance-trait-matching.rs +++ b/src/test/compile-fail/variance-trait-matching.rs @@ -31,7 +31,7 @@ fn get<'a, G>(get: &G) -> i32 // This fails to type-check because, without variance, we can't // use `G : Get<&'a i32>` as evidence that `G : Get<&'b i32>`, // even if `'a : 'b`. - pick(get, &22) //~ ERROR cannot infer + pick(get, &22) //~ ERROR 34:5: 34:9: explicit lifetime required in the type of `get` [E0621] } fn pick<'b, G>(get: &'b G, if_odd: &'b i32) -> i32 diff --git a/src/test/compile-fail/weak-lang-item.rs b/src/test/compile-fail/weak-lang-item.rs index 8eac959fc1e90..8579611b9380a 100644 --- a/src/test/compile-fail/weak-lang-item.rs +++ b/src/test/compile-fail/weak-lang-item.rs @@ -11,6 +11,7 @@ // aux-build:weak-lang-items.rs // error-pattern: language item required, but not found: `panic_fmt` // error-pattern: language item required, but not found: `eh_personality` +// ignore-wasm32-bare compiled with panic=abort, personality not required #![no_std] diff --git a/src/test/debuginfo/constant-debug-locs.rs b/src/test/debuginfo/constant-debug-locs.rs index 7a24510b7d409..8fc910d8a6daa 100644 --- a/src/test/debuginfo/constant-debug-locs.rs +++ b/src/test/debuginfo/constant-debug-locs.rs @@ -15,7 +15,6 @@ #![allow(dead_code, unused_variables)] #![feature(omit_gdb_pretty_printer_section)] #![omit_gdb_pretty_printer_section] -#![feature(const_unsafe_cell_new)] #![feature(static_mutex)] // This test makes sure that the compiler doesn't crash when trying to assign diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs index 9596f0287bc59..2a28c895b797e 100644 --- a/src/test/debuginfo/pretty-std.rs +++ b/src/test/debuginfo/pretty-std.rs @@ -44,6 +44,10 @@ // gdb-command: print some_string // gdb-check:$8 = Some = {"IAMA optional string!"} +// gdb-command: set print length 5 +// gdb-command: print some_string +// gdb-check:$8 = Some = {"IAMA "...} + // === LLDB TESTS ================================================================================== diff --git a/src/test/incremental/add_private_fn_at_krate_root_cc/struct_point.rs b/src/test/incremental/add_private_fn_at_krate_root_cc/struct_point.rs index 875aa32936508..067ce51d0f785 100644 --- a/src/test/incremental/add_private_fn_at_krate_root_cc/struct_point.rs +++ b/src/test/incremental/add_private_fn_at_krate_root_cc/struct_point.rs @@ -12,28 +12,29 @@ // crate. This should not cause anything we use to be invalidated. // Regression test for #36168. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph // aux-build:point.rs -// ignore-test FIXME(#42293) this regressed in #44142 but should get fixed with red/green +// must-compile-successfully #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] +#![crate_type = "rlib"] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_free_fn", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_free_fn", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] extern crate point; /// A fn item that calls (public) methods on `Point` from the same impl -mod fn_calls_methods_in_same_impl { +pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin(); @@ -41,10 +42,10 @@ mod fn_calls_methods_in_same_impl { } /// A fn item that calls (public) methods on `Point` from another impl -mod fn_calls_free_fn { +pub mod fn_calls_free_fn { use point::{self, Point}; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; point::distance_squared(&x); @@ -52,34 +53,31 @@ mod fn_calls_free_fn { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/cache_file_headers.rs b/src/test/incremental/cache_file_headers.rs index 274a3920be8d4..feecfecd0b853 100644 --- a/src/test/incremental/cache_file_headers.rs +++ b/src/test/incremental/cache_file_headers.rs @@ -20,6 +20,7 @@ //[rpass1] rustc-env:RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER="l33t haxx0r rustc 2.1 LTS" // revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![rustc_partition_translated(module="cache_file_headers", cfg="rpass2")] diff --git a/src/test/incremental/callee_caller_cross_crate/b.rs b/src/test/incremental/callee_caller_cross_crate/b.rs index 355983e9ca1b9..9e56d34636ff0 100644 --- a/src/test/incremental/callee_caller_cross_crate/b.rs +++ b/src/test/incremental/callee_caller_cross_crate/b.rs @@ -12,8 +12,6 @@ // revisions:rpass1 rpass2 // compile-flags:-Z query-dep-graph -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - #![feature(rustc_attrs)] extern crate a; diff --git a/src/test/incremental/change_add_field/struct_point.rs b/src/test/incremental/change_add_field/struct_point.rs index ac5c0d3b9e72d..b1c566e1739ef 100644 --- a/src/test/incremental/change_add_field/struct_point.rs +++ b/src/test/incremental/change_add_field/struct_point.rs @@ -13,32 +13,34 @@ // Fns with that type used only in their body are also recompiled, but // their callers are not. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] +#![crate_type = "rlib"] // These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_sig", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-call_fn_with_type_in_sig", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_body", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_make_struct", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_write_field", cfg="rpass2")] - -#![rustc_partition_reused(module="struct_point-call_fn_with_type_in_body", cfg="rpass2")] - -mod point { - #[cfg(rpass1)] +#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-call_fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_with_type_in_body", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_write_field", cfg="cfail2")] + +#![rustc_partition_reused(module="struct_point-call_fn_with_type_in_body", cfg="cfail2")] + +pub mod point { + #[cfg(cfail1)] pub struct Point { pub x: f32, pub y: f32, } - #[cfg(rpass2)] + #[cfg(cfail2)] pub struct Point { pub x: f32, pub y: f32, @@ -47,18 +49,18 @@ mod point { impl Point { pub fn origin() -> Point { - #[cfg(rpass1)] + #[cfg(cfail1)] return Point { x: 0.0, y: 0.0 }; - #[cfg(rpass2)] + #[cfg(cfail2)] return Point { x: 0.0, y: 0.0, z: 0.0 }; } pub fn total(&self) -> f32 { - #[cfg(rpass1)] + #[cfg(cfail1)] return self.x + self.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return self.x + self.y + self.z; } @@ -75,10 +77,10 @@ mod point { /// sufficiently "private", we might not need to type-check again. /// Rebuilding is probably always necessary since the layout may be /// affected. -mod fn_with_type_in_sig { +pub mod fn_with_type_in_sig { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn boop(p: Option<&Point>) -> f32 { p.map(|p| p.total()).unwrap_or(0.0) } @@ -91,10 +93,10 @@ mod fn_with_type_in_sig { /// sufficiently "private", we might not need to type-check again. /// Rebuilding is probably always necessary since the layout may be /// affected. -mod call_fn_with_type_in_sig { +pub mod call_fn_with_type_in_sig { use fn_with_type_in_sig; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn bip() -> f32 { fn_with_type_in_sig::boop(None) } @@ -107,10 +109,10 @@ mod call_fn_with_type_in_sig { /// sufficiently "private", we might not need to type-check again. /// Rebuilding is probably always necessary since the layout may be /// affected. -mod fn_with_type_in_body { +pub mod fn_with_type_in_body { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn boop() -> f32 { Point::origin().total() } @@ -120,44 +122,41 @@ mod fn_with_type_in_body { /// body. In this case, the effects of the change should be contained /// to Y; X should not have to be rebuilt, nor should it need to be /// typechecked again. -mod call_fn_with_type_in_body { +pub mod call_fn_with_type_in_body { use fn_with_type_in_body; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn bip() -> f32 { fn_with_type_in_body::boop() } } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn make_origin(p: Point) -> Point { Point { ..p } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_private_fn/struct_point.rs b/src/test/incremental/change_private_fn/struct_point.rs index abfd55ba52cc7..d8251a4fbcf6c 100644 --- a/src/test/incremental/change_private_fn/struct_point.rs +++ b/src/test/incremental/change_private_fn/struct_point.rs @@ -11,32 +11,34 @@ // Test where we change the body of a private method in an impl. // We then test what sort of functions must be rebuilt as a result. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] +#![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")] +#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] -mod point { +pub mod point { pub struct Point { pub x: f32, pub y: f32, } fn distance_squared(this: &Point) -> f32 { - #[cfg(rpass1)] + #[cfg(cfail1)] return this.x + this.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return this.x * this.x + this.y * this.y; } @@ -56,10 +58,10 @@ mod point { } /// A fn item that calls (public) methods on `Point` from the same impl which changed -mod fn_calls_methods_in_same_impl { +pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin(); @@ -67,10 +69,10 @@ mod fn_calls_methods_in_same_impl { } /// A fn item that calls (public) methods on `Point` from another impl -mod fn_calls_methods_in_another_impl { +pub mod fn_calls_methods_in_another_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let mut x = Point { x: 2.0, y: 2.0 }; x.translate(3.0, 3.0); @@ -78,34 +80,31 @@ mod fn_calls_methods_in_another_impl { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_private_fn_cc/auxiliary/point.rs b/src/test/incremental/change_private_fn_cc/auxiliary/point.rs index dcc1ced635fbf..af20336806fd4 100644 --- a/src/test/incremental/change_private_fn_cc/auxiliary/point.rs +++ b/src/test/incremental/change_private_fn_cc/auxiliary/point.rs @@ -14,10 +14,10 @@ pub struct Point { } fn distance_squared(this: &Point) -> f32 { - #[cfg(rpass1)] + #[cfg(cfail1)] return this.x + this.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return this.x * this.x + this.y * this.y; } diff --git a/src/test/incremental/change_private_fn_cc/struct_point.rs b/src/test/incremental/change_private_fn_cc/struct_point.rs index d58a9bacdb53c..b3816b90194e4 100644 --- a/src/test/incremental/change_private_fn_cc/struct_point.rs +++ b/src/test/incremental/change_private_fn_cc/struct_point.rs @@ -11,29 +11,29 @@ // Test where we change the body of a private method in an impl. // We then test what sort of functions must be rebuilt as a result. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph // aux-build:point.rs +// must-compile-successfully -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - +#![crate_type = "rlib"] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] extern crate point; /// A fn item that calls (public) methods on `Point` from the same impl which changed -mod fn_calls_methods_in_same_impl { +pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin(); @@ -41,10 +41,10 @@ mod fn_calls_methods_in_same_impl { } /// A fn item that calls (public) methods on `Point` from another impl -mod fn_calls_methods_in_another_impl { +pub mod fn_calls_methods_in_another_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let mut x = Point { x: 2.0, y: 2.0 }; x.translate(3.0, 3.0); @@ -52,34 +52,31 @@ mod fn_calls_methods_in_another_impl { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_private_impl_method/struct_point.rs b/src/test/incremental/change_private_impl_method/struct_point.rs index d8c6cafe596a6..c18f95a631205 100644 --- a/src/test/incremental/change_private_impl_method/struct_point.rs +++ b/src/test/incremental/change_private_impl_method/struct_point.rs @@ -11,33 +11,35 @@ // Test where we change the body of a private method in an impl. // We then test what sort of functions must be rebuilt as a result. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] +#![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")] +#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] -mod point { +pub mod point { pub struct Point { pub x: f32, pub y: f32, } impl Point { - fn distance_squared(&self) -> f32 { - #[cfg(rpass1)] + pub fn distance_squared(&self) -> f32 { + #[cfg(cfail1)] return self.x + self.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return self.x * self.x + self.y * self.y; } @@ -56,10 +58,10 @@ mod point { } /// A fn item that calls (public) methods on `Point` from the same impl which changed -mod fn_calls_methods_in_same_impl { +pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin(); @@ -67,10 +69,10 @@ mod fn_calls_methods_in_same_impl { } /// A fn item that calls (public) methods on `Point` from another impl -mod fn_calls_methods_in_another_impl { +pub mod fn_calls_methods_in_another_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let mut x = Point { x: 2.0, y: 2.0 }; x.translate(3.0, 3.0); @@ -78,34 +80,31 @@ mod fn_calls_methods_in_another_impl { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_private_impl_method_cc/auxiliary/point.rs b/src/test/incremental/change_private_impl_method_cc/auxiliary/point.rs index 8df1cf54da2b9..f5e3a06051cc9 100644 --- a/src/test/incremental/change_private_impl_method_cc/auxiliary/point.rs +++ b/src/test/incremental/change_private_impl_method_cc/auxiliary/point.rs @@ -15,10 +15,10 @@ pub struct Point { impl Point { fn distance_squared(&self) -> f32 { - #[cfg(rpass1)] + #[cfg(cfail1)] return self.x + self.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return self.x * self.x + self.y * self.y; } diff --git a/src/test/incremental/change_private_impl_method_cc/struct_point.rs b/src/test/incremental/change_private_impl_method_cc/struct_point.rs index 3f665f5c82052..55e1dffe9da41 100644 --- a/src/test/incremental/change_private_impl_method_cc/struct_point.rs +++ b/src/test/incremental/change_private_impl_method_cc/struct_point.rs @@ -11,30 +11,30 @@ // Test where we change the body of a private method in an impl. // We then test what sort of functions must be rebuilt as a result. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph // aux-build:point.rs +// must-compile-successfully -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - +#![crate_type = "rlib"] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] extern crate point; /// A fn item that calls (public) methods on `Point` from the same impl which changed -mod fn_calls_methods_in_same_impl { +pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin(); @@ -42,10 +42,10 @@ mod fn_calls_methods_in_same_impl { } /// A fn item that calls (public) methods on `Point` from another impl -mod fn_calls_methods_in_another_impl { +pub mod fn_calls_methods_in_another_impl { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn dirty() { let mut x = Point { x: 2.0, y: 2.0 }; x.translate(3.0, 3.0); @@ -53,34 +53,31 @@ mod fn_calls_methods_in_another_impl { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs index 5b29ee1435f9a..2cb7ef13f8e52 100644 --- a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs @@ -10,22 +10,24 @@ // Test where we change the body of a public, inherent method. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully +#![crate_type = "rlib"] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] -#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")] +#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_reused(module="struct_point-fn_calls_changed_method", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_changed_method", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] -mod point { +pub mod point { pub struct Point { pub x: f32, pub y: f32, @@ -33,10 +35,10 @@ mod point { impl Point { pub fn distance_from_origin(&self) -> f32 { - #[cfg(rpass1)] + #[cfg(cfail1)] return self.x * self.x + self.y * self.y; - #[cfg(rpass2)] + #[cfg(cfail2)] return (self.x * self.x + self.y * self.y).sqrt(); } @@ -47,10 +49,10 @@ mod point { } /// A fn item that calls the method on `Point` which changed -mod fn_calls_changed_method { +pub mod fn_calls_changed_method { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let p = Point { x: 2.0, y: 2.0 }; p.distance_from_origin(); @@ -58,10 +60,10 @@ mod fn_calls_changed_method { } /// A fn item that calls a method on `Point` which did not change -mod fn_calls_another_method { +pub mod fn_calls_another_method { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let p = Point { x: 2.0, y: 2.0 }; p.x(); @@ -69,34 +71,31 @@ mod fn_calls_another_method { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs index 4d12b7b390cc9..f2485a876cc60 100644 --- a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs @@ -10,30 +10,32 @@ // Test where we change the *signature* of a public, inherent method. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully +#![crate_type = "rlib"] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] // These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")] -#![rustc_partition_translated(module="struct_point-fn_calls_changed_method", cfg="rpass2")] +#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_translated(module="struct_point-fn_calls_changed_method", cfg="cfail2")] -#![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")] -#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")] +#![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="cfail2")] -mod point { +pub mod point { pub struct Point { pub x: f32, pub y: f32, } impl Point { - #[cfg(rpass1)] + #[cfg(cfail1)] pub fn distance_from_point(&self, p: Option) -> f32 { let p = p.unwrap_or(Point { x: 0.0, y: 0.0 }); let x_diff = self.x - p.x; @@ -41,7 +43,7 @@ mod point { return x_diff * x_diff + y_diff * y_diff; } - #[cfg(rpass2)] + #[cfg(cfail2)] pub fn distance_from_point(&self, p: Option<&Point>) -> f32 { const ORIGIN: &Point = &Point { x: 0.0, y: 0.0 }; let p = p.unwrap_or(ORIGIN); @@ -57,10 +59,10 @@ mod point { } /// A fn item that calls the method that was changed -mod fn_calls_changed_method { +pub mod fn_calls_changed_method { use point::Point; - #[rustc_dirty(label="TypeckTables", cfg="rpass2")] + #[rustc_dirty(label="TypeckTables", cfg="cfail2")] pub fn check() { let p = Point { x: 2.0, y: 2.0 }; p.distance_from_point(None); @@ -68,10 +70,10 @@ mod fn_calls_changed_method { } /// A fn item that calls a method that was not changed -mod fn_calls_another_method { +pub mod fn_calls_another_method { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn check() { let p = Point { x: 2.0, y: 2.0 }; p.x(); @@ -79,34 +81,31 @@ mod fn_calls_another_method { } /// A fn item that makes an instance of `Point` but does not invoke methods -mod fn_make_struct { +pub mod fn_make_struct { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn make_origin() -> Point { Point { x: 2.0, y: 2.0 } } } /// A fn item that reads fields from `Point` but does not invoke methods -mod fn_read_field { +pub mod fn_read_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn get_x(p: Point) -> f32 { p.x } } /// A fn item that writes to a field of `Point` but does not invoke methods -mod fn_write_field { +pub mod fn_write_field { use point::Point; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] pub fn inc_x(p: &mut Point) { p.x += 1.0; } } - -fn main() { -} diff --git a/src/test/incremental/change_symbol_export_status.rs b/src/test/incremental/change_symbol_export_status.rs index 71f46c641bf8f..ab91a941a1663 100644 --- a/src/test/incremental/change_symbol_export_status.rs +++ b/src/test/incremental/change_symbol_export_status.rs @@ -9,13 +9,13 @@ // except according to those terms. // revisions: rpass1 rpass2 +// compile-flags: -Zquery-dep-graph #![feature(rustc_attrs)] #![allow(private_no_mangle_fns)] -#![rustc_partition_reused(module="change_symbol_export_status", cfg="rpass2")] #![rustc_partition_translated(module="change_symbol_export_status-mod1", cfg="rpass2")] - +#![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")] // This test case makes sure that a change in symbol visibility is detected by // our dependency tracking. We do this by changing a module's visibility to @@ -37,6 +37,11 @@ mod mod1 { pub fn foo() {} } +pub mod mod2 { + #[no_mangle] + pub fn bar() {} +} + fn main() { mod1::foo(); } diff --git a/src/test/incremental/commandline-args.rs b/src/test/incremental/commandline-args.rs index 95187b825be9f..e29f2ec2a1345 100644 --- a/src/test/incremental/commandline-args.rs +++ b/src/test/incremental/commandline-args.rs @@ -12,6 +12,7 @@ // the cache while changing an untracked one doesn't. // revisions:rpass1 rpass2 rpass3 +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] diff --git a/src/test/incremental/hashes/call_expressions.rs b/src/test/incremental/hashes/call_expressions.rs index 647ff5dedf3dd..a62d84fedf3aa 100644 --- a/src/test/incremental/hashes/call_expressions.rs +++ b/src/test/incremental/hashes/call_expressions.rs @@ -36,12 +36,8 @@ pub fn change_callee_function() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_callee_function() { callee2(1, 2) } @@ -55,12 +51,8 @@ pub fn change_argument_function() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_argument_function() { callee1(1, 3) } @@ -78,8 +70,8 @@ mod change_callee_indirectly_function { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + + pub fn change_callee_indirectly_function() { callee(1, 2) } @@ -100,12 +92,8 @@ pub fn change_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_callee_method() { let s = Struct; s.method2('x', true); @@ -121,12 +109,8 @@ pub fn change_argument_method() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_argument_method() { let s = Struct; s.method1('y', true); @@ -142,12 +126,8 @@ pub fn change_ufcs_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_ufcs_callee_method() { let s = Struct; Struct::method2(&s, 'x', true); @@ -163,12 +143,8 @@ pub fn change_argument_method_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_argument_method_ufcs() { let s = Struct; Struct::method1(&s, 'x', false); @@ -184,12 +160,10 @@ pub fn change_to_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +// One might think this would be expanded in the HirBody/Mir, but it actually +// results in slightly different Hir/Mir. pub fn change_to_ufcs() { let s = Struct; Struct::method1(&s, 'x', true); @@ -202,18 +176,16 @@ impl Struct2 { } // Change UFCS Callee Indirectly ----------------------------------------------- -mod change_ufcs_callee_indirectly { +pub mod change_ufcs_callee_indirectly { #[cfg(cfail1)] use super::Struct as Struct; #[cfg(not(cfail1))] use super::Struct2 as Struct; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] + #[rustc_clean(cfg="cfail3")] + + pub fn change_ufcs_callee_indirectly() { let s = Struct; Struct::method1(&s, 'q', false) diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs index 38fe5cdffebd0..4abc77e0ab64d 100644 --- a/src/test/incremental/hashes/closure_expressions.rs +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -36,8 +36,6 @@ fn change_closure_body() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_closure_body() { let _ = || 3u32; } @@ -56,8 +54,6 @@ fn add_parameter() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_parameter() { let x = 0u32; let _ = |x: u32| x + 1; @@ -76,8 +72,6 @@ fn change_parameter_pattern() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_parameter_pattern() { let _ = |&x: &u32| x; } @@ -95,8 +89,6 @@ fn add_move() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_move() { let _ = move || 1; } @@ -115,8 +107,6 @@ fn add_type_ascription_to_parameter() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_type_ascription_to_parameter() { let closure = |x: u32| x + 1u32; let _: u32 = closure(1); @@ -136,8 +126,6 @@ fn change_parameter_type() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_parameter_type() { let closure = |x: u16| (x as u64) + 1; let _ = closure(1); diff --git a/src/test/incremental/hashes/consts.rs b/src/test/incremental/hashes/consts.rs index 28e85c94b664c..496ae4276f863 100644 --- a/src/test/incremental/hashes/consts.rs +++ b/src/test/incremental/hashes/consts.rs @@ -30,10 +30,8 @@ const CONST_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] pub const CONST_VISIBILITY: u8 = 0; @@ -42,10 +40,8 @@ pub const CONST_VISIBILITY: u8 = 0; const CONST_CHANGE_TYPE_1: i32 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_1: u32 = 0; @@ -54,65 +50,53 @@ const CONST_CHANGE_TYPE_1: u32 = 0; const CONST_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_2: Option = None; // Change value between simple literals --------------------------------------- -#[cfg(cfail1)] -const CONST_CHANGE_VALUE_1: i16 = 1; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +const CONST_CHANGE_VALUE_1: i16 = { + #[cfg(cfail1)] + { 1 } -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -const CONST_CHANGE_VALUE_1: i16 = 2; + #[cfg(not(cfail1))] + { 2 } +}; // Change value between expressions ------------------------------------------- -#[cfg(cfail1)] -const CONST_CHANGE_VALUE_2: i16 = 1 + 1; - -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -const CONST_CHANGE_VALUE_2: i16 = 1 + 2; - +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +const CONST_CHANGE_VALUE_2: i16 = { + #[cfg(cfail1)] + { 1 + 1 } -#[cfg(cfail1)] -const CONST_CHANGE_VALUE_3: i16 = 2 + 3; + #[cfg(not(cfail1))] + { 1 + 2 } +}; -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -const CONST_CHANGE_VALUE_3: i16 = 2 * 3; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +const CONST_CHANGE_VALUE_3: i16 = { + #[cfg(cfail1)] + { 2 + 3 } + #[cfg(not(cfail1))] + { 2 * 3 } +}; -#[cfg(cfail1)] -const CONST_CHANGE_VALUE_4: i16 = 1 + 2 * 3; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +const CONST_CHANGE_VALUE_4: i16 = { + #[cfg(cfail1)] + { 1 + 2 * 3 } -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -const CONST_CHANGE_VALUE_4: i16 = 1 + 2 * 4; + #[cfg(not(cfail1))] + { 1 + 2 * 4 } +}; // Change type indirectly ----------------------------------------------------- @@ -126,15 +110,11 @@ mod const_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] + #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] + #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs index 7f991b30fc492..f38d18646306e 100644 --- a/src/test/incremental/hashes/enum_constructors.rs +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -25,7 +25,7 @@ #![crate_type="rlib"] -enum Enum { +pub enum Enum { Struct { x: i32, y: i64, @@ -36,7 +36,7 @@ enum Enum { // Change field value (struct-like) ----------------------------------------- #[cfg(cfail1)] -fn change_field_value_struct_like() -> Enum { +pub fn change_field_value_struct_like() -> Enum { Enum::Struct { x: 0, y: 1, @@ -45,13 +45,9 @@ fn change_field_value_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_value_struct_like() -> Enum { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_value_struct_like() -> Enum { Enum::Struct { x: 0, y: 2, @@ -63,7 +59,7 @@ fn change_field_value_struct_like() -> Enum { // Change field order (struct-like) ----------------------------------------- #[cfg(cfail1)] -fn change_field_order_struct_like() -> Enum { +pub fn change_field_order_struct_like() -> Enum { Enum::Struct { x: 3, y: 4, @@ -72,13 +68,11 @@ fn change_field_order_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_order_struct_like() -> Enum { +#[rustc_clean(cfg="cfail2", except="HirBody,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +// FIXME(michaelwoerister):Interesting. I would have thought that that changes the MIR. And it +// would if it were not all constants +pub fn change_field_order_struct_like() -> Enum { Enum::Struct { y: 4, x: 3, @@ -87,7 +81,7 @@ fn change_field_order_struct_like() -> Enum { } -enum Enum2 { +pub enum Enum2 { Struct { x: i8, y: i8, @@ -104,7 +98,7 @@ enum Enum2 { // Change constructor path (struct-like) ------------------------------------ #[cfg(cfail1)] -fn change_constructor_path_struct_like() { +pub fn change_constructor_path_struct_like() { let _ = Enum::Struct { x: 0, y: 1, @@ -113,13 +107,9 @@ fn change_constructor_path_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_path_struct_like() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_path_struct_like() { let _ = Enum2::Struct { x: 0, y: 1, @@ -131,7 +121,7 @@ fn change_constructor_path_struct_like() { // Change variant (regular struct) ------------------------------------ #[cfg(cfail1)] -fn change_constructor_variant_struct_like() { +pub fn change_constructor_variant_struct_like() { let _ = Enum2::Struct { x: 0, y: 1, @@ -140,13 +130,9 @@ fn change_constructor_variant_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_variant_struct_like() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_variant_struct_like() { let _ = Enum2::Struct2 { x: 0, y: 1, @@ -156,19 +142,19 @@ fn change_constructor_variant_struct_like() { // Change constructor path indirectly (struct-like) ------------------------- -mod change_constructor_path_indirectly_struct_like { +pub mod change_constructor_path_indirectly_struct_like { #[cfg(cfail1)] use super::Enum as TheEnum; #[cfg(not(cfail1))] use super::Enum2 as TheEnum; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> TheEnum { + #[rustc_clean( + cfg="cfail2", + except="FnSignature,Hir,HirBody,MirOptimized,MirValidated,\ + TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> TheEnum { TheEnum::Struct { x: 0, y: 1, @@ -179,20 +165,16 @@ mod change_constructor_path_indirectly_struct_like { // Change constructor variant indirectly (struct-like) --------------------------- -mod change_constructor_variant_indirectly_struct_like { +pub mod change_constructor_variant_indirectly_struct_like { use super::Enum2; #[cfg(cfail1)] use super::Enum2::Struct as Variant; #[cfg(not(cfail1))] use super::Enum2::Struct2 as Variant; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> Enum2 { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> Enum2 { Variant { x: 0, y: 1, @@ -204,18 +186,14 @@ mod change_constructor_variant_indirectly_struct_like { // Change field value (tuple-like) ------------------------------------------- #[cfg(cfail1)] -fn change_field_value_tuple_like() -> Enum { +pub fn change_field_value_tuple_like() -> Enum { Enum::Tuple(0, 1, 2) } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_value_tuple_like() -> Enum { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_value_tuple_like() -> Enum { Enum::Tuple(0, 1, 3) } @@ -223,18 +201,17 @@ fn change_field_value_tuple_like() -> Enum { // Change constructor path (tuple-like) -------------------------------------- #[cfg(cfail1)] -fn change_constructor_path_tuple_like() { +pub fn change_constructor_path_tuple_like() { let _ = Enum::Tuple(0, 1, 2); } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_path_tuple_like() { +#[rustc_clean( + cfg="cfail2", + except="HirBody,MirOptimized,MirValidated,TypeckTables" +)] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_path_tuple_like() { let _ = Enum2::Tuple(0, 1, 2); } @@ -242,36 +219,35 @@ fn change_constructor_path_tuple_like() { // Change constructor variant (tuple-like) -------------------------------------- #[cfg(cfail1)] -fn change_constructor_variant_tuple_like() { +pub fn change_constructor_variant_tuple_like() { let _ = Enum2::Tuple(0, 1, 2); } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_variant_tuple_like() { +#[rustc_clean( + cfg="cfail2", + except="HirBody,MirOptimized,MirValidated,TypeckTables" +)] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_variant_tuple_like() { let _ = Enum2::Tuple2(0, 1, 2); } // Change constructor path indirectly (tuple-like) --------------------------- -mod change_constructor_path_indirectly_tuple_like { +pub mod change_constructor_path_indirectly_tuple_like { #[cfg(cfail1)] use super::Enum as TheEnum; #[cfg(not(cfail1))] use super::Enum2 as TheEnum; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> TheEnum { + #[rustc_clean( + cfg="cfail2", + except="FnSignature,Hir,HirBody,MirOptimized,MirValidated,\ + TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> TheEnum { TheEnum::Tuple(0, 1, 2) } } @@ -279,32 +255,28 @@ mod change_constructor_path_indirectly_tuple_like { // Change constructor variant indirectly (tuple-like) --------------------------- -mod change_constructor_variant_indirectly_tuple_like { +pub mod change_constructor_variant_indirectly_tuple_like { use super::Enum2; #[cfg(cfail1)] use super::Enum2::Tuple as Variant; #[cfg(not(cfail1))] use super::Enum2::Tuple2 as Variant; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> Enum2 { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> Enum2 { Variant(0, 1, 2) } } -enum Clike { +pub enum Clike { A, B, C } -enum Clike2 { +pub enum Clike2 { B, C, D @@ -312,18 +284,14 @@ enum Clike2 { // Change constructor path (C-like) -------------------------------------- #[cfg(cfail1)] -fn change_constructor_path_c_like() { +pub fn change_constructor_path_c_like() { let _ = Clike::B; } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_path_c_like() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_path_c_like() { let _ = Clike2::B; } @@ -331,36 +299,32 @@ fn change_constructor_path_c_like() { // Change constructor variant (C-like) -------------------------------------- #[cfg(cfail1)] -fn change_constructor_variant_c_like() { +pub fn change_constructor_variant_c_like() { let _ = Clike::A; } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_variant_c_like() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_variant_c_like() { let _ = Clike::C; } // Change constructor path indirectly (C-like) --------------------------- -mod change_constructor_path_indirectly_c_like { +pub mod change_constructor_path_indirectly_c_like { #[cfg(cfail1)] use super::Clike as TheEnum; #[cfg(not(cfail1))] use super::Clike2 as TheEnum; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> TheEnum { + #[rustc_clean( + cfg="cfail2", + except="FnSignature,Hir,HirBody,MirOptimized,MirValidated,\ + TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> TheEnum { TheEnum::B } } @@ -368,20 +332,16 @@ mod change_constructor_path_indirectly_c_like { // Change constructor variant indirectly (C-like) --------------------------- -mod change_constructor_variant_indirectly_c_like { +pub mod change_constructor_variant_indirectly_c_like { use super::Clike; #[cfg(cfail1)] use super::Clike::A as Variant; #[cfg(not(cfail1))] use super::Clike::B as Variant; - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> Clike { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> Clike { Variant } } diff --git a/src/test/incremental/hashes/enum_defs.rs b/src/test/incremental/hashes/enum_defs.rs index 8f84266d5a4e5..dbb7aca1924d3 100644 --- a/src/test/incremental/hashes/enum_defs.rs +++ b/src/test/incremental/hashes/enum_defs.rs @@ -37,13 +37,9 @@ enum EnumVisibility { A } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] pub enum EnumVisibility { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] A } @@ -57,15 +53,10 @@ enum EnumChangeNameCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeNameCStyleVariant { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant1, - #[rustc_metadata_clean(cfg="cfail3")] Variant2Changed, } @@ -79,10 +70,8 @@ enum EnumChangeNameTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeNameTupleStyleVariant { Variant1, Variant2Changed(u32, f32), @@ -98,10 +87,8 @@ enum EnumChangeNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeNameStructStyleVariant { Variant1, Variant2Changed { a: u32, b: f32 }, @@ -117,20 +104,12 @@ enum EnumChangeValueCStyleVariant0 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant0 { Variant1, - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant2 = - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] 22, } @@ -141,12 +120,8 @@ enum EnumChangeValueCStyleVariant1 { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant1 { Variant1, Variant2 = 11, @@ -161,10 +136,8 @@ enum EnumAddCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddCStyleVariant { Variant1, Variant2, @@ -180,10 +153,8 @@ enum EnumRemoveCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumRemoveCStyleVariant { Variant1, } @@ -197,10 +168,8 @@ enum EnumAddTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddTupleStyleVariant { Variant1, Variant2(u32, f32), @@ -216,10 +185,8 @@ enum EnumRemoveTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumRemoveTupleStyleVariant { Variant1, } @@ -233,10 +200,8 @@ enum EnumAddStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddStructStyleVariant { Variant1, Variant2 { a: u32, b: f32 }, @@ -252,10 +217,8 @@ enum EnumRemoveStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumRemoveStructStyleVariant { Variant1, } @@ -269,14 +232,10 @@ enum EnumChangeFieldTypeTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeTupleStyleVariant { Variant1(u32, - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] u64), } @@ -290,16 +249,12 @@ enum EnumChangeFieldTypeStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeStructStyleVariant { Variant1, Variant2 { a: u32, - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] b: u64 }, } @@ -313,10 +268,8 @@ enum EnumChangeFieldNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeFieldNameStructStyleVariant { Variant1 { a: u32, c: u32 }, } @@ -330,17 +283,11 @@ enum EnumChangeOrderTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeOrderTupleStyleVariant { Variant1( - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] u64, - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] u32), } @@ -353,10 +300,8 @@ enum EnumChangeFieldOrderStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeFieldOrderStructStyleVariant { Variant1 { b: f32, a: u32 }, } @@ -370,10 +315,8 @@ enum EnumAddFieldTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddFieldTupleStyleVariant { Variant1(u32, u32, u32), } @@ -387,10 +330,8 @@ enum EnumAddFieldStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddFieldStructStyleVariant { Variant1 { a: u32, b: u32, c: u32 }, } @@ -405,10 +346,8 @@ enum EnumAddMustUse { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] #[must_use] enum EnumAddMustUse { Variant1, @@ -425,10 +364,8 @@ enum EnumAddReprC { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] #[repr(C)] enum EnumAddReprC { Variant1, @@ -444,11 +381,8 @@ enum EnumChangeNameOfTypeParameter { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeNameOfTypeParameter { Variant1(T), } @@ -463,11 +397,8 @@ enum EnumAddTypeParameter { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] enum EnumAddTypeParameter { Variant1(S), Variant2(T), @@ -482,11 +413,8 @@ enum EnumChangeNameOfLifetimeParameter<'a> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="PredicatesOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumChangeNameOfLifetimeParameter<'b> { Variant1(&'b u32), } @@ -501,11 +429,8 @@ enum EnumAddLifetimeParameter<'a> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="PredicatesOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddLifetimeParameter<'a, 'b> { Variant1(&'a u32), Variant2(&'b u32), @@ -521,11 +446,8 @@ enum EnumAddLifetimeParameterBound<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="GenericsOfItem,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddLifetimeParameterBound<'a, 'b: 'a> { Variant1(&'a u32), Variant2(&'b u32), @@ -539,11 +461,8 @@ enum EnumAddLifetimeBoundToParameter<'a, T> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddLifetimeBoundToParameter<'a, T: 'a> { Variant1(T), Variant2(&'a u32), @@ -558,11 +477,8 @@ enum EnumAddTraitBound { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] enum EnumAddTraitBound { Variant1(T), } @@ -577,11 +493,8 @@ enum EnumAddLifetimeParameterBoundWhere<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="GenericsOfItem,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddLifetimeParameterBoundWhere<'a, 'b> where 'b: 'a { Variant1(&'a u32), Variant2(&'b u32), @@ -597,11 +510,8 @@ enum EnumAddLifetimeBoundToParameterWhere<'a, T> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2", except="TypeOfItem")] +#[rustc_clean(cfg="cfail3")] enum EnumAddLifetimeBoundToParameterWhere<'a, T> where T: 'a { Variant1(T), Variant2(&'a u32), @@ -616,11 +526,8 @@ enum EnumAddTraitBoundWhere { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -#[repr(C)] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] enum EnumAddTraitBoundWhere where T: Sync { Variant1(T), } @@ -635,23 +542,13 @@ enum EnumSwapUsageTypeParameters { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumSwapUsageTypeParameters { - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant1 { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] a: B }, - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant2 { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] a: A }, } @@ -666,23 +563,13 @@ enum EnumSwapUsageLifetimeParameters<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] enum EnumSwapUsageLifetimeParameters<'a, 'b> { - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant1 { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] a: &'b u32 }, - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant2 { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] b: &'a u32 }, } @@ -701,16 +588,10 @@ mod change_field_type_indirectly_tuple_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] enum TupleStyle { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant1( - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] FieldType ) } @@ -725,16 +606,10 @@ mod change_field_type_indirectly_struct_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] enum StructStyle { - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] Variant1 { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] a: FieldType } } @@ -754,10 +629,8 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,PredicatesOfItem")] + #[rustc_clean(cfg="cfail3")] enum Enum { Variant1(T) } @@ -772,10 +645,8 @@ mod change_trait_bound_indirectly_where { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,PredicatesOfItem")] + #[rustc_clean(cfg="cfail3")] enum Enum where T: Trait { Variant1(T) } diff --git a/src/test/incremental/hashes/exported_vs_not.rs b/src/test/incremental/hashes/exported_vs_not.rs index 082badacc6ccd..985c064f6a0a2 100644 --- a/src/test/incremental/hashes/exported_vs_not.rs +++ b/src/test/incremental/hashes/exported_vs_not.rs @@ -26,12 +26,8 @@ pub fn body_not_exported_to_metadata() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn body_not_exported_to_metadata() -> u32 { 2 } @@ -49,12 +45,8 @@ pub fn body_exported_to_metadata_because_of_inline() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_inline() -> u32 { 2 @@ -73,12 +65,8 @@ pub fn body_exported_to_metadata_because_of_generic() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_generic() -> u32 { 2 diff --git a/src/test/incremental/hashes/extern_mods.rs b/src/test/incremental/hashes/extern_mods.rs index 1d26e6c07d15b..7ccb452b7ed26 100644 --- a/src/test/incremental/hashes/extern_mods.rs +++ b/src/test/incremental/hashes/extern_mods.rs @@ -34,10 +34,8 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { pub fn change_function_name2(c: i64) -> i32; } @@ -51,13 +49,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn change_parameter_name(d: i64) -> i32; } @@ -70,13 +64,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn change_parameter_type(c: i32) -> i32; } @@ -89,13 +79,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn change_return_type(c: i32) -> i8; } @@ -108,13 +94,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn add_parameter(c: i32, d: i32) -> i32; } @@ -127,13 +109,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn add_return_type(c: i32) -> i32; } @@ -146,13 +124,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn make_function_variadic(c: i32, ...); } @@ -165,13 +139,9 @@ extern "C" { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern "rust-call" { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn change_calling_convention(c: i32); } @@ -184,13 +154,9 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn make_function_public(c: i32); } @@ -203,10 +169,8 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] extern { pub fn add_function1(c: i32); pub fn add_function2(); @@ -222,10 +186,8 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] #[link_args = "-foo -bar -baz"] extern { pub fn change_link_args(c: i32); @@ -241,10 +203,8 @@ extern { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_dirty(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] #[link(name = "bar")] extern { pub fn change_link_name(c: i32); @@ -260,13 +220,9 @@ mod indirectly_change_parameter_type { #[cfg(not(cfail1))] use super::c_i64 as c_int; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_dirty(cfg="cfail2")] + #[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn indirectly_change_parameter_type(c: c_int); } } @@ -280,13 +236,9 @@ mod indirectly_change_return_type { #[cfg(not(cfail1))] use super::c_i64 as c_int; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_dirty(cfg="cfail2")] + #[rustc_clean(cfg="cfail3")] extern { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub fn indirectly_change_return_type() -> c_int; } } diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index bae3c9bf5965d..763b0cd05d4f9 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -40,8 +40,6 @@ fn change_loop_body() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_body() { let mut _x = 0; for _ in 0..1 { @@ -67,8 +65,6 @@ fn change_iteration_variable_name() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_iteration_variable_name() { let mut _x = 0; for _a in 0..1 { @@ -94,8 +90,6 @@ fn change_iteration_variable_pattern() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_iteration_variable_pattern() { let mut _x = 0; for &_i in &[0, 1, 2] { @@ -121,8 +115,6 @@ fn change_iterable() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_iterable() { let mut _x = 0; for _ in &[0, 1, 3] { @@ -147,8 +139,6 @@ fn add_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_break() { let mut _x = 0; for _ in 0..1 { @@ -174,8 +164,6 @@ fn add_loop_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label() { let mut _x = 0; 'label: for _ in 0..1 { @@ -201,8 +189,6 @@ fn add_loop_label_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_break() { let mut _x = 0; 'label: for _ in 0..1 { @@ -230,8 +216,6 @@ fn change_break_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_break_label() { let mut _x = 0; 'outer: for _ in 0..1 { @@ -259,8 +243,6 @@ fn add_loop_label_to_continue() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_continue() { let mut _x = 0; 'label: for _ in 0..1 { @@ -288,8 +270,6 @@ fn change_continue_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_label() { let mut _x = 0; 'outer: for _ in 0..1 { @@ -317,8 +297,6 @@ fn change_continue_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_to_break() { let mut _x = 0; for _ in 0..1 { diff --git a/src/test/incremental/hashes/function_interfaces.rs b/src/test/incremental/hashes/function_interfaces.rs index 2fe3f0d5d1fe0..b3eb566367c72 100644 --- a/src/test/incremental/hashes/function_interfaces.rs +++ b/src/test/incremental/hashes/function_interfaces.rs @@ -37,8 +37,6 @@ fn add_parameter() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_parameter(p: i32) {} @@ -50,8 +48,6 @@ fn add_return_type() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] // The type doesn't change, so metadata is the same -#[rustc_metadata_clean(cfg="cfail3")] fn add_return_type() -> () {} @@ -63,8 +59,6 @@ fn type_of_parameter(p: i32) {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn type_of_parameter(p: i64) {} @@ -76,8 +70,6 @@ fn type_of_parameter_ref(p: &i32) {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn type_of_parameter_ref(p: &mut i32) {} @@ -89,8 +81,6 @@ fn order_of_parameters(p1: i32, p2: i64) {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn order_of_parameters(p2: i64, p1: i32) {} @@ -102,8 +92,6 @@ fn make_unsafe() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] unsafe fn make_unsafe() {} @@ -115,8 +103,6 @@ fn make_extern() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] extern fn make_extern() {} @@ -128,8 +114,6 @@ extern "C" fn make_intrinsic() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] extern "rust-intrinsic" fn make_intrinsic() {} @@ -141,8 +125,6 @@ fn type_parameter() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn type_parameter() {} @@ -154,8 +136,6 @@ fn lifetime_parameter() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -// #[rustc_metadata_dirty(cfg="cfail2")] -- Unused lifetime params don't show up in the type? -#[rustc_metadata_clean(cfg="cfail3")] fn lifetime_parameter<'a>() {} @@ -167,8 +147,6 @@ fn trait_bound() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn trait_bound() {} @@ -180,8 +158,6 @@ fn builtin_bound() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn builtin_bound() {} @@ -193,8 +169,6 @@ fn lifetime_bound<'a, T>() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn lifetime_bound<'a, T: 'a>() {} @@ -206,8 +180,6 @@ fn second_trait_bound() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn second_trait_bound() {} @@ -219,8 +191,6 @@ fn second_builtin_bound() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn second_builtin_bound() {} @@ -232,8 +202,6 @@ fn second_lifetime_bound<'a, 'b, T: 'a>() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn second_lifetime_bound<'a, 'b, T: 'a + 'b>() {} @@ -245,8 +213,6 @@ fn inline() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[inline] fn inline() {} @@ -260,8 +226,6 @@ fn inline_never() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[inline(never)] fn inline_never() {} @@ -274,8 +238,6 @@ fn no_mangle() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[no_mangle] fn no_mangle() {} @@ -288,8 +250,6 @@ fn linkage() {} #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[linkage="weak_odr"] fn linkage() {} @@ -304,8 +264,6 @@ fn return_impl_trait() -> i32 { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn return_impl_trait() -> impl Clone { 0 } @@ -321,8 +279,6 @@ fn change_return_impl_trait() -> impl Clone { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] // The actual type is the same, so: clean -#[rustc_metadata_clean(cfg="cfail3")] fn change_return_impl_trait() -> impl Copy { 0u32 } @@ -341,8 +297,6 @@ mod change_return_type_indirectly { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn indirect_return_type() -> ReturnType { ReturnType {} } @@ -359,8 +313,6 @@ mod change_parameter_type_indirectly { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn indirect_parameter_type(p: ParameterType) {} } @@ -378,8 +330,6 @@ mod change_trait_bound_indirectly { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn indirect_trait_bound(p: T) {} } @@ -394,7 +344,5 @@ mod change_trait_bound_indirectly_in_where_clause { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn indirect_trait_bound_where(p: T) where T: Trait {} } diff --git a/src/test/incremental/hashes/if_expressions.rs b/src/test/incremental/hashes/if_expressions.rs index c39eeab34c8f3..d6878028cfae2 100644 --- a/src/test/incremental/hashes/if_expressions.rs +++ b/src/test/incremental/hashes/if_expressions.rs @@ -36,12 +36,8 @@ pub fn change_condition(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_condition(x: bool) -> u32 { if !x { return 1 @@ -61,12 +57,8 @@ pub fn change_then_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_then_branch(x: bool) -> u32 { if x { return 2 @@ -88,12 +80,8 @@ pub fn change_else_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_else_branch(x: bool) -> u32 { if x { 1 @@ -110,24 +98,20 @@ pub fn add_else_branch(x: bool) -> u32 { let mut ret = 1; if x { - ret += 1; + ret = 2; } ret } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_else_branch(x: bool) -> u32 { let mut ret = 1; if x { - ret += 1; + ret = 2; } else { } @@ -147,12 +131,8 @@ pub fn change_condition_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_condition_if_let(x: Option) -> u32 { if let Some(_) = x { return 1 @@ -174,12 +154,8 @@ pub fn change_then_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_then_branch_if_let(x: Option) -> u32 { if let Some(x) = x { return x + 1 @@ -201,12 +177,8 @@ pub fn change_else_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_else_branch_if_let(x: Option) -> u32 { if let Some(x) = x { x @@ -223,24 +195,20 @@ pub fn add_else_branch_if_let(x: Option) -> u32 { let mut ret = 1; if let Some(x) = x { - ret += x; + ret = x; } ret } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_else_branch_if_let(x: Option) -> u32 { let mut ret = 1; if let Some(x) = x { - ret += x; + ret = x; } else { } diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs index a12624d083248..715146146f148 100644 --- a/src/test/incremental/hashes/indexing_expressions.rs +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -10,7 +10,7 @@ // This test case tests the incremental compilation hash (ICH) implementation -// for closure expression. +// for indexing expression. // The general pattern followed here is: Change one thing between rev1 and rev2 // and make sure that the hash has changed, then change nothing between rev2 and @@ -36,8 +36,6 @@ fn change_simple_index(slice: &[u32]) -> u32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_simple_index(slice: &[u32]) -> u32 { slice[4] } @@ -55,8 +53,6 @@ fn change_lower_bound(slice: &[u32]) -> &[u32] { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_lower_bound(slice: &[u32]) -> &[u32] { &slice[2..5] } @@ -74,8 +70,6 @@ fn change_upper_bound(slice: &[u32]) -> &[u32] { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -93,8 +87,6 @@ fn add_lower_bound(slice: &[u32]) -> &[u32] { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_lower_bound(slice: &[u32]) -> &[u32] { &slice[3..4] } @@ -112,8 +104,6 @@ fn add_upper_bound(slice: &[u32]) -> &[u32] { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -131,8 +121,6 @@ fn change_mutability(slice: &mut [u32]) -> u32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_mutability(slice: &mut [u32]) -> u32 { (&slice[3..5])[0] } @@ -150,8 +138,6 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { &slice[3..=7] } diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index daddc0c9f5459..c8c2fa5e8c813 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -25,7 +25,7 @@ #![feature(rustc_attrs)] #![crate_type="rlib"] -struct Foo; +pub struct Foo; // Change Method Name ----------------------------------------------------------- #[cfg(cfail1)] @@ -34,13 +34,10 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,AssociatedItemDefIds")] +#[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail3")] pub fn method_name2() { } } @@ -53,17 +50,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] + #[rustc_clean(cfg="cfail3")] pub fn method_body() { println!("Hello, world!"); } @@ -80,17 +71,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] + #[rustc_clean(cfg="cfail3")] #[inline] pub fn method_body_inlined() { println!("Hello, world!"); @@ -105,15 +90,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="AssociatedItems,Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] fn method_privacy() { } } @@ -124,15 +105,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(cfg="cfail2", except="TypeOfItem,PredicatesOfItem")] + #[rustc_clean(cfg="cfail3")] pub fn method_selfness(&self) { } } @@ -143,15 +120,14 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,FnSignature,TypeckTables,MirOptimized,MirValidated" + )] + #[rustc_clean(cfg="cfail3")] pub fn method_selfmutness(&mut self) { } } @@ -164,19 +140,14 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,AssociatedItemDefIds")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2")] + #[rustc_clean(cfg="cfail3")] pub fn add_method_to_impl1(&self) { } - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail3")] pub fn add_method_to_impl2(&self) { } } @@ -189,15 +160,14 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,FnSignature,TypeckTables,MirOptimized,MirValidated" + )] + #[rustc_clean(cfg="cfail3")] pub fn add_method_parameter(&self, _: i32) { } } @@ -210,17 +180,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] + #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_name(&self, b: i64) { } } @@ -233,15 +197,13 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,FnSignature,MirOptimized,MirValidated,TypeckTables")] + #[rustc_clean(cfg="cfail3")] pub fn change_method_return_type(&self) -> u8 { 0 } } @@ -254,15 +216,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] #[inline] pub fn make_method_inline(&self) -> u8 { 0 } } @@ -276,17 +234,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] + #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_order(&self, b: i64, a: i64) { } } @@ -299,15 +251,14 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,FnSignature,TypeckTables,MirOptimized,MirValidated" + )] + #[rustc_clean(cfg="cfail3")] pub unsafe fn make_method_unsafe(&self) { } } @@ -320,15 +271,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,FnSignature,TypeckTables")] + #[rustc_clean(cfg="cfail3")] pub extern fn make_method_extern(&self) { } } @@ -341,15 +288,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,FnSignature,TypeckTables")] + #[rustc_clean(cfg="cfail3")] pub extern "system" fn change_method_calling_convention(&self) { } } @@ -362,15 +305,20 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + // Warning: Note that `TypeckTables` are coming up clean here. + // The addition or removal of lifetime parameters that don't + // appear in the arguments or fn body in any way does not, in + // fact, affect the `TypeckTables` in any semantic way (at least + // as of this writing). **However,** altering the order of + // lowering **can** cause it appear to affect the `TypeckTables`: + // if we lower generics before the body, then the `HirId` for + // things in the body will be affected. So if you start to see + // `TypeckTables` appear dirty, that might be the cause. -nmatsakis + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_parameter_to_method<'a>(&self) { } } @@ -383,15 +331,23 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + // Warning: Note that `TypeckTables` are coming up clean here. + // The addition or removal of type parameters that don't appear in + // the arguments or fn body in any way does not, in fact, affect + // the `TypeckTables` in any semantic way (at least as of this + // writing). **However,** altering the order of lowering **can** + // cause it appear to affect the `TypeckTables`: if we lower + // generics before the body, then the `HirId` for things in the + // body will be affected. So if you start to see `TypeckTables` + // appear dirty, that might be the cause. -nmatsakis + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,GenericsOfItem,PredicatesOfItem,TypeOfItem", + )] + #[rustc_clean(cfg="cfail3")] pub fn add_type_parameter_to_method(&self) { } } @@ -404,15 +360,14 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean( + cfg="cfail2", + except="Hir,HirBody,GenericsOfItem,PredicatesOfItem,TypeOfItem,TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b: 'a>(&self) { } } @@ -425,15 +380,21 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + // Warning: Note that `TypeckTables` are coming up clean here. + // The addition or removal of bounds that don't appear in the + // arguments or fn body in any way does not, in fact, affect the + // `TypeckTables` in any semantic way (at least as of this + // writing). **However,** altering the order of lowering **can** + // cause it appear to affect the `TypeckTables`: if we lower + // generics before the body, then the `HirId` for things in the + // body will be affected. So if you start to see `TypeckTables` + // appear dirty, that might be the cause. -nmatsakis + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,GenericsOfItem,PredicatesOfItem,\ + TypeOfItem")] + #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_type_param_of_method<'a, T: 'a>(&self) { } } @@ -446,15 +407,20 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + // Warning: Note that `TypeckTables` are coming up clean here. + // The addition or removal of bounds that don't appear in the + // arguments or fn body in any way does not, in fact, affect the + // `TypeckTables` in any semantic way (at least as of this + // writing). **However,** altering the order of lowering **can** + // cause it appear to affect the `TypeckTables`: if we lower + // generics before the body, then the `HirId` for things in the + // body will be affected. So if you start to see `TypeckTables` + // appear dirty, that might be the cause. -nmatsakis + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,PredicatesOfItem")] + #[rustc_clean(cfg="cfail3")] pub fn add_trait_bound_to_type_param_of_method(&self) { } } @@ -467,15 +433,11 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -impl Foo { - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] #[no_mangle] pub fn add_no_mangle_to_method(&self) { } } @@ -491,15 +453,14 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,GenericsOfItem")] +#[rustc_clean(cfg="cfail3")] impl Bar { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean( + cfg="cfail2", + except="GenericsOfItem,FnSignature,TypeckTables,TypeOfItem,MirOptimized,MirValidated" + )] + #[rustc_clean(cfg="cfail3")] pub fn add_type_parameter_to_impl(&self) { } } @@ -512,15 +473,11 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] impl Bar { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="FnSignature,MirOptimized,MirValidated,TypeckTables")] + #[rustc_clean(cfg="cfail3")] pub fn change_impl_self_type(&self) { } } @@ -533,15 +490,11 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] impl Bar { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2")] + #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_impl_parameter(&self) { } } @@ -554,14 +507,26 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] impl Bar { - #[rustc_clean(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2")] + #[rustc_clean(cfg="cfail3")] pub fn add_trait_bound_to_impl_parameter(&self) { } } + + +// Force instantiation of some fns so we can check their hash. +pub fn instantiation_root() { + Foo::method_privacy(); + + #[cfg(cfail1)] + { + Bar(0u32).change_impl_self_type(); + } + + #[cfg(not(cfail1))] + { + Bar(0u64).change_impl_self_type(); + } +} diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs index a1057c036d6c4..0947239c573c2 100644 --- a/src/test/incremental/hashes/inline_asm.rs +++ b/src/test/incremental/hashes/inline_asm.rs @@ -48,8 +48,6 @@ fn change_template(a: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_template(a: i32) -> i32 { let c: i32; @@ -88,8 +86,6 @@ fn change_output(a: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_output(a: i32) -> i32 { let mut _out1: i32 = 0; @@ -128,8 +124,6 @@ fn change_input(_a: i32, _b: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_input(_a: i32, _b: i32) -> i32 { let _out; @@ -167,8 +161,6 @@ fn change_input_constraint(_a: i32, _b: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_input_constraint(_a: i32, _b: i32) -> i32 { let _out; @@ -206,8 +198,6 @@ fn change_clobber(_a: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_clobber(_a: i32) -> i32 { let _out; @@ -245,8 +235,6 @@ fn change_options(_a: i32) -> i32 { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn change_options(_a: i32) -> i32 { let _out; diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 9e532548e11dd..f3bddc669842b 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -32,12 +32,9 @@ pub fn change_name() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_name() { let _y = 2u64; } @@ -51,12 +48,9 @@ pub fn add_type() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_type() { let _x: u32 = 2u32; } @@ -70,12 +64,9 @@ pub fn change_type() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_type() { let _x: u8 = 2; } @@ -89,12 +80,9 @@ pub fn change_mutability_of_reference_type() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated")] +#[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_reference_type() { let _x: &mut u64; } @@ -108,12 +96,9 @@ pub fn change_mutability_of_slot() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; } @@ -127,12 +112,9 @@ pub fn change_simple_binding_to_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_simple_binding_to_pattern() { let (_a, _b) = (0u8, 'x'); } @@ -146,12 +128,9 @@ pub fn change_name_in_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern() { let (_a, _c) = (1u8, 'y'); } @@ -165,12 +144,9 @@ pub fn add_ref_in_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn add_ref_in_pattern() { let (ref _a, _b) = (1u8, 'y'); } @@ -184,12 +160,9 @@ pub fn add_amp_in_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn add_amp_in_pattern() { let (&_a, _b) = (&1u8, 'y'); } @@ -203,12 +176,9 @@ pub fn change_mutability_of_binding_in_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern() { let (mut _a, _b) = (99u8, 'q'); } @@ -222,12 +192,9 @@ pub fn add_initializer() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,TypeckTables,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn add_initializer() { let _x: i16 = 3i16; } @@ -241,12 +208,9 @@ pub fn change_initializer() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_initializer() { let _x = 5u16; } diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index da43ef3c461b7..8d015288757bf 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -40,8 +40,6 @@ fn change_loop_body() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_body() { let mut _x = 0; loop { @@ -66,8 +64,6 @@ fn add_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_break() { let mut _x = 0; loop { @@ -93,8 +89,6 @@ fn add_loop_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label() { let mut _x = 0; 'label: loop { @@ -120,8 +114,6 @@ fn add_loop_label_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_break() { let mut _x = 0; 'label: loop { @@ -149,8 +141,6 @@ fn change_break_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_break_label() { let mut _x = 0; 'outer: loop { @@ -178,8 +168,6 @@ fn add_loop_label_to_continue() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_continue() { let mut _x = 0; 'label: loop { @@ -207,8 +195,6 @@ fn change_continue_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_label() { let mut _x = 0; 'outer: loop { @@ -236,8 +222,6 @@ fn change_continue_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_to_break() { let mut _x = 0; loop { diff --git a/src/test/incremental/hashes/match_expressions.rs b/src/test/incremental/hashes/match_expressions.rs index 48f99b834ce17..38edd675cc637 100644 --- a/src/test/incremental/hashes/match_expressions.rs +++ b/src/test/incremental/hashes/match_expressions.rs @@ -36,12 +36,9 @@ pub fn add_arm(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_arm(x: u32) -> u32 { match x { 0 => 0, @@ -64,12 +61,9 @@ pub fn change_order_of_arms(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_order_of_arms(x: u32) -> u32 { match x { 1 => 1, @@ -91,12 +85,9 @@ pub fn add_guard_clause(x: u32, y: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_guard_clause(x: u32, y: bool) -> u32 { match x { 0 => 0, @@ -118,12 +109,9 @@ pub fn change_guard_clause(x: u32, y: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_guard_clause(x: u32, y: bool) -> u32 { match x { 0 => 0, @@ -145,12 +133,9 @@ pub fn add_at_binding(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_at_binding(x: u32) -> u32 { match x { 0 => 0, @@ -172,12 +157,9 @@ pub fn change_name_of_at_binding(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_name_of_at_binding(x: u32) -> u32 { match x { 0 => 0, @@ -193,21 +175,18 @@ pub fn change_name_of_at_binding(x: u32) -> u32 { pub fn change_simple_name_to_pattern(x: u32) -> u32 { match (x, x & 1) { (0, 0) => 0, - a => 1 + a => 1, } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_simple_name_to_pattern(x: u32) -> u32 { match (x, x & 1) { (0, 0) => 0, - (x, y) => 1 + (x, y) => 1, } } @@ -224,12 +203,9 @@ pub fn change_name_in_pattern(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern(x: u32) -> u32 { match (x, x & 1) { (b, 0) => 0, @@ -245,21 +221,18 @@ pub fn change_name_in_pattern(x: u32) -> u32 { pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { (a, 0) => 0, - _ => 1 + _ => 1, } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { (mut a, 0) => 0, - _ => 1 + _ => 1, } } @@ -270,21 +243,18 @@ pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { (a, 0) => 0, - _ => 1 + _ => 1, } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { (ref a, 0) => 0, - _ => 1, + _ => 1, } } @@ -295,21 +265,18 @@ pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { match (&x, x & 1) { (a, 0) => 0, - _ => 1 + _ => 1, } } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", +except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { match (&x, x & 1) { (&a, 0) => 0, - _ => 1, + _ => 1, } } @@ -326,12 +293,9 @@ pub fn change_rhs_of_arm(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn change_rhs_of_arm(x: u32) -> u32 { match x { 0 => 0, @@ -353,12 +317,9 @@ pub fn add_alternative_to_arm(x: u32) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", + except="HirBody,MirValidated,MirOptimized,TypeckTables")] +#[rustc_clean(cfg="cfail3")] pub fn add_alternative_to_arm(x: u32) -> u32 { match x { 0 | 7 => 0, diff --git a/src/test/incremental/hashes/panic_exprs.rs b/src/test/incremental/hashes/panic_exprs.rs index 5d4d434fd633f..c76c10f2ab443 100644 --- a/src/test/incremental/hashes/panic_exprs.rs +++ b/src/test/incremental/hashes/panic_exprs.rs @@ -34,12 +34,8 @@ pub fn indexing(slice: &[u8]) -> u8 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn indexing(slice: &[u8]) -> u8 { slice[100] } @@ -52,12 +48,8 @@ pub fn arithmetic_overflow_plus(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_plus(val: i32) -> i32 { val + 1 } @@ -70,12 +62,8 @@ pub fn arithmetic_overflow_minus(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_minus(val: i32) -> i32 { val - 1 } @@ -88,12 +76,8 @@ pub fn arithmetic_overflow_mult(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_mult(val: i32) -> i32 { val * 2 } @@ -106,12 +90,8 @@ pub fn arithmetic_overflow_negation(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_negation(val: i32) -> i32 { -val } @@ -124,12 +104,8 @@ pub fn division_by_zero(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn division_by_zero(val: i32) -> i32 { 2 / val } @@ -141,35 +117,54 @@ pub fn mod_by_zero(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn mod_by_zero(val: i32) -> i32 { 2 % val } +// shift left ------------------------------------------------------------------ +#[cfg(cfail1)] +pub fn shift_left(val: i32, shift: usize) -> i32 { + val << shift +} + +#[cfg(not(cfail1))] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] +pub fn shift_left(val: i32, shift: usize) -> i32 { + val << shift +} + + +// shift right ------------------------------------------------------------------ +#[cfg(cfail1)] +pub fn shift_right(val: i32, shift: usize) -> i32 { + val >> shift +} + +#[cfg(not(cfail1))] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] +pub fn shift_right(val: i32, shift: usize) -> i32 { + val >> shift +} + // THE FOLLOWING ITEMS SHOULD NOT BE INFLUENCED BY THEIR SOURCE LOCATION // bitwise --------------------------------------------------------------------- #[cfg(cfail1)] pub fn bitwise(val: i32) -> i32 { - !val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1 + !val & 0x101010101 | 0x45689 ^ 0x2372382 } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise(val: i32) -> i32 { - !val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1 + !val & 0x101010101 | 0x45689 ^ 0x2372382 } @@ -180,12 +175,8 @@ pub fn logical(val1: bool, val2: bool, val3: bool) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn logical(val1: bool, val2: bool, val3: bool) -> bool { val1 && val2 || val3 } diff --git a/src/test/incremental/hashes/panic_exprs_no_overflow_checks.rs b/src/test/incremental/hashes/panic_exprs_no_overflow_checks.rs index b3fc8e2d36d1c..8402da04091ec 100644 --- a/src/test/incremental/hashes/panic_exprs_no_overflow_checks.rs +++ b/src/test/incremental/hashes/panic_exprs_no_overflow_checks.rs @@ -41,12 +41,8 @@ pub fn indexing(slice: &[u8]) -> u8 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn indexing(slice: &[u8]) -> u8 { slice[100] } @@ -60,12 +56,8 @@ pub fn arithmetic_overflow_plus_inherit(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[rustc_inherit_overflow_checks] pub fn arithmetic_overflow_plus_inherit(val: i32) -> i32 { val + 1 @@ -80,12 +72,8 @@ pub fn arithmetic_overflow_minus_inherit(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[rustc_inherit_overflow_checks] pub fn arithmetic_overflow_minus_inherit(val: i32) -> i32 { val - 1 @@ -100,12 +88,8 @@ pub fn arithmetic_overflow_mult_inherit(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[rustc_inherit_overflow_checks] pub fn arithmetic_overflow_mult_inherit(val: i32) -> i32 { val * 2 @@ -120,12 +104,8 @@ pub fn arithmetic_overflow_negation_inherit(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] #[rustc_inherit_overflow_checks] pub fn arithmetic_overflow_negation_inherit(val: i32) -> i32 { -val @@ -139,12 +119,8 @@ pub fn division_by_zero(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn division_by_zero(val: i32) -> i32 { 2 / val } @@ -156,12 +132,8 @@ pub fn mod_by_zero(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="HirBody,MirValidated,MirOptimized")] +#[rustc_clean(cfg="cfail3")] pub fn mod_by_zero(val: i32) -> i32 { 2 % val } @@ -177,12 +149,8 @@ pub fn bitwise(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise(val: i32) -> i32 { !val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1 } @@ -195,12 +163,8 @@ pub fn logical(val1: bool, val2: bool, val3: bool) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn logical(val1: bool, val2: bool, val3: bool) -> bool { val1 && val2 || val3 } @@ -212,12 +176,8 @@ pub fn arithmetic_overflow_plus(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_plus(val: i32) -> i32 { val + 1 } @@ -230,12 +190,8 @@ pub fn arithmetic_overflow_minus(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_minus(val: i32) -> i32 { val - 1 } @@ -248,12 +204,8 @@ pub fn arithmetic_overflow_mult(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_mult(val: i32) -> i32 { val * 2 } @@ -266,12 +218,8 @@ pub fn arithmetic_overflow_negation(val: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_clean(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_negation(val: i32) -> i32 { -val } diff --git a/src/test/incremental/hashes/statics.rs b/src/test/incremental/hashes/statics.rs index 7c6da3ba9fea6..e729a2c039e4c 100644 --- a/src/test/incremental/hashes/statics.rs +++ b/src/test/incremental/hashes/statics.rs @@ -32,10 +32,8 @@ static STATIC_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] pub static STATIC_VISIBILITY: u8 = 0; @@ -44,10 +42,8 @@ pub static STATIC_VISIBILITY: u8 = 0; static STATIC_MUTABILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] static mut STATIC_MUTABILITY: u8 = 0; @@ -56,10 +52,8 @@ static mut STATIC_MUTABILITY: u8 = 0; static STATIC_LINKAGE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] #[linkage="weak_odr"] static STATIC_LINKAGE: u8 = 0; @@ -69,10 +63,8 @@ static STATIC_LINKAGE: u8 = 0; static STATIC_NO_MANGLE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] #[no_mangle] static STATIC_NO_MANGLE: u8 = 0; @@ -82,10 +74,8 @@ static STATIC_NO_MANGLE: u8 = 0; static STATIC_THREAD_LOCAL: u8 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] #[thread_local] static STATIC_THREAD_LOCAL: u8 = 0; @@ -95,10 +85,8 @@ static STATIC_THREAD_LOCAL: u8 = 0; static STATIC_CHANGE_TYPE_1: i16 = 0; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_1: u64 = 0; @@ -107,65 +95,53 @@ static STATIC_CHANGE_TYPE_1: u64 = 0; static STATIC_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] +#[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_2: Option = None; // Change value between simple literals --------------------------------------- -#[cfg(cfail1)] -static STATIC_CHANGE_VALUE_1: i16 = 1; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +static STATIC_CHANGE_VALUE_1: i16 = { + #[cfg(cfail1)] + { 1 } -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -static STATIC_CHANGE_VALUE_1: i16 = 2; + #[cfg(not(cfail1))] + { 2 } +}; // Change value between expressions ------------------------------------------- -#[cfg(cfail1)] -static STATIC_CHANGE_VALUE_2: i16 = 1 + 1; - -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -static STATIC_CHANGE_VALUE_2: i16 = 1 + 2; - +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +static STATIC_CHANGE_VALUE_2: i16 = { + #[cfg(cfail1)] + { 1 + 1 } -#[cfg(cfail1)] -static STATIC_CHANGE_VALUE_3: i16 = 2 + 3; + #[cfg(not(cfail1))] + { 1 + 2 } +}; -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -static STATIC_CHANGE_VALUE_3: i16 = 2 * 3; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +static STATIC_CHANGE_VALUE_3: i16 = { + #[cfg(cfail1)] + { 2 + 3 } + #[cfg(not(cfail1))] + { 2 * 3 } +}; -#[cfg(cfail1)] -static STATIC_CHANGE_VALUE_4: i16 = 1 + 2 * 3; +#[rustc_clean(cfg="cfail2", except="HirBody")] +#[rustc_clean(cfg="cfail3")] +static STATIC_CHANGE_VALUE_4: i16 = { + #[cfg(cfail1)] + { 1 + 2 * 3 } -#[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -static STATIC_CHANGE_VALUE_4: i16 = 1 + 2 * 4; + #[cfg(not(cfail1))] + { 1 + 2 * 4 } +}; // Change type indirectly ----------------------------------------------------- @@ -179,15 +155,11 @@ mod static_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] + #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody,TypeOfItem")] + #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/struct_constructors.rs b/src/test/incremental/hashes/struct_constructors.rs index 0e23d953baf2d..a16f4a2fdfd65 100644 --- a/src/test/incremental/hashes/struct_constructors.rs +++ b/src/test/incremental/hashes/struct_constructors.rs @@ -25,7 +25,7 @@ #![crate_type="rlib"] -struct RegularStruct { +pub struct RegularStruct { x: i32, y: i64, z: i16, @@ -33,7 +33,7 @@ struct RegularStruct { // Change field value (regular struct) ----------------------------------------- #[cfg(cfail1)] -fn change_field_value_regular_struct() -> RegularStruct { +pub fn change_field_value_regular_struct() -> RegularStruct { RegularStruct { x: 0, y: 1, @@ -42,13 +42,9 @@ fn change_field_value_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_value_regular_struct() -> RegularStruct { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_value_regular_struct() -> RegularStruct { RegularStruct { x: 0, y: 2, @@ -60,7 +56,7 @@ fn change_field_value_regular_struct() -> RegularStruct { // Change field order (regular struct) ----------------------------------------- #[cfg(cfail1)] -fn change_field_order_regular_struct() -> RegularStruct { +pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { x: 3, y: 4, @@ -69,13 +65,9 @@ fn change_field_order_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_order_regular_struct() -> RegularStruct { +#[rustc_clean(cfg="cfail2", except="HirBody,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { y: 4, x: 3, @@ -87,7 +79,7 @@ fn change_field_order_regular_struct() -> RegularStruct { // Add field (regular struct) -------------------------------------------------- #[cfg(cfail1)] -fn add_field_regular_struct() -> RegularStruct { +pub fn add_field_regular_struct() -> RegularStruct { let struct1 = RegularStruct { x: 3, y: 4, @@ -101,13 +93,9 @@ fn add_field_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn add_field_regular_struct() -> RegularStruct { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn add_field_regular_struct() -> RegularStruct { let struct1 = RegularStruct { x: 3, y: 4, @@ -125,7 +113,7 @@ fn add_field_regular_struct() -> RegularStruct { // Change field label (regular struct) ----------------------------------------- #[cfg(cfail1)] -fn change_field_label_regular_struct() -> RegularStruct { +pub fn change_field_label_regular_struct() -> RegularStruct { let struct1 = RegularStruct { x: 3, y: 4, @@ -140,13 +128,9 @@ fn change_field_label_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_label_regular_struct() -> RegularStruct { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_label_regular_struct() -> RegularStruct { let struct1 = RegularStruct { x: 3, y: 4, @@ -162,7 +146,7 @@ fn change_field_label_regular_struct() -> RegularStruct { -struct RegularStruct2 { +pub struct RegularStruct2 { x: i8, y: i8, z: i8, @@ -170,7 +154,7 @@ struct RegularStruct2 { // Change constructor path (regular struct) ------------------------------------ #[cfg(cfail1)] -fn change_constructor_path_regular_struct() { +pub fn change_constructor_path_regular_struct() { let _ = RegularStruct { x: 0, y: 1, @@ -179,13 +163,9 @@ fn change_constructor_path_regular_struct() { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_path_regular_struct() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_path_regular_struct() { let _ = RegularStruct2 { x: 0, y: 1, @@ -196,19 +176,18 @@ fn change_constructor_path_regular_struct() { // Change constructor path indirectly (regular struct) ------------------------- -mod change_constructor_path_indirectly_regular_struct { +pub mod change_constructor_path_indirectly_regular_struct { #[cfg(cfail1)] use super::RegularStruct as Struct; #[cfg(not(cfail1))] use super::RegularStruct2 as Struct; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> Struct { + #[rustc_clean( + cfg="cfail2", + except="FnSignature,Hir,HirBody,MirOptimized,MirValidated,TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> Struct { Struct { x: 0, y: 1, @@ -219,62 +198,53 @@ mod change_constructor_path_indirectly_regular_struct { -struct TupleStruct(i32, i64, i16); +pub struct TupleStruct(i32, i64, i16); // Change field value (tuple struct) ------------------------------------------- #[cfg(cfail1)] -fn change_field_value_tuple_struct() -> TupleStruct { +pub fn change_field_value_tuple_struct() -> TupleStruct { TupleStruct(0, 1, 2) } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_field_value_tuple_struct() -> TupleStruct { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated")] +#[rustc_clean(cfg="cfail3")] +pub fn change_field_value_tuple_struct() -> TupleStruct { TupleStruct(0, 1, 3) } -struct TupleStruct2(u16, u16, u16); +pub struct TupleStruct2(u16, u16, u16); // Change constructor path (tuple struct) -------------------------------------- #[cfg(cfail1)] -fn change_constructor_path_tuple_struct() { +pub fn change_constructor_path_tuple_struct() { let _ = TupleStruct(0, 1, 2); } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -fn change_constructor_path_tuple_struct() { +#[rustc_clean(cfg="cfail2", except="HirBody,MirOptimized,MirValidated,TypeckTables")] +#[rustc_clean(cfg="cfail3")] +pub fn change_constructor_path_tuple_struct() { let _ = TupleStruct2(0, 1, 2); } // Change constructor path indirectly (tuple struct) --------------------------- -mod change_constructor_path_indirectly_tuple_struct { +pub mod change_constructor_path_indirectly_tuple_struct { #[cfg(cfail1)] use super::TupleStruct as Struct; #[cfg(not(cfail1))] use super::TupleStruct2 as Struct; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_dirty(label="HirBody", cfg="cfail2")] - #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] - fn function() -> Struct { + #[rustc_clean( + cfg="cfail2", + except="FnSignature,Hir,HirBody,MirOptimized,MirValidated,TypeckTables" + )] + #[rustc_clean(cfg="cfail3")] + pub fn function() -> Struct { Struct(0, 1, 2) } } diff --git a/src/test/incremental/hashes/struct_defs.rs b/src/test/incremental/hashes/struct_defs.rs index 17a5dc1678367..d89d779c849c3 100644 --- a/src/test/incremental/hashes/struct_defs.rs +++ b/src/test/incremental/hashes/struct_defs.rs @@ -36,9 +36,15 @@ pub struct LayoutPacked; #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] #[repr(packed)] pub struct LayoutPacked; @@ -47,9 +53,15 @@ struct LayoutC; #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] #[repr(C)] struct LayoutC; @@ -61,12 +73,18 @@ struct TupleStructFieldType(i32); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] +// Note that changing the type of a field does not change the type of the struct or enum, but +// adding/removing fields or changing a fields name or visibility does. struct TupleStructFieldType( - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] u32 ); @@ -78,14 +96,17 @@ struct TupleStructAddField(i32); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct TupleStructAddField( - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] i32, - #[rustc_metadata_clean(cfg="cfail3")] u32 ); @@ -97,9 +118,15 @@ struct TupleStructFieldVisibility(char); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct TupleStructFieldVisibility(pub char); @@ -110,12 +137,18 @@ struct RecordStructFieldType { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] +// Note that changing the type of a field does not change the type of the struct or enum, but +// adding/removing fields or changing a fields name or visibility does. struct RecordStructFieldType { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] x: u64 } @@ -127,9 +160,15 @@ struct RecordStructFieldName { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct RecordStructFieldName { y: f32 } @@ -140,14 +179,17 @@ struct RecordStructAddField { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct RecordStructAddField { - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] x: f32, - #[rustc_metadata_clean(cfg="cfail3")] y: () } @@ -158,12 +200,16 @@ struct RecordStructFieldVisibility { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct RecordStructFieldVisibility { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] pub x: f32 } @@ -175,9 +221,15 @@ struct AddLifetimeParameter<'a>(&'a f32, &'a f64); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_dirty(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddLifetimeParameter<'a, 'b>(&'a f32, &'b f64); @@ -188,15 +240,17 @@ struct AddLifetimeParameterBound<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddLifetimeParameterBound<'a, 'b: 'a>( - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] &'a f32, - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] &'b f64 ); @@ -205,15 +259,17 @@ struct AddLifetimeParameterBoundWhereClause<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddLifetimeParameterBoundWhereClause<'a, 'b>( - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] &'a f32, - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] &'b f64) where 'b: 'a; @@ -225,17 +281,19 @@ struct AddTypeParameter(T1, T1); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_dirty(label="TypeOfItem", cfg="cfail2")] +#[rustc_dirty(label="GenericsOfItem", cfg="cfail2")] +#[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddTypeParameter( // The field contains the parent's Generics, so it's dirty even though its // type hasn't changed. - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] T1, - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] T2 ); @@ -247,12 +305,16 @@ struct AddTypeParameterBound(T); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddTypeParameterBound( - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] T ); @@ -262,20 +324,35 @@ struct AddTypeParameterBoundWhereClause(T); #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct AddTypeParameterBoundWhereClause( - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] T ) where T: Sync; // Empty struct ---------------------------------------------------------------- - +// Since we cannot change anything in this case, we just make sure that the +// fingerprint is stable (i.e. that there are no random influences like memory +// addresses taken into account by the hashing algorithm). +// Note: there is no #[cfg(...)], so this is ALWAYS compiled #[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] pub struct EmptyStruct; @@ -286,14 +363,17 @@ struct Visibility; #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="TypeOfItem", cfg="cfail2")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail2")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_clean(label="TypeOfItem", cfg="cfail3")] +#[rustc_clean(label="GenericsOfItem", cfg="cfail3")] +#[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] pub struct Visibility; - - - struct ReferencedType1; struct ReferencedType2; @@ -305,12 +385,16 @@ mod tuple_struct_change_field_type_indirectly { use super::ReferencedType2 as FieldType; #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="TypeOfItem", cfg="cfail2")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail2")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="TypeOfItem", cfg="cfail3")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail3")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct TupleStruct( - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] FieldType ); } @@ -324,12 +408,16 @@ mod record_struct_change_field_type_indirectly { use super::ReferencedType2 as FieldType; #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="TypeOfItem", cfg="cfail2")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail2")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="TypeOfItem", cfg="cfail3")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail3")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct RecordStruct { - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] _x: FieldType } } @@ -348,9 +436,15 @@ mod change_trait_bound_indirectly { use super::ReferencedTrait2 as Trait; #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="TypeOfItem", cfg="cfail2")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail2")] + #[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="TypeOfItem", cfg="cfail3")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail3")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct Struct(T); } @@ -362,8 +456,14 @@ mod change_trait_bound_indirectly_in_where_clause { use super::ReferencedTrait2 as Trait; #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="TypeOfItem", cfg="cfail2")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail2")] + #[rustc_dirty(label="PredicatesOfItem", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_clean(label="TypeOfItem", cfg="cfail3")] + #[rustc_clean(label="GenericsOfItem", cfg="cfail3")] + #[rustc_clean(label="PredicatesOfItem", cfg="cfail3")] struct Struct(T) where T : Trait; } diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs index 44950ee8a601f..e09659be75599 100644 --- a/src/test/incremental/hashes/trait_defs.rs +++ b/src/test/incremental/hashes/trait_defs.rs @@ -39,7 +39,6 @@ trait TraitVisibility { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] pub trait TraitVisibility { } @@ -51,8 +50,6 @@ trait TraitUnsafety { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] unsafe trait TraitUnsafety { } @@ -65,8 +62,6 @@ trait TraitAddMethod { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] pub trait TraitAddMethod { fn method(); } @@ -82,8 +77,6 @@ trait TraitChangeMethodName { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeMethodName { fn methodChanged(); } @@ -99,13 +92,9 @@ trait TraitAddReturnType { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddReturnType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method() -> u32; } @@ -120,13 +109,9 @@ trait TraitChangeReturnType { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeReturnType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method() -> u64; } @@ -141,13 +126,9 @@ trait TraitAddParameterToMethod { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddParameterToMethod { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: u32); } @@ -163,22 +144,16 @@ trait TraitChangeMethodParameterName { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeMethodParameterName { // FIXME(#38501) This should preferably always be clean. #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(b: u32); #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn with_default(y: i32) {} } @@ -193,13 +168,9 @@ trait TraitChangeMethodParameterType { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeMethodParameterType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: i64); } @@ -214,13 +185,9 @@ trait TraitChangeMethodParameterTypeRef { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeMethodParameterTypeRef { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: &mut i32); } @@ -235,13 +202,9 @@ trait TraitChangeMethodParametersOrder { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeMethodParametersOrder { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(b: i64, a: i32); } @@ -249,20 +212,16 @@ trait TraitChangeMethodParametersOrder { // Add default implementation to method ------------------------------------------- #[cfg(cfail1)] -trait TraitAddMethodDefaultImplementation { +trait TraitAddMethodAutoImplementation { fn method(); } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] -trait TraitAddMethodDefaultImplementation { +trait TraitAddMethodAutoImplementation { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method() { } } @@ -278,8 +237,6 @@ trait TraitChangeOrderOfMethods { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeOrderOfMethods { fn method1(); fn method0(); @@ -296,13 +253,9 @@ trait TraitChangeModeSelfRefToMut { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeModeSelfRefToMut { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(&mut self); } @@ -316,15 +269,11 @@ trait TraitChangeModeSelfOwnToMut: Sized { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeModeSelfOwnToMut: Sized { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(mut self) {} } @@ -338,13 +287,9 @@ trait TraitChangeModeSelfOwnToRef { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeModeSelfOwnToRef { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(&self); } @@ -359,13 +304,9 @@ trait TraitAddUnsafeModifier { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddUnsafeModifier { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] unsafe fn method(); } @@ -380,13 +321,9 @@ trait TraitAddExternModifier { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddExternModifier { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] extern fn method(); } @@ -401,13 +338,9 @@ trait TraitChangeExternCToRustIntrinsic { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeExternCToRustIntrinsic { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] extern "rust-intrinsic" fn method(); } @@ -422,13 +355,9 @@ trait TraitAddTypeParameterToMethod { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTypeParameterToMethod { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -443,13 +372,9 @@ trait TraitAddLifetimeParameterToMethod { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeParameterToMethod { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method<'a>(); } @@ -468,13 +393,9 @@ trait TraitAddTraitBoundToMethodTypeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitBoundToMethodTypeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -489,13 +410,9 @@ trait TraitAddBuiltinBoundToMethodTypeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltinBoundToMethodTypeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -510,13 +427,9 @@ trait TraitAddLifetimeBoundToMethodLifetimeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToMethodLifetimeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method<'a, 'b: 'a>(a: &'a u32, b: &'b u32); } @@ -531,13 +444,9 @@ trait TraitAddSecondTraitBoundToMethodTypeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondTraitBoundToMethodTypeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -552,13 +461,9 @@ trait TraitAddSecondBuiltinBoundToMethodTypeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondBuiltinBoundToMethodTypeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -573,13 +478,9 @@ trait TraitAddSecondLifetimeBoundToMethodLifetimeParameter { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeBoundToMethodLifetimeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method<'a, 'b, 'c: 'a + 'b>(a: &'a u32, b: &'b u32, c: &'c u32); } @@ -591,16 +492,12 @@ trait TraitAddAssociatedType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddAssociatedType { type Associated; @@ -623,13 +520,9 @@ trait TraitAddTraitBoundToAssociatedType { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitBoundToAssociatedType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] type Associated: ReferencedTrait0; fn method(); @@ -648,13 +541,9 @@ trait TraitAddLifetimeBoundToAssociatedType<'a> { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToAssociatedType<'a> { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] type Associated: 'a; fn method(); @@ -673,13 +562,9 @@ trait TraitAddDefaultToAssociatedType { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddDefaultToAssociatedType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] type Associated = ReferenceType0; fn method(); @@ -696,8 +581,6 @@ trait TraitAddAssociatedConstant { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddAssociatedConstant { const Value: u32; @@ -717,19 +600,13 @@ trait TraitAddInitializerToAssociatedConstant { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddInitializerToAssociatedConstant { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] const Value: u32 = 1; #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -746,19 +623,13 @@ trait TraitChangeTypeOfAssociatedConstant { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeTypeOfAssociatedConstant { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] const Value: f64; #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(); } @@ -771,8 +642,6 @@ trait TraitAddSuperTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSuperTrait : ReferencedTrait0 { } @@ -784,8 +653,6 @@ trait TraitAddBuiltiBound { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltiBound : Send { } @@ -797,8 +664,6 @@ trait TraitAddStaticLifetimeBound { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddStaticLifetimeBound : 'static { } @@ -810,8 +675,6 @@ trait TraitAddTraitAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitAsSecondBound : ReferencedTrait0 + ReferencedTrait1 { } #[cfg(cfail1)] @@ -820,8 +683,6 @@ trait TraitAddTraitAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitAsSecondBoundFromBuiltin : Send + ReferencedTrait0 { } @@ -833,8 +694,6 @@ trait TraitAddBuiltinBoundAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltinBoundAsSecondBound : ReferencedTrait0 + Send { } #[cfg(cfail1)] @@ -843,8 +702,6 @@ trait TraitAddBuiltinBoundAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltinBoundAsSecondBoundFromBuiltin: Send + Copy { } @@ -856,8 +713,6 @@ trait TraitAddStaticBoundAsSecondBound : ReferencedTrait0 { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddStaticBoundAsSecondBound : ReferencedTrait0 + 'static { } #[cfg(cfail1)] @@ -866,8 +721,6 @@ trait TraitAddStaticBoundAsSecondBoundFromBuiltin : Send { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddStaticBoundAsSecondBoundFromBuiltin : Send + 'static { } @@ -879,8 +732,6 @@ trait TraitAddTypeParameterToTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTypeParameterToTrait { } @@ -892,8 +743,6 @@ trait TraitAddLifetimeParameterToTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeParameterToTrait<'a> { } @@ -905,8 +754,6 @@ trait TraitAddTraitBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitBoundToTypeParameterOfTrait { } @@ -918,8 +765,6 @@ trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToTypeParameterOfTrait<'a, T: 'a> { } @@ -931,8 +776,6 @@ trait TraitAddLifetimeBoundToLifetimeParameterOfTrait<'a, 'b> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToLifetimeParameterOfTrait<'a: 'b, 'b> { } @@ -944,8 +787,6 @@ trait TraitAddBuiltinBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltinBoundToTypeParameterOfTrait { } @@ -957,8 +798,6 @@ trait TraitAddSecondTypeParameterToTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondTypeParameterToTrait { } @@ -970,8 +809,6 @@ trait TraitAddSecondLifetimeParameterToTrait<'a> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeParameterToTrait<'a, 'b> { } @@ -983,8 +820,6 @@ trait TraitAddSecondTraitBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondTraitBoundToTypeParameterOfTrait { } @@ -996,8 +831,6 @@ trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeBoundToTypeParameterOfTrait<'a, 'b, T: 'a + 'b> { } @@ -1009,8 +842,6 @@ trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTrait<'a: 'b, 'b, 'c> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTrait<'a: 'b + 'c, 'b, 'c> { } @@ -1022,8 +853,6 @@ trait TraitAddSecondBuiltinBoundToTypeParameterOfTrait { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondBuiltinBoundToTypeParameterOfTrait { } @@ -1041,8 +870,6 @@ trait TraitAddTraitBoundToTypeParameterOfTraitWhere { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 { } @@ -1054,8 +881,6 @@ trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToTypeParameterOfTraitWhere<'a, T> where T: 'a { } @@ -1067,8 +892,6 @@ trait TraitAddLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b> { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b> where 'a: 'b { } @@ -1080,8 +903,6 @@ trait TraitAddBuiltinBoundToTypeParameterOfTraitWhere { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddBuiltinBoundToTypeParameterOfTraitWhere where T: Send { } @@ -1093,8 +914,6 @@ trait TraitAddSecondTraitBoundToTypeParameterOfTraitWhere where T: Referenced #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondTraitBoundToTypeParameterOfTraitWhere where T: ReferencedTrait0 + ReferencedTrait1 { } @@ -1107,8 +926,6 @@ trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeBoundToTypeParameterOfTraitWhere<'a, 'b, T> where T: 'a + 'b { } @@ -1120,8 +937,6 @@ trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b, 'c> whe #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondLifetimeBoundToLifetimeParameterOfTraitWhere<'a, 'b, 'c> where 'a: 'b + 'c { } @@ -1133,8 +948,6 @@ trait TraitAddSecondBuiltinBoundToTypeParameterOfTraitWhere where T: Send { } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] trait TraitAddSecondBuiltinBoundToTypeParameterOfTraitWhere where T: Send + Sync { } @@ -1147,13 +960,9 @@ mod change_return_type_of_method_indirectly_use { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeReturnType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method() -> ReturnType; } } @@ -1169,13 +978,9 @@ mod change_method_parameter_type_indirectly_by_use { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeArgType { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: ArgType); } } @@ -1191,13 +996,9 @@ mod change_method_parameter_type_bound_indirectly_by_use { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeBoundOfMethodTypeParameter { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: T); } } @@ -1214,13 +1015,9 @@ mod change_method_parameter_type_bound_indirectly_by_use_where { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeBoundOfMethodTypeParameterWhere { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method(a: T) where T: Bound; } } @@ -1236,8 +1033,6 @@ mod change_method_type_parameter_bound_indirectly { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeTraitBound { fn method(a: T); } @@ -1255,8 +1050,6 @@ mod change_method_type_parameter_bound_indirectly_where { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] trait TraitChangeTraitBoundWhere where T: Bound { fn method(a: T); } diff --git a/src/test/incremental/hashes/trait_impls.rs b/src/test/incremental/hashes/trait_impls.rs index 06c8eb6a878f0..eb31175b6f257 100644 --- a/src/test/incremental/hashes/trait_impls.rs +++ b/src/test/incremental/hashes/trait_impls.rs @@ -43,22 +43,16 @@ impl ChangeMethodNameTrait for Foo { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] pub trait ChangeMethodNameTrait { #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name2(); } #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeMethodNameTrait for Foo { #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name2() { } } @@ -78,15 +72,11 @@ impl ChangeMethodBodyTrait for Foo { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeMethodBodyTrait for Foo { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name() { () } @@ -109,15 +99,11 @@ impl ChangeMethodBodyTraitInlined for Foo { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeMethodBodyTraitInlined for Foo { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] #[inline] fn method_name() { panic!() @@ -144,13 +130,9 @@ pub trait ChangeMethodSelfnessTrait { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeMethodSelfnessTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name(&self) { () } @@ -176,13 +158,9 @@ pub trait RemoveMethodSelfnessTrait { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl RemoveMethodSelfnessTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name() {} } @@ -206,13 +184,9 @@ pub trait ChangeMethodSelfmutnessTrait { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeMethodSelfmutnessTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name(&mut self) {} } @@ -236,8 +210,6 @@ pub trait ChangeItemKindTrait { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeItemKindTrait for Foo { type name = (); } @@ -264,8 +236,6 @@ pub trait RemoveItemTrait { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl RemoveItemTrait for Foo { type TypeName = (); } @@ -291,8 +261,6 @@ pub trait AddItemTrait { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddItemTrait for Foo { type TypeName = (); fn method_name() { } @@ -313,21 +281,15 @@ impl ChangeHasValueTrait for Foo { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] pub trait ChangeHasValueTrait { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name() { } } #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeHasValueTrait for Foo { fn method_name() { } } @@ -346,13 +308,9 @@ impl AddDefaultTrait for Foo { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddDefaultTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] default fn method_name() { } } @@ -376,13 +334,9 @@ pub trait AddArgumentTrait { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddArgumentTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name(&self, _x: u32) { } } @@ -406,13 +360,9 @@ pub trait ChangeArgumentTypeTrait { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeArgumentTypeTrait for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn method_name(&self, _x: char) { } } @@ -433,13 +383,9 @@ impl AddTypeParameterToImpl for Bar { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddTypeParameterToImpl for Bar { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn id(t: T) -> T { t } } @@ -458,13 +404,9 @@ impl ChangeSelfTypeOfImpl for u32 { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl ChangeSelfTypeOfImpl for u64 { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn id(self) -> Self { self } } @@ -483,13 +425,9 @@ impl AddLifetimeBoundToImplParameter for T { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddLifetimeBoundToImplParameter for T { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn id(self) -> Self { self } } @@ -508,13 +446,9 @@ impl AddTraitBoundToImplParameter for T { #[cfg(not(cfail1))] #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_dirty(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddTraitBoundToImplParameter for T { #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_clean(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] fn id(self) -> Self { self } } @@ -533,13 +467,9 @@ impl AddNoMangleToMethod for Foo { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl AddNoMangleToMethod for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] #[no_mangle] fn add_no_mangle_to_method(&self) { } } @@ -558,13 +488,9 @@ impl MakeMethodInline for Foo { #[cfg(not(cfail1))] #[rustc_clean(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] impl MakeMethodInline for Foo { #[rustc_dirty(label="Hir", cfg="cfail2")] #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] #[inline] fn make_method_inline(&self) -> u8 { 0 } } diff --git a/src/test/incremental/hashes/type_defs.rs b/src/test/incremental/hashes/type_defs.rs index 35fb583cd4ed2..59346f5fdb23e 100644 --- a/src/test/incremental/hashes/type_defs.rs +++ b/src/test/incremental/hashes/type_defs.rs @@ -35,9 +35,8 @@ type ChangePrimitiveType = i32; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangePrimitiveType = i64; @@ -47,9 +46,8 @@ type ChangePrimitiveType = i64; type ChangeMutability = &'static i32; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeMutability = &'static mut i32; @@ -59,9 +57,8 @@ type ChangeMutability = &'static mut i32; type ChangeLifetime<'a> = (&'static i32, &'a i32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeLifetime<'a> = (&'a i32, &'a i32); @@ -74,9 +71,8 @@ struct Struct2; type ChangeTypeStruct = Struct1; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeTypeStruct = Struct2; @@ -86,9 +82,8 @@ type ChangeTypeStruct = Struct2; type ChangeTypeTuple = (u32, u64); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeTypeTuple = (u32, i64); @@ -107,9 +102,8 @@ enum Enum2 { type ChangeTypeEnum = Enum1; #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeTypeEnum = Enum2; @@ -119,9 +113,8 @@ type ChangeTypeEnum = Enum2; type AddTupleField = (i32, i64); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddTupleField = (i32, i64, i16); @@ -131,9 +124,8 @@ type AddTupleField = (i32, i64, i16); type ChangeNestedTupleField = (i32, (i64, i16)); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type ChangeNestedTupleField = (i32, (i64, i8)); @@ -143,9 +135,8 @@ type ChangeNestedTupleField = (i32, (i64, i8)); type AddTypeParam = (T1, T1); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddTypeParam = (T1, T2); @@ -155,9 +146,8 @@ type AddTypeParam = (T1, T2); type AddTypeParamBound = (T1, u32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddTypeParamBound = (T1, u32); @@ -167,9 +157,8 @@ type AddTypeParamBound = (T1, u32); type AddTypeParamBoundWhereClause where T1: Clone = (T1, u32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddTypeParamBoundWhereClause where T1: Clone+Copy = (T1, u32); @@ -179,9 +168,8 @@ type AddTypeParamBoundWhereClause where T1: Clone+Copy = (T1, u32); type AddLifetimeParam<'a> = (&'a u32, &'a u32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddLifetimeParam<'a, 'b> = (&'a u32, &'b u32); @@ -191,9 +179,8 @@ type AddLifetimeParam<'a, 'b> = (&'a u32, &'b u32); type AddLifetimeParamBound<'a, 'b> = (&'a u32, &'b u32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddLifetimeParamBound<'a, 'b: 'a> = (&'a u32, &'b u32); @@ -205,9 +192,8 @@ where 'b: 'a = (&'a u32, &'b u32, &'c u32); #[cfg(not(cfail1))] -#[rustc_dirty(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(cfg="cfail2", except="Hir,HirBody")] +#[rustc_clean(cfg="cfail3")] type AddLifetimeParamBoundWhereClause<'a, 'b, 'c> where 'b: 'a, 'c: 'a @@ -225,10 +211,8 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly = (T, u32); } @@ -241,9 +225,7 @@ mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_dirty(label="Hir", cfg="cfail2")] - #[rustc_clean(label="Hir", cfg="cfail3")] - #[rustc_metadata_dirty(cfg="cfail2")] - #[rustc_metadata_clean(cfg="cfail3")] + #[rustc_clean(cfg="cfail2", except="Hir,HirBody")] + #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly where T : Trait = (T, u32); } diff --git a/src/test/incremental/hashes/unary_and_binary_exprs.rs b/src/test/incremental/hashes/unary_and_binary_exprs.rs index 05b0dec4e7e81..ec4ae62b12b13 100644 --- a/src/test/incremental/hashes/unary_and_binary_exprs.rs +++ b/src/test/incremental/hashes/unary_and_binary_exprs.rs @@ -32,12 +32,8 @@ pub fn const_negation() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn const_negation() -> i32 { -1 } @@ -51,12 +47,8 @@ pub fn const_bitwise_not() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn const_bitwise_not() -> i32 { !99 } @@ -70,12 +62,8 @@ pub fn var_negation(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn var_negation(x: i32, y: i32) -> i32 { -y } @@ -89,12 +77,8 @@ pub fn var_bitwise_not(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn var_bitwise_not(x: i32, y: i32) -> i32 { !y } @@ -108,12 +92,8 @@ pub fn var_deref(x: &i32, y: &i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated,TypeckTables", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn var_deref(x: &i32, y: &i32) -> i32 { *y } @@ -127,12 +107,8 @@ pub fn first_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn first_const_add() -> i32 { 2 + 3 } @@ -146,12 +122,8 @@ pub fn second_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn second_const_add() -> i32 { 1 + 3 } @@ -165,12 +137,8 @@ pub fn first_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn first_var_add(a: i32, b: i32) -> i32 { b + 2 } @@ -184,12 +152,8 @@ pub fn second_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn second_var_add(a: i32, b: i32) -> i32 { 1 + b } @@ -203,12 +167,8 @@ pub fn plus_to_minus(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn plus_to_minus(a: i32) -> i32 { 1 - a } @@ -222,12 +182,8 @@ pub fn plus_to_mult(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn plus_to_mult(a: i32) -> i32 { 1 * a } @@ -241,12 +197,8 @@ pub fn plus_to_div(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn plus_to_div(a: i32) -> i32 { 1 / a } @@ -260,12 +212,8 @@ pub fn plus_to_mod(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn plus_to_mod(a: i32) -> i32 { 1 % a } @@ -279,12 +227,8 @@ pub fn and_to_or(a: bool, b: bool) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn and_to_or(a: bool, b: bool) -> bool { a || b } @@ -298,12 +242,8 @@ pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { 1 | a } @@ -317,12 +257,8 @@ pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { 1 ^ a } @@ -336,12 +272,8 @@ pub fn bitwise_and_to_lshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_lshift(a: i32) -> i32 { a << 1 } @@ -355,12 +287,8 @@ pub fn bitwise_and_to_rshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_rshift(a: i32) -> i32 { a >> 1 } @@ -374,12 +302,8 @@ pub fn eq_to_uneq(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn eq_to_uneq(a: i32) -> bool { a != 1 } @@ -393,12 +317,8 @@ pub fn eq_to_lt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn eq_to_lt(a: i32) -> bool { a < 1 } @@ -412,12 +332,8 @@ pub fn eq_to_gt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn eq_to_gt(a: i32) -> bool { a > 1 } @@ -431,12 +347,8 @@ pub fn eq_to_le(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn eq_to_le(a: i32) -> bool { a <= 1 } @@ -450,12 +362,8 @@ pub fn eq_to_ge(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn eq_to_ge(a: i32) -> bool { a >= 1 } @@ -471,12 +379,8 @@ pub fn type_cast(a: u8) -> u64 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated,TypeckTables", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn type_cast(a: u8) -> u64 { let b = a as u32; let c = b as u64; @@ -492,12 +396,8 @@ pub fn value_cast(a: u32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn value_cast(a: u32) -> i32 { 2 as i32 } @@ -514,12 +414,8 @@ pub fn lvalue() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn lvalue() -> i32 { let mut x = 10; let mut y = 11; @@ -538,12 +434,8 @@ pub fn rvalue() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn rvalue() -> i32 { let mut x = 10; x = 8; @@ -559,12 +451,8 @@ pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { } #[cfg(not(cfail1))] -#[rustc_clean(label="Hir", cfg="cfail2")] -#[rustc_clean(label="Hir", cfg="cfail3")] -#[rustc_dirty(label="HirBody", cfg="cfail2")] -#[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] +#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] +#[rustc_clean(cfg="cfail3")] pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { s[j] } diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index f4fd7e709b4b1..eae5aea651075 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -40,8 +40,6 @@ fn change_loop_body() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_body() { let mut _x = 0; while let Some(0u32) = None { @@ -67,8 +65,6 @@ fn change_loop_condition() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_condition() { let mut _x = 0; while let Some(1u32) = None { @@ -93,8 +89,6 @@ fn add_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_break() { let mut _x = 0; while let Some(0u32) = None { @@ -120,8 +114,6 @@ fn add_loop_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label() { let mut _x = 0; 'label: while let Some(0u32) = None { @@ -147,8 +139,6 @@ fn add_loop_label_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_break() { let mut _x = 0; 'label: while let Some(0u32) = None { @@ -176,8 +166,6 @@ fn change_break_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_break_label() { let mut _x = 0; 'outer: while let Some(0u32) = None { @@ -205,8 +193,6 @@ fn add_loop_label_to_continue() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_continue() { let mut _x = 0; 'label: while let Some(0u32) = None { @@ -234,8 +220,6 @@ fn change_continue_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_label() { let mut _x = 0; 'outer: while let Some(0u32) = None { @@ -263,8 +247,6 @@ fn change_continue_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_to_break() { let mut _x = 0; while let Some(0u32) = None { diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index aa70d7e9fc112..6b1898e401b24 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -40,8 +40,6 @@ fn change_loop_body() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_body() { let mut _x = 0; while true { @@ -67,8 +65,6 @@ fn change_loop_condition() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_loop_condition() { let mut _x = 0; while false { @@ -93,8 +89,6 @@ fn add_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_break() { let mut _x = 0; while true { @@ -120,8 +114,6 @@ fn add_loop_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label() { let mut _x = 0; 'label: while true { @@ -147,8 +139,6 @@ fn add_loop_label_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_break() { let mut _x = 0; 'label: while true { @@ -176,8 +166,6 @@ fn change_break_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_break_label() { let mut _x = 0; 'outer: while true { @@ -205,8 +193,6 @@ fn add_loop_label_to_continue() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn add_loop_label_to_continue() { let mut _x = 0; 'label: while true { @@ -234,8 +220,6 @@ fn change_continue_label() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_label() { let mut _x = 0; 'outer: while true { @@ -263,8 +247,6 @@ fn change_continue_to_break() { #[rustc_clean(label="Hir", cfg="cfail3")] #[rustc_dirty(label="HirBody", cfg="cfail2")] #[rustc_clean(label="HirBody", cfg="cfail3")] -#[rustc_metadata_clean(cfg="cfail2")] -#[rustc_metadata_clean(cfg="cfail3")] fn change_continue_to_break() { let mut _x = 0; while true { diff --git a/src/test/incremental/ich_nested_items.rs b/src/test/incremental/ich_nested_items.rs index e8e40d57b1ee6..2e0f0ba083783 100644 --- a/src/test/incremental/ich_nested_items.rs +++ b/src/test/incremental/ich_nested_items.rs @@ -11,29 +11,29 @@ // Check that the hash of `foo` doesn't change just because we ordered // the nested items (or even added new ones). -// revisions: rpass1 rpass2 +// revisions: cfail1 cfail2 +// must-compile-successfully +#![crate_type = "rlib"] #![feature(rustc_attrs)] -#[cfg(rpass1)] -fn foo() { - fn bar() { } - fn baz() { } +#[cfg(cfail1)] +pub fn foo() { + pub fn bar() { } + pub fn baz() { } } -#[cfg(rpass2)] -#[rustc_clean(label="Hir", cfg="rpass2")] -#[rustc_clean(label="HirBody", cfg="rpass2")] -fn foo() { - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] - fn baz() { } // order is different... +#[cfg(cfail2)] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +pub fn foo() { + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail2")] + pub fn baz() { } // order is different... - #[rustc_clean(label="Hir", cfg="rpass2")] - #[rustc_clean(label="HirBody", cfg="rpass2")] - fn bar() { } // but that doesn't matter. + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail2")] + pub fn bar() { } // but that doesn't matter. - fn bap() { } // neither does adding a new item + pub fn bap() { } // neither does adding a new item } - -fn main() { } diff --git a/src/test/incremental/issue-35593.rs b/src/test/incremental/issue-35593.rs index 51e04dd7b2ce0..52a601ac1e87b 100644 --- a/src/test/incremental/issue-35593.rs +++ b/src/test/incremental/issue-35593.rs @@ -12,6 +12,7 @@ // equal example. // revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![rustc_partition_reused(module="issue_35593", cfg="rpass2")] diff --git a/src/test/incremental/issue-38222.rs b/src/test/incremental/issue-38222.rs index d14b1cfd6c9ac..7bb8af74eeb7e 100644 --- a/src/test/incremental/issue-38222.rs +++ b/src/test/incremental/issue-38222.rs @@ -12,6 +12,8 @@ // dep-node. // revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph + #![feature(rustc_attrs)] @@ -31,10 +33,9 @@ pub fn main() { mod mod1 { pub fn some_fn() { - let _ = 1; - } + #[cfg(rpass2)] + {} - #[cfg(rpass2)] - fn _some_other_fn() { + let _ = 1; } } diff --git a/src/test/incremental/krate-inherent.rs b/src/test/incremental/krate-inherent.rs index ac6cc3e9826f1..bc3e3a78fd6b8 100644 --- a/src/test/incremental/krate-inherent.rs +++ b/src/test/incremental/krate-inherent.rs @@ -8,27 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// revisions: rpass1 rpass2 +// revisions: cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully #![allow(warnings)] #![feature(rustc_attrs)] -#![rustc_partition_reused(module="krate_inherent-x", cfg="rpass2")] +#![rustc_partition_reused(module="krate_inherent-x", cfg="cfail2")] +#![crate_type = "rlib"] -fn main() { } - -mod x { - struct Foo; +pub mod x { + pub struct Foo; impl Foo { - fn foo(&self) { } + pub fn foo(&self) { } } - fn method() { + pub fn method() { let x: Foo = Foo; x.foo(); // inherent methods used to add an edge from Krate } } -#[cfg(rpass1)] -fn bar() { } // remove this unrelated fn in rpass2, which should not affect `x::method` +#[cfg(cfail1)] +pub fn bar() { } // remove this unrelated fn in cfail2, which should not affect `x::method` diff --git a/src/test/incremental/krate-inlined.rs b/src/test/incremental/krate-inlined.rs index 043cb761da093..83b75116c6086 100644 --- a/src/test/incremental/krate-inlined.rs +++ b/src/test/incremental/krate-inlined.rs @@ -20,12 +20,14 @@ #![rustc_partition_reused(module="krate_inlined-x", cfg="rpass2")] fn main() { + x::method(); + #[cfg(rpass2)] () } mod x { - fn method() { + pub fn method() { // use some methods that require inlining HIR from another crate: let mut v = vec![]; v.push(1); diff --git a/src/test/compile-fail/incr_comp_with_macro_export.rs b/src/test/incremental/macro_export.rs similarity index 90% rename from src/test/compile-fail/incr_comp_with_macro_export.rs rename to src/test/incremental/macro_export.rs index eafef17230367..914632e96ba31 100644 --- a/src/test/compile-fail/incr_comp_with_macro_export.rs +++ b/src/test/incremental/macro_export.rs @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Zincremental=tmp/cfail-tests/incr_comp_with_macro_export +// revisions: cfail1 cfail2 cfail3 // must-compile-successfully - // This test case makes sure that we can compile with incremental compilation // enabled when there are macros exported from this crate. (See #37756) diff --git a/src/test/incremental/remapped_paths_cc/main.rs b/src/test/incremental/remapped_paths_cc/main.rs index 58fb8bc3c889f..ce7f5792cea9e 100644 --- a/src/test/incremental/remapped_paths_cc/main.rs +++ b/src/test/incremental/remapped_paths_cc/main.rs @@ -9,10 +9,8 @@ // except according to those terms. // revisions:rpass1 rpass2 rpass3 -// compile-flags: -Z query-dep-graph -g -Zincremental-cc +// compile-flags: -Z query-dep-graph -g // aux-build:extern_crate.rs -// ignore-test FIXME(#42293) this regressed in #44142 but should get fixed with red/green - // This test case makes sure that we detect if paths emitted into debuginfo // are changed, even when the change happens in an external crate. diff --git a/src/test/incremental/remove-private-item-cross-crate/main.rs b/src/test/incremental/remove-private-item-cross-crate/main.rs index 24fa1502b9224..d94cb403da8a5 100644 --- a/src/test/incremental/remove-private-item-cross-crate/main.rs +++ b/src/test/incremental/remove-private-item-cross-crate/main.rs @@ -17,8 +17,7 @@ #![feature(rustc_attrs)] #![crate_type = "bin"] -// FIXME(#42293) this regressed in #44142 but should get fixed with red/green -// #![rustc_partition_reused(module="main", cfg="rpass2")] +#![rustc_partition_reused(module="main", cfg="rpass2")] extern crate a; diff --git a/src/test/incremental/remove_crate/auxiliary/extern_crate.rs b/src/test/incremental/remove_crate/auxiliary/extern_crate.rs new file mode 100644 index 0000000000000..39543cd829d8a --- /dev/null +++ b/src/test/incremental/remove_crate/auxiliary/extern_crate.rs @@ -0,0 +1,13 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn foo(_: u8) { + +} diff --git a/src/test/incremental/remove_crate/main.rs b/src/test/incremental/remove_crate/main.rs new file mode 100644 index 0000000000000..fafcb8bb0c83a --- /dev/null +++ b/src/test/incremental/remove_crate/main.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that removing an upstream crate does not cause any trouble. + +// revisions:rpass1 rpass2 +// aux-build:extern_crate.rs + +#[cfg(rpass1)] +extern crate extern_crate; + +pub fn main() { + #[cfg(rpass1)] + { + extern_crate::foo(1); + } + + #[cfg(rpass2)] + { + foo(1); + } +} + +#[cfg(rpass2)] +pub fn foo(_: u8) { + +} diff --git a/src/test/incremental/remove_source_file/main.rs b/src/test/incremental/remove_source_file/main.rs index 4ba33f3bb3d62..3ae26c6aa4517 100644 --- a/src/test/incremental/remove_source_file/main.rs +++ b/src/test/incremental/remove_source_file/main.rs @@ -11,21 +11,24 @@ // This test case makes sure that the compiler doesn't crash due to a failing // table lookup when a source file is removed. -// revisions:rpass1 rpass2 +// revisions:cfail1 cfail2 // Note that we specify -g so that the FileMaps actually get referenced by the // incr. comp. cache: // compile-flags: -Z query-dep-graph -g +// must-compile-successfully -#[cfg(rpass1)] +#![crate_type= "rlib"] + +#[cfg(cfail1)] mod auxiliary; -#[cfg(rpass1)] -fn main() { +#[cfg(cfail1)] +pub fn foo() { auxiliary::print_hello(); } -#[cfg(rpass2)] -fn main() { +#[cfg(cfail2)] +pub fn foo() { println!("hello"); } diff --git a/src/test/incremental/rlib_cross_crate/b.rs b/src/test/incremental/rlib_cross_crate/b.rs index 39065d9671ace..9849e93d3ff9e 100644 --- a/src/test/incremental/rlib_cross_crate/b.rs +++ b/src/test/incremental/rlib_cross_crate/b.rs @@ -18,8 +18,6 @@ // no-prefer-dynamic // compile-flags: -Z query-dep-graph -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - #![feature(rustc_attrs)] extern crate a; diff --git a/src/test/incremental/spans_in_type_debuginfo.rs b/src/test/incremental/spans_in_type_debuginfo.rs index 7d8e6c9d9d7ef..e1369d92c5ccb 100644 --- a/src/test/incremental/spans_in_type_debuginfo.rs +++ b/src/test/incremental/spans_in_type_debuginfo.rs @@ -14,7 +14,6 @@ // revisions:rpass1 rpass2 // compile-flags: -Z query-dep-graph -g -#![rustc_partition_reused(module="spans_in_type_debuginfo", cfg="rpass2")] #![rustc_partition_reused(module="spans_in_type_debuginfo-structs", cfg="rpass2")] #![rustc_partition_reused(module="spans_in_type_debuginfo-enums", cfg="rpass2")] diff --git a/src/test/incremental/spans_significant_w_panic.rs b/src/test/incremental/spans_significant_w_panic.rs new file mode 100644 index 0000000000000..c0bf35e781c70 --- /dev/null +++ b/src/test/incremental/spans_significant_w_panic.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test makes sure that just changing a definition's location in the +// source file also changes its incr. comp. hash, if debuginfo is enabled. + +// revisions:rpass1 rpass2 + +// compile-flags: -C overflow-checks=on + +#![feature(rustc_attrs)] + +#[cfg(rpass1)] +pub fn main() { + let _ = 0u8 + 1; +} + +#[cfg(rpass2)] +#[rustc_clean(label="Hir", cfg="rpass2")] +#[rustc_dirty(label="HirBody", cfg="rpass2")] +pub fn main() { + let _ = 0u8 + 1; +} diff --git a/src/test/incremental/spike.rs b/src/test/incremental/spike.rs index 257699cd3fce1..a820471b7d55b 100644 --- a/src/test/incremental/spike.rs +++ b/src/test/incremental/spike.rs @@ -13,6 +13,7 @@ // `y` module entirely (but not the `x` module). // revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs index 760975b292f95..3e75fa985acb4 100644 --- a/src/test/incremental/string_constant.rs +++ b/src/test/incremental/string_constant.rs @@ -8,47 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// revisions: rpass1 rpass2 +// revisions: cfail1 cfail2 // compile-flags: -Z query-dep-graph +// must-compile-successfully #![allow(warnings)] #![feature(rustc_attrs)] +#![crate_type = "rlib"] // Here the only thing which changes is the string constant in `x`. // Therefore, the compiler deduces (correctly) that typeck is not // needed even for callers of `x`. -fn main() { } -mod x { - #[cfg(rpass1)] +pub mod x { + #[cfg(cfail1)] pub fn x() { println!("{}", "1"); } - #[cfg(rpass2)] - #[rustc_dirty(label="HirBody", cfg="rpass2")] - #[rustc_dirty(label="MirOptimized", cfg="rpass2")] + #[cfg(cfail2)] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_dirty(label="MirOptimized", cfg="cfail2")] pub fn x() { println!("{}", "2"); } } -mod y { +pub mod y { use x; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] - #[rustc_clean(label="MirOptimized", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] + #[rustc_clean(label="MirOptimized", cfg="cfail2")] pub fn y() { x::x(); } } -mod z { +pub mod z { use y; - #[rustc_clean(label="TypeckTables", cfg="rpass2")] - #[rustc_clean(label="MirOptimized", cfg="rpass2")] + #[rustc_clean(label="TypeckTables", cfg="cfail2")] + #[rustc_clean(label="MirOptimized", cfg="cfail2")] pub fn z() { y::y(); } diff --git a/src/test/incremental/struct_change_field_type_cross_crate/b.rs b/src/test/incremental/struct_change_field_type_cross_crate/b.rs index e5ec9784847f0..9660f47da35c1 100644 --- a/src/test/incremental/struct_change_field_type_cross_crate/b.rs +++ b/src/test/incremental/struct_change_field_type_cross_crate/b.rs @@ -12,8 +12,6 @@ // revisions:rpass1 rpass2 // compile-flags: -Z query-dep-graph -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - #![feature(rustc_attrs)] extern crate a; diff --git a/src/test/incremental/type_alias_cross_crate/b.rs b/src/test/incremental/type_alias_cross_crate/b.rs index 63e1437f0687b..ee35a4d9b9c6e 100644 --- a/src/test/incremental/type_alias_cross_crate/b.rs +++ b/src/test/incremental/type_alias_cross_crate/b.rs @@ -12,8 +12,6 @@ // revisions:rpass1 rpass2 rpass3 // compile-flags: -Z query-dep-graph -// ignore-test -- ignored until red/green restores cross-crate tracking fidelity - #![feature(rustc_attrs)] extern crate a; diff --git a/src/test/incremental/unchecked_dirty_clean_metadata.rs b/src/test/incremental/unchecked_dirty_clean_metadata.rs deleted file mode 100644 index 917c2c9dbce4f..0000000000000 --- a/src/test/incremental/unchecked_dirty_clean_metadata.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// revisions: rpass1 cfail2 -// compile-flags: -Z query-dep-graph - -#![allow(warnings)] -#![feature(rustc_attrs)] - -// Sanity check for the dirty-clean system. We add -// #[rustc_metadata_dirty]/#[rustc_metadata_clean] attributes in places that -// are not checked and make sure that this causes an error. - -fn main() { - - #[rustc_metadata_dirty(cfg="cfail2")] - //[cfail2]~^ ERROR found unchecked #[rustc_dirty]/#[rustc_clean] attribute - { - // empty block - } - - #[rustc_metadata_clean(cfg="cfail2")] - //[cfail2]~^ ERROR found unchecked #[rustc_dirty]/#[rustc_clean] attribute - { - // empty block - } -} - diff --git a/src/test/incremental/warnings-reemitted.rs b/src/test/incremental/warnings-reemitted.rs new file mode 100644 index 0000000000000..bf66ac7829c2e --- /dev/null +++ b/src/test/incremental/warnings-reemitted.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Coverflow-checks=on +// must-compile-successfully + +#![allow(warnings)] + +fn main() { + 255u8 + 1; //~ WARNING this expression will panic at run-time +} diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md index d999ff9755160..b00b35aa29ff4 100644 --- a/src/test/mir-opt/README.md +++ b/src/test/mir-opt/README.md @@ -7,13 +7,13 @@ The test format is: // END RUST SOURCE // START $file_name_of_some_mir_dump_0 // $expected_line_0 -// ... +// (lines or elision) // $expected_line_N // END $file_name_of_some_mir_dump_0 -// ... +// (lines or elision) // START $file_name_of_some_mir_dump_N // $expected_line_0 -// ... +// (lines or elision) // $expected_line_N // END $file_name_of_some_mir_dump_N ``` @@ -22,10 +22,15 @@ All the test information is in comments so the test is runnable. For each $file_name, compiletest expects [$expected_line_0, ..., $expected_line_N] to appear in the dumped MIR in order. Currently it allows -other non-matched lines before, after and in-between. Note that this includes -lines that end basic blocks or begin new ones; it is good practice -in your tests to include the terminator for each of your basic blocks as an -internal sanity check guarding against a test like: +other non-matched lines before and after, but not between $expected_lines, +should you want to skip lines, you must include an elision comment, of the form +(as a regex) `//\s*...\s*`. The lines will be skipped lazily, that is, if there +are two identical lines in the output that match the line after the elision +comment, the first one wil be matched. + +Examples: + +The following blocks will not match the one after it. ``` bb0: { @@ -35,8 +40,6 @@ bb0: { } ``` -that will inadvertantly pattern-matching against: - ``` bb0: { StorageLive(_1); @@ -49,6 +52,18 @@ bb1: { } ``` +But this will match the one above, + +``` +bb0: { + StorageLive(_1); + _1 = const true; + ... + StorageDead(_1); + ... +} +``` + Lines match ignoring whitespace, and the prefix "//" is removed. It also currently strips trailing comments -- partly because the full file path diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index d3bf7f68785d5..321c05c490356 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -36,30 +36,30 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-initial.after.mir +// START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // StorageLive(_1); // _1 = const false; // StorageLive(_2); // StorageLive(_3); // _3 = _1; -// _2 = _3; +// _2 = move _3; // StorageDead(_3); // StorageLive(_4); // _4 = std::option::Option>::None; // StorageLive(_5); // StorageLive(_6); -// _6 = _4; -// replace(_5 <- _6) -> [return: bb1, unwind: bb5]; +// _6 = move _4; +// replace(_5 <-move _6) -> [return: bb2, unwind: bb5]; // } // bb1: { -// drop(_6) -> [return: bb6, unwind: bb4]; +// resume; // } // bb2: { -// resume; +// drop(_6) -> [return: bb6, unwind: bb4]; // } // bb3: { -// drop(_4) -> bb2; +// drop(_4) -> bb1; // } // bb4: { // drop(_5) -> bb3; @@ -74,7 +74,7 @@ fn main() { // } // bb7: { // StorageDead(_5); -// drop(_4) -> bb8; +// drop(_4) -> [return: bb8, unwind: bb1]; // } // bb8: { // StorageDead(_4); @@ -82,4 +82,4 @@ fn main() { // StorageDead(_1); // return; // } -// END rustc.node4.SimplifyCfg-initial.after.mir +// END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index 4015930ef7633..ed9c303a16fd4 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + #![feature(box_syntax)] fn main() { @@ -28,9 +30,12 @@ impl Drop for S { } // END RUST SOURCE -// START rustc.node4.ElaborateDrops.before.mir +// START rustc.main.ElaborateDrops.before.mir // let mut _0: (); -// let _1: std::boxed::Box; +// scope 1 { +// let _1: std::boxed::Box; +// } +// ... // let mut _2: std::boxed::Box; // let mut _3: (); // let mut _4: std::boxed::Box; @@ -39,27 +44,27 @@ impl Drop for S { // StorageLive(_1); // StorageLive(_2); // _2 = Box(S); -// (*_2) = const S::new() -> [return: bb1, unwind: bb3]; +// (*_2) = const S::new() -> [return: bb2, unwind: bb3]; // } // // bb1: { -// _1 = _2; -// drop(_2) -> bb4; +// resume; // } // // bb2: { -// resume; +// _1 = move _2; +// drop(_2) -> bb4; // } // // bb3: { -// drop(_2) -> bb2; +// drop(_2) -> bb1; // } // // bb4: { // StorageDead(_2); // StorageLive(_4); -// _4 = _1; -// _3 = const std::mem::drop(_4) -> [return: bb5, unwind: bb7]; +// _4 = move _1; +// _3 = const std::mem::drop(move _4) -> [return: bb5, unwind: bb7]; // } // // bb5: { @@ -67,7 +72,7 @@ impl Drop for S { // } // // bb6: { -// drop(_1) -> bb2; +// drop(_1) -> bb1; // } // // bb7: { @@ -85,4 +90,4 @@ impl Drop for S { // return; // } // } -// END rustc.node4.ElaborateDrops.before.mir +// END rustc.main.ElaborateDrops.before.mir diff --git a/src/test/mir-opt/combine_array_len.rs b/src/test/mir-opt/combine_array_len.rs new file mode 100644 index 0000000000000..136c3493fa407 --- /dev/null +++ b/src/test/mir-opt/combine_array_len.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn norm2(x: [f32; 2]) -> f32 { + let a = x[0]; + let b = x[1]; + a*a + b*b +} + +fn main() { + assert_eq!(norm2([3.0, 4.0]), 5.0*5.0); +} + +// END RUST SOURCE + +// START rustc.norm2.InstCombine.before.mir +// _5 = Len(_1); +// ... +// _10 = Len(_1); +// END rustc.norm2.InstCombine.before.mir + +// START rustc.norm2.InstCombine.after.mir +// _5 = const 2usize; +// ... +// _10 = const 2usize; +// END rustc.norm2.InstCombine.after.mir diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 26b042d0343f0..50d8a5154c449 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -13,22 +13,30 @@ fn test(x: u32) -> u32 { y } -fn main() { } +fn main() { + // Make sure the function actually gets instantiated. + test(0); +} // END RUST SOURCE -// START rustc.node4.CopyPropagation.before.mir +// START rustc.test.CopyPropagation.before.mir // bb0: { -// _2 = _1; +// ... +// _3 = _1; +// ... +// _2 = move _3; +// ... // _4 = _2; -// _3 = _4; -// _5 = _3; -// _0 = _5; +// _0 = move _4; +// ... // return; // } -// END rustc.node4.CopyPropagation.before.mir -// START rustc.node4.CopyPropagation.after.mir +// END rustc.test.CopyPropagation.before.mir +// START rustc.test.CopyPropagation.after.mir // bb0: { -// _0 = _1; +// ... +// _0 = move _1; +// ... // return; // } -// END rustc.node4.CopyPropagation.after.mir +// END rustc.test.CopyPropagation.after.mir diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs new file mode 100644 index 0000000000000..35bb231df5adf --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -0,0 +1,140 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that CopyPropagation does not propagate an assignment to a function argument +// (doing so can break usages of the original argument value) + +fn dummy(x: u8) -> u8 { + x +} + +fn foo(mut x: u8) { + // calling `dummy` to make an use of `x` that copyprop cannot eliminate + x = dummy(x); // this will assign a local to `x` +} + +fn bar(mut x: u8) { + dummy(x); + x = 5; +} + +fn baz(mut x: i32) { + // self-assignment to a function argument should be eliminated + x = x; +} + +fn arg_src(mut x: i32) -> i32 { + let y = x; + x = 123; // Don't propagate this assignment to `y` + y +} + +fn main() { + // Make sure the function actually gets instantiated. + foo(0); + bar(0); + baz(0); + arg_src(0); +} + +// END RUST SOURCE +// START rustc.foo.CopyPropagation.before.mir +// bb0: { +// ... +// _3 = _1; +// _2 = const dummy(move _3) -> bb1; +// } +// bb1: { +// ... +// _1 = move _2; +// ... +// } +// END rustc.foo.CopyPropagation.before.mir +// START rustc.foo.CopyPropagation.after.mir +// bb0: { +// ... +// _3 = _1; +// _2 = const dummy(move _3) -> bb1; +// } +// bb1: { +// ... +// _1 = move _2; +// ... +// } +// END rustc.foo.CopyPropagation.after.mir +// START rustc.bar.CopyPropagation.before.mir +// bb0: { +// StorageLive(_3); +// _3 = _1; +// _2 = const dummy(move _3) -> bb1; +// } +// bb1: { +// StorageDead(_3); +// _1 = const 5u8; +// _0 = (); +// return; +// } +// END rustc.bar.CopyPropagation.before.mir +// START rustc.bar.CopyPropagation.after.mir +// bb0: { +// ... +// _3 = _1; +// _2 = const dummy(move _3) -> bb1; +// } +// bb1: { +// ... +// _1 = const 5u8; +// ... +// } +// END rustc.bar.CopyPropagation.after.mir +// START rustc.baz.CopyPropagation.before.mir +// bb0: { +// StorageLive(_2); +// _2 = _1; +// _1 = move _2; +// StorageDead(_2); +// _0 = (); +// return; +// } +// END rustc.baz.CopyPropagation.before.mir +// START rustc.baz.CopyPropagation.after.mir +// bb0: { +// ... +// _2 = _1; +// _1 = move _2; +// ... +// } +// END rustc.baz.CopyPropagation.after.mir +// START rustc.arg_src.CopyPropagation.before.mir +// bb0: { +// ... +// _3 = _1; +// _2 = move _3; +// ... +// _1 = const 123i32; +// ... +// _4 = _2; +// _0 = move _4; +// ... +// return; +// } +// END rustc.arg_src.CopyPropagation.before.mir +// START rustc.arg_src.CopyPropagation.after.mir +// bb0: { +// ... +// _3 = _1; +// ... +// _1 = const 123i32; +// ... +// _0 = move _3; +// ... +// return; +// } +// END rustc.arg_src.CopyPropagation.after.mir diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index 81dd1932894fb..c918bef129ad1 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -18,24 +18,31 @@ fn bar(a: usize) -> Baz { Baz { x: a, y: 0.0, z: false } } -fn main() {} +fn main() { + // Make sure the function actually gets instantiated. + bar(0); +} // END RUST SOURCE -// START rustc.node13.Deaggregator.before.mir +// START rustc.bar.Deaggregator.before.mir // bb0: { +// ... // _2 = _1; -// _3 = _2; -// _0 = Baz { x: _3, y: const 0f32, z: const false }; +// ... +// _0 = Baz { x: move _2, y: const 0f32, z: const false }; +// ... // return; // } -// END rustc.node13.Deaggregator.before.mir -// START rustc.node13.Deaggregator.after.mir +// END rustc.bar.Deaggregator.before.mir +// START rustc.bar.Deaggregator.after.mir // bb0: { +// ... // _2 = _1; -// _3 = _2; -// (_0.0: usize) = _3; +// ... +// (_0.0: usize) = move _2; // (_0.1: f32) = const 0f32; // (_0.2: bool) = const false; +// ... // return; // } -// END rustc.node13.Deaggregator.after.mir +// END rustc.bar.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs index 25fa0e90835c6..8af56b7c01173 100644 --- a/src/test/mir-opt/deaggregator_test_enum.rs +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -26,20 +26,22 @@ fn main() { } // END RUST SOURCE -// START rustc.node10.Deaggregator.before.mir +// START rustc.bar.Deaggregator.before.mir // bb0: { +// StorageLive(_2); // _2 = _1; -// _3 = _2; -// _0 = Baz::Foo { x: _3 }; +// _0 = Baz::Foo { x: move _2 }; +// StorageDead(_2); // return; // } -// END rustc.node10.Deaggregator.before.mir -// START rustc.node10.Deaggregator.after.mir +// END rustc.bar.Deaggregator.before.mir +// START rustc.bar.Deaggregator.after.mir // bb0: { +// StorageLive(_2); // _2 = _1; -// _3 = _2; -// ((_0 as Foo).0: usize) = _3; +// ((_0 as Foo).0: usize) = move _2; // discriminant(_0) = 1; +// StorageDead(_2); // return; // } -// END rustc.node10.Deaggregator.after.mir +// END rustc.bar.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index 02d496b2901e6..b6505de22f3b6 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -23,35 +23,44 @@ fn test1(x: bool, y: i32) -> Foo { } } -fn main() {} +fn main() { + // Make sure the function actually gets instantiated. + test1(false, 0); +} // END RUST SOURCE -// START rustc.node12.Deaggregator.before.mir +// START rustc.test1.Deaggregator.before.mir // bb1: { -// _6 = _4; -// _0 = Foo::A(_6,); +// StorageLive(_4); +// _4 = _2; +// _0 = Foo::A(move _4,); +// StorageDead(_4); // goto -> bb3; // } -// // bb2: { -// _7 = _4; -// _0 = Foo::B(_7,); +// StorageLive(_5); +// _5 = _2; +// _0 = Foo::B(move _5,); +// StorageDead(_5); // goto -> bb3; // } -// END rustc.node12.Deaggregator.before.mir -// START rustc.node12.Deaggregator.after.mir +// END rustc.test1.Deaggregator.before.mir +// START rustc.test1.Deaggregator.after.mir // bb1: { -// _6 = _4; -// ((_0 as A).0: i32) = _6; +// StorageLive(_4); +// _4 = _2; +// ((_0 as A).0: i32) = move _4; // discriminant(_0) = 0; +// StorageDead(_4); // goto -> bb3; // } -// // bb2: { -// _7 = _4; -// ((_0 as B).0: i32) = _7; +// StorageLive(_5); +// _5 = _2; +// ((_0 as B).0: i32) = move _5; // discriminant(_0) = 1; +// StorageDead(_5); // goto -> bb3; // } -// END rustc.node12.Deaggregator.after.mir +// END rustc.test1.Deaggregator.after.mir // diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index a180a69be55af..3a9a458fd464d 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -19,30 +19,41 @@ fn test(x: i32) -> [Foo; 2] { [Foo::A(x), Foo::A(x)] } -fn main() { } +fn main() { + // Make sure the function actually gets instantiated. + test(0); +} // END RUST SOURCE -// START rustc.node10.Deaggregator.before.mir +// START rustc.test.Deaggregator.before.mir // bb0: { -// _2 = _1; -// _4 = _2; -// _3 = Foo::A(_4,); -// _6 = _2; -// _5 = Foo::A(_6,); -// _0 = [_3, _5]; +// ... +// _3 = _1; +// ... +// _2 = Foo::A(move _3,); +// ... +// _5 = _1; +// _4 = Foo::A(move _5,); +// ... +// _0 = [move _2, move _4]; +// ... // return; // } -// END rustc.node10.Deaggregator.before.mir -// START rustc.node10.Deaggregator.after.mir +// END rustc.test.Deaggregator.before.mir +// START rustc.test.Deaggregator.after.mir // bb0: { -// _2 = _1; -// _4 = _2; -// ((_3 as A).0: i32) = _4; -// discriminant(_3) = 0; -// _6 = _2; -// ((_5 as A).0: i32) = _6; -// discriminant(_5) = 0; -// _0 = [_3, _5]; +// ... +// _3 = _1; +// ... +// ((_2 as A).0: i32) = move _3; +// discriminant(_2) = 0; +// ... +// _5 = _1; +// ((_4 as A).0: i32) = move _5; +// discriminant(_4) = 0; +// ... +// _0 = [move _2, move _4]; +// ... // return; // } -// END rustc.node10.Deaggregator.after.mir +// END rustc.test.Deaggregator.after.mir diff --git a/src/test/mir-opt/end_region_1.rs b/src/test/mir-opt/end_region_1.rs index 1941d1bc7be1b..640dec0861d04 100644 --- a/src/test/mir-opt/end_region_1.rs +++ b/src/test/mir-opt/end_region_1.rs @@ -19,11 +19,13 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _1: i32; +// ... // let _2: &'10_1rs i32; -// +// ... // bb0: { // StorageLive(_1); // _1 = const 3i32; @@ -35,4 +37,4 @@ fn main() { // StorageDead(_1); // return; // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index d8dd4aeadf495..56c3e2a38a0ed 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -24,13 +24,18 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _2: bool; +// ... // let _3: &'23_1rs bool; +// ... // let _7: &'23_3rs bool; +// ... // let mut _4: (); // let mut _5: bool; +// ... // bb0: { // goto -> bb1; // } @@ -41,7 +46,7 @@ fn main() { // _3 = &'23_1rs _2; // StorageLive(_5); // _5 = _2; -// switchInt(_5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; // } // bb2: { // _0 = (); @@ -52,6 +57,7 @@ fn main() { // return; // } // bb3: { +// _4 = (); // StorageDead(_5); // StorageLive(_7); // _7 = &'23_3rs _2; @@ -63,4 +69,4 @@ fn main() { // StorageDead(_2); // goto -> bb1; // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index e404af838cef4..8c0d56eba7828 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -25,15 +25,19 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let mut _1: bool; +// ... // let _3: &'26_1rs bool; +// ... // let _7: &'26_3rs bool; +// ... // let mut _2: (); // let mut _4: (); // let mut _5: bool; -// +// let mut _6: !; // bb0: { // StorageLive(_1); // goto -> bb1; @@ -44,7 +48,7 @@ fn main() { // _3 = &'26_1rs _1; // StorageLive(_5); // _5 = _1; -// switchInt(_5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; // } // bb2: { // _0 = (); @@ -66,4 +70,4 @@ fn main() { // StorageDead(_3); // goto -> bb1; // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_4.rs b/src/test/mir-opt/end_region_4.rs index d51c627d14b23..ded818688d72f 100644 --- a/src/test/mir-opt/end_region_4.rs +++ b/src/test/mir-opt/end_region_4.rs @@ -29,12 +29,17 @@ fn foo(i: i32) { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _1: D; +// ... // let _2: i32; +// ... // let _3: &'26_2rs i32; +// ... // let _6: &'26_4rs i32; +// ... // let mut _4: (); // let mut _5: i32; // bb0: { @@ -46,9 +51,12 @@ fn foo(i: i32) { // _3 = &'26_2rs _2; // StorageLive(_5); // _5 = (*_3); -// _4 = const foo(_5) -> [return: bb1, unwind: bb3]; +// _4 = const foo(move _5) -> [return: bb2, unwind: bb3]; // } // bb1: { +// resume; +// } +// bb2: { // StorageDead(_5); // StorageLive(_6); // _6 = &'26_4rs _2; @@ -58,17 +66,14 @@ fn foo(i: i32) { // EndRegion('26_2rs); // StorageDead(_3); // StorageDead(_2); -// drop(_1) -> bb4; -// } -// bb2: { -// resume; +// drop(_1) -> [return: bb4, unwind: bb1]; // } // bb3: { // EndRegion('26_2rs); -// drop(_1) -> bb2; +// drop(_1) -> bb1; // } // bb4: { // StorageDead(_1); // return; // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs index 6299ec3815cd1..1da97a997a19c 100644 --- a/src/test/mir-opt/end_region_5.rs +++ b/src/test/mir-opt/end_region_5.rs @@ -9,7 +9,6 @@ // except according to those terms. // compile-flags: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength // Unwinding should EndRegion for in-scope borrows: Borrowing via by-ref closure. @@ -26,10 +25,13 @@ fn foo(f: F) where F: FnOnce() -> i32 { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { +// ... // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(18) d:&'14s D]; // let mut _4: &'14s D; @@ -39,31 +41,31 @@ fn foo(f: F) where F: FnOnce() -> i32 { // StorageLive(_3); // StorageLive(_4); // _4 = &'14s _1; -// _3 = [closure@NodeId(18)] { d: _4 }; +// _3 = [closure@NodeId(18)] { d: move _4 }; // StorageDead(_4); -// _2 = const foo(_3) -> [return: bb1, unwind: bb3]; +// _2 = const foo(move _3) -> [return: bb2, unwind: bb3]; // } // bb1: { +// resume; +// } +// bb2: { // EndRegion('14s); // StorageDead(_3); // _0 = (); -// drop(_1) -> bb4; -// } -// bb2: { -// resume; +// drop(_1) -> [return: bb4, unwind: bb1]; // } // bb3: { // EndRegion('14s); -// drop(_1) -> bb2; +// drop(_1) -> bb1; // } // bb4: { // StorageDead(_1); // return; // } // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir -// START rustc.node18.SimplifyCfg-qualify-consts.after.mir +// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(18) d:&'14s D]) -> i32 { // let mut _0: i32; // let mut _2: i32; @@ -71,8 +73,8 @@ fn foo(f: F) where F: FnOnce() -> i32 { // bb0: { // StorageLive(_2); // _2 = ((*(_1.0: &'14s D)).0: i32); -// _0 = _2; +// _0 = move _2; // StorageDead(_2); // return; // } -// END rustc.node18.SimplifyCfg-qualify-consts.after.mir +// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs index 13ab3e4f2dd2a..dadc755eb8c57 100644 --- a/src/test/mir-opt/end_region_6.rs +++ b/src/test/mir-opt/end_region_6.rs @@ -26,10 +26,12 @@ fn foo(f: F) where F: FnOnce() -> i32 { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(22) d:&'19s D]; // let mut _4: &'19s D; @@ -39,44 +41,45 @@ fn foo(f: F) where F: FnOnce() -> i32 { // StorageLive(_3); // StorageLive(_4); // _4 = &'19s _1; -// _3 = [closure@NodeId(22)] { d: _4 }; +// _3 = [closure@NodeId(22)] { d: move _4 }; // StorageDead(_4); -// _2 = const foo(_3) -> [return: bb1, unwind: bb3]; +// _2 = const foo(move _3) -> [return: bb2, unwind: bb3]; // } // bb1: { +// resume; +// } +// bb2: { // EndRegion('19s); // StorageDead(_3); // _0 = (); -// drop(_1) -> bb4; -// } -// bb2: { -// resume; +// drop(_1) -> [return: bb4, unwind: bb1]; // } // bb3: { // EndRegion('19s); -// drop(_1) -> bb2; +// drop(_1) -> bb1; // } // bb4: { // StorageDead(_1); // return; // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir -// START rustc.node22.SimplifyCfg-qualify-consts.after.mir +// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(22) d:&'19s D]) -> i32 { // let mut _0: i32; +// ... // let _2: &'15_0rs D; +// ... // let mut _3: i32; -// // bb0: { // StorageLive(_2); // _2 = &'15_0rs (*(_1.0: &'19s D)); // StorageLive(_3); // _3 = ((*_2).0: i32); -// _0 = _3; +// _0 = move _3; // StorageDead(_3); // EndRegion('15_0rs); // StorageDead(_2); // return; // } -// END rustc.node22.SimplifyCfg-qualify-consts.after.mir +// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs index 826d3749167d3..1426174b482b6 100644 --- a/src/test/mir-opt/end_region_7.rs +++ b/src/test/mir-opt/end_region_7.rs @@ -26,21 +26,22 @@ fn foo(f: F) where F: FnOnce() -> i32 { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(22) d:D]; // let mut _4: D; -// // bb0: { // StorageLive(_1); // _1 = D::{{constructor}}(const 0i32,); // StorageLive(_3); // StorageLive(_4); -// _4 = _1; -// _3 = [closure@NodeId(22)] { d: _4 }; +// _4 = move _1; +// _3 = [closure@NodeId(22)] { d: move _4 }; // drop(_4) -> [return: bb4, unwind: bb3]; // } // bb1: { @@ -54,7 +55,7 @@ fn foo(f: F) where F: FnOnce() -> i32 { // } // bb4: { // StorageDead(_4); -// _2 = const foo(_3) -> [return: bb5, unwind: bb3]; +// _2 = const foo(move _3) -> [return: bb5, unwind: bb3]; // } // bb5: { // drop(_3) -> [return: bb6, unwind: bb2]; @@ -62,34 +63,38 @@ fn foo(f: F) where F: FnOnce() -> i32 { // bb6: { // StorageDead(_3); // _0 = (); -// drop(_1) -> bb7; +// drop(_1) -> [return: bb7, unwind: bb1]; // } // bb7: { // StorageDead(_1); // return; // } // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir -// START rustc.node22.SimplifyCfg-qualify-consts.after.mir +// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 { // let mut _0: i32; +// ... // let _2: &'15_0rs D; +// ... // let mut _3: i32; -// // bb0: { // StorageLive(_2); // _2 = &'15_0rs (_1.0: D); // StorageLive(_3); // _3 = ((*_2).0: i32); -// _0 = _3; +// _0 = move _3; // StorageDead(_3); // EndRegion('15_0rs); // StorageDead(_2); -// drop(_1) -> bb1; +// drop(_1) -> [return: bb2, unwind: bb1]; // } // bb1: { +// resume; +// } +// bb2: { // return; // } // } -// END rustc.node22.SimplifyCfg-qualify-consts.after.mir +// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs index 6438484fcfae4..405864aba9436 100644 --- a/src/test/mir-opt/end_region_8.rs +++ b/src/test/mir-opt/end_region_8.rs @@ -27,11 +27,14 @@ fn foo(f: F) where F: FnOnce() -> i32 { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let _2: &'21_1rs D; +// ... // let mut _3: (); // let mut _4: [closure@NodeId(22) r:&'21_1rs D]; // let mut _5: &'21_1rs D; @@ -43,32 +46,32 @@ fn foo(f: F) where F: FnOnce() -> i32 { // StorageLive(_4); // StorageLive(_5); // _5 = _2; -// _4 = [closure@NodeId(22)] { r: _5 }; +// _4 = [closure@NodeId(22)] { r: move _5 }; // StorageDead(_5); -// _3 = const foo(_4) -> [return: bb1, unwind: bb3]; +// _3 = const foo(move _4) -> [return: bb2, unwind: bb3]; // } // bb1: { +// resume; +// } +// bb2: { // StorageDead(_4); // _0 = (); // EndRegion('21_1rs); // StorageDead(_2); -// drop(_1) -> bb4; -// } -// bb2: { -// resume; +// drop(_1) -> [return: bb4, unwind: bb1]; // } // bb3: { // EndRegion('21_1rs); -// drop(_1) -> bb2; +// drop(_1) -> bb1; // } // bb4: { // StorageDead(_1); // return; // } // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir -// START rustc.node22.SimplifyCfg-qualify-consts.after.mir +// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(22) r:&'21_1rs D]) -> i32 { // let mut _0: i32; // let mut _2: i32; @@ -76,9 +79,9 @@ fn foo(f: F) where F: FnOnce() -> i32 { // bb0: { // StorageLive(_2); // _2 = ((*(_1.0: &'21_1rs D)).0: i32); -// _0 = _2; +// _0 = move _2; // StorageDead(_2); // return; // } // } -// END rustc.node22.SimplifyCfg-qualify-consts.after.mir +// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index 59d5d934391e9..b313e296ac99c 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -37,18 +37,21 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let mut _1: bool; +// ... // let _2: i32; +// ... // let mut _4: &'33_0rs i32; +// ... // let mut _3: (); // let mut _5: !; // let mut _6: (); // let mut _7: bool; // let mut _8: !; -// // bb0: { // StorageLive(_1); // _1 = const false; @@ -61,9 +64,8 @@ fn main() { // bb1: { // StorageLive(_7); // _7 = _1; -// switchInt(_7) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _7) -> [0u8: bb3, otherwise: bb2]; // } -// // bb2: { // _0 = (); // StorageDead(_7); @@ -73,7 +75,6 @@ fn main() { // StorageDead(_1); // return; // } -// // bb3: { // _4 = &'33_0rs _2; // _6 = (); @@ -83,4 +84,4 @@ fn main() { // goto -> bb1; // } // } -// END rustc.node4.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 8f9dd79cd7542..37a6229febabb 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -39,12 +39,13 @@ fn main() { fn query() -> bool { true } // END RUST SOURCE -// START rustc.node16.SimplifyCfg-qualify-consts.after.mir +// START rustc.main.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); // scope 1 { // let _2: S<'35_0rs>; // } +// ... // let mut _1: (); // let mut _3: std::cell::Cell>>; // let mut _4: std::option::Option<&'35_0rs S<'35_0rs>>; @@ -61,6 +62,7 @@ fn query() -> bool { true } // let mut _15: std::option::Option<&'35_0rs S<'35_0rs>>; // let mut _16: &'35_0rs S<'35_0rs>; // let mut _17: &'35_0rs S<'35_0rs>; +// // bb0: { // goto -> bb1; // } @@ -69,11 +71,14 @@ fn query() -> bool { true } // StorageLive(_3); // StorageLive(_4); // _4 = std::option::Option<&'35_0rs S<'35_0rs>>::None; -// _3 = const >::new(_4) -> bb2; +// _3 = const >::new(move _4) -> [return: bb3, unwind: bb2]; // } // bb2: { +// resume; +// } +// bb3: { // StorageDead(_4); -// _2 = S<'35_0rs> { r: _3 }; +// _2 = S<'35_0rs> { r: move _3 }; // StorageDead(_3); // StorageLive(_6); // _6 = &'16s (_2.0: std::cell::Cell>>); @@ -82,29 +87,29 @@ fn query() -> bool { true } // StorageLive(_9); // _9 = &'35_0rs _2; // _8 = &'35_0rs (*_9); -// _7 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(_8,); +// _7 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _8,); // StorageDead(_8); -// _5 = const >::set(_6, _7) -> bb3; +// _5 = const >::set(move _6, move _7) -> [return: bb4, unwind: bb2]; // } -// bb3: { +// bb4: { // EndRegion('16s); // StorageDead(_7); // StorageDead(_6); // StorageDead(_9); // StorageLive(_11); -// _11 = const query() -> bb4; -// } -// bb4: { -// switchInt(_11) -> [0u8: bb6, otherwise: bb5]; +// _11 = const query() -> [return: bb5, unwind: bb2]; // } // bb5: { +// switchInt(move _11) -> [0u8: bb7, otherwise: bb6]; +// } +// bb6: { // _0 = (); // StorageDead(_11); // EndRegion('35_0rs); // StorageDead(_2); // return; // } -// bb6: { +// bb7: { // _10 = (); // StorageDead(_11); // StorageLive(_14); @@ -114,11 +119,11 @@ fn query() -> bool { true } // StorageLive(_17); // _17 = &'35_0rs _2; // _16 = &'35_0rs (*_17); -// _15 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(_16,); +// _15 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _16,); // StorageDead(_16); -// _13 = const >::set(_14, _15) -> bb7; +// _13 = const >::set(move _14, move _15) -> [return: bb8, unwind: bb2]; // } -// bb7: { +// bb8: { // EndRegion('33s); // StorageDead(_15); // StorageDead(_14); @@ -129,4 +134,4 @@ fn query() -> bool { true } // goto -> bb1; // } // } -// END rustc.node16.SimplifyCfg-qualify-consts.after.mir +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index 1f9ad988acc0b..69c5cdccf49d7 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -60,11 +60,11 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // transformation encoding the effects of rvalue-promotion. // This may be the simplest and most-likely option; note in // particular that `StorageDead(_6)` goes away below in -// rustc.node4.QualifyAndPromoteConstants.after.mir +// rustc.main.QualifyAndPromoteConstants.after.mir // END RUST SOURCE -// START rustc.node4.QualifyAndPromoteConstants.before.mir +// START rustc.main.QualifyAndPromoteConstants.before.mir // fn main() -> () { // let mut _0: (); // let mut _1: &'12ds S1; @@ -92,17 +92,21 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // _9 = S1::{{constructor}}(const "dang1",); // _8 = &'10s _9; // _7 = &'10s (*_8); -// _3 = D1<'12ds, '10s>::{{constructor}}(_4, _7); +// _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7); // EndRegion('10s); // StorageDead(_7); // StorageDead(_4); // _2 = (_3.0: &'12ds S1); -// _1 = _2; +// _1 = move _2; // StorageDead(_2); -// drop(_3) -> bb1; +// drop(_3) -> [return: bb2, unwind: bb1]; // } // // bb1: { +// resume; +// } +// +// bb2: { // StorageDead(_3); // StorageDead(_8); // StorageDead(_9); @@ -113,9 +117,9 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // return; // } // } -// END rustc.node4.QualifyAndPromoteConstants.before.mir +// END rustc.main.QualifyAndPromoteConstants.before.mir -// START rustc.node4.QualifyAndPromoteConstants.after.mir +// START rustc.main.QualifyAndPromoteConstants.after.mir // fn main() -> () { // let mut _0: (); // let mut _1: &'12ds S1; @@ -133,23 +137,27 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // StorageLive(_3); // StorageLive(_4); // StorageLive(_5); -// _5 = promoted1; +// _5 = promoted[1]; // _4 = &'12ds (*_5); // StorageLive(_7); // StorageLive(_8); -// _8 = promoted0; +// _8 = promoted[0]; // _7 = &'10s (*_8); -// _3 = D1<'12ds, '10s>::{{constructor}}(_4, _7); +// _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7); // EndRegion('10s); // StorageDead(_7); // StorageDead(_4); // _2 = (_3.0: &'12ds S1); -// _1 = _2; +// _1 = move _2; // StorageDead(_2); -// drop(_3) -> bb1; +// drop(_3) -> [return: bb2, unwind: bb1]; // } // // bb1: { +// resume; +// } +// +// bb2: { // StorageDead(_3); // StorageDead(_8); // StorageDead(_5); @@ -158,4 +166,4 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // return; // } // } -// END rustc.node4.QualifyAndPromoteConstants.after.mir +// END rustc.main.QualifyAndPromoteConstants.after.mir diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs new file mode 100644 index 0000000000000..3fb54f90984dd --- /dev/null +++ b/src/test/mir-opt/inline-closure-borrows-arg.rs @@ -0,0 +1,50 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z span_free_formats + +// Tests that MIR inliner can handle closure arguments, +// even when (#45894) + +fn main() { + println!("{}", foo(0, &14)); +} + +fn foo(_t: T, q: &i32) -> i32 { + let x = |r: &i32, _s: &i32| { + let variable = &*r; + *variable + }; + x(q, q) +} + +// END RUST SOURCE +// START rustc.foo.Inline.after.mir +// ... +// bb0: { +// ... +// _3 = [closure@NodeId(39)]; +// ... +// _4 = &_3; +// ... +// _6 = &(*_2); +// ... +// _7 = &(*_2); +// _5 = (move _6, move _7); +// _9 = move (_5.0: &i32); +// _10 = move (_5.1: &i32); +// StorageLive(_8); +// _8 = (*_9); +// _0 = move _8; +// ... +// return; +// } +// ... +// END rustc.foo.Inline.after.mir diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs new file mode 100644 index 0000000000000..dc8ff13c03a88 --- /dev/null +++ b/src/test/mir-opt/inline-closure.rs @@ -0,0 +1,44 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z span_free_formats + +// Tests that MIR inliner can handle closure arguments. (#45894) + +fn main() { + println!("{}", foo(0, 14)); +} + +fn foo(_t: T, q: i32) -> i32 { + let x = |_t, _q| _t; + x(q, q) +} + +// END RUST SOURCE +// START rustc.foo.Inline.after.mir +// ... +// bb0: { +// ... +// _3 = [closure@NodeId(28)]; +// ... +// _4 = &_3; +// ... +// _6 = _2; +// ... +// _7 = _2; +// _5 = (move _6, move _7); +// _8 = move (_5.0: i32); +// _9 = move (_5.1: i32); +// _0 = move _8; +// ... +// return; +// } +// ... +// END rustc.foo.Inline.after.mir diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index 5a9336e96592d..b5c188cf834a9 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -21,7 +21,7 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyCfg-initial.after.mir +// START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // StorageLive(_1); // _1 = const false; @@ -31,7 +31,7 @@ fn main() { // bb1: { // StorageLive(_4); // _4 = _1; -// switchInt(_4) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _4) -> [0u8: bb3, otherwise: bb2]; // } // // bb2: { @@ -48,4 +48,4 @@ fn main() { // _2 = (); // goto -> bb1; // } -// END rustc.node4.SimplifyCfg-initial.after.mir +// END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index 3a8b5c449c22a..f7f447cc6ba64 100644 --- a/src/test/mir-opt/issue-41110.rs +++ b/src/test/mir-opt/issue-41110.rs @@ -8,12 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + // check that we don't emit multiple drop flags when they are not needed. fn main() { let x = S.other(S.id()); } +// no_mangle to make sure this gets instantiated even in an executable. +#[no_mangle] pub fn test() { let u = S; let mut v = S; @@ -33,24 +37,28 @@ impl S { } // END RUST SOURCE -// START rustc.node4.ElaborateDrops.after.mir +// START rustc.main.ElaborateDrops.after.mir // let mut _0: (); -// let _1: (); +// scope 1 { +// let _1: (); +// } +// ... // let mut _2: S; // let mut _3: S; // let mut _4: S; // let mut _5: bool; -// // bb0: { -// END rustc.node4.ElaborateDrops.after.mir -// START rustc.node13.ElaborateDrops.after.mir +// END rustc.main.ElaborateDrops.after.mir +// START rustc.test.ElaborateDrops.after.mir // let mut _0: (); +// ... // let _1: S; +// ... // let mut _2: S; +// ... // let mut _3: (); // let mut _4: S; // let mut _5: S; // let mut _6: bool; -// // bb0: { -// END rustc.node13.ElaborateDrops.after.mir +// END rustc.test.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/issue-41697.rs b/src/test/mir-opt/issue-41697.rs index 47eeffe35a83e..4d2ba5e2b120c 100644 --- a/src/test/mir-opt/issue-41697.rs +++ b/src/test/mir-opt/issue-41697.rs @@ -12,7 +12,7 @@ // artificial cycles: during type-checking, we had to get the MIR for // the constant expressions in `[u8; 2]`, which in turn would trigger // an attempt to get the item-path, which in turn would request the -// types of the impl, which would trigger a cycle. We supressed this +// types of the impl, which would trigger a cycle. We suppressed this // cycle now by forcing mir-dump to avoid asking for types of an impl. #![feature(rustc_attrs)] diff --git a/src/test/mir-opt/issue-43457.rs b/src/test/mir-opt/issue-43457.rs index 3f0f5068577c6..85cecc5070cd5 100644 --- a/src/test/mir-opt/issue-43457.rs +++ b/src/test/mir-opt/issue-43457.rs @@ -23,7 +23,7 @@ fn rc_refcell_test(r: RefCell) { fn main() { } // END RUST SOURCE -// START rustc.node5.SimplifyCfg-qualify-consts.after.mir +// START rustc.rc_refcell_test.SimplifyCfg-qualify-consts.after.mir // // fn rc_refcell_test(_1: std::cell::RefCell) -> () { // let mut _0: (); diff --git a/src/test/mir-opt/lower_128bit_debug_test.rs b/src/test/mir-opt/lower_128bit_debug_test.rs new file mode 100644 index 0000000000000..82d0815554799 --- /dev/null +++ b/src/test/mir-opt/lower_128bit_debug_test.rs @@ -0,0 +1,119 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// asmjs can't even pass i128 as arguments or return values, so ignore it. +// this will hopefully be fixed by the LLVM 5 upgrade (#43370) +// ignore-asmjs +// ignore-emscripten + +// compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=yes + +#![feature(i128_type)] + +fn test_signed(mut x: i128) -> i128 { + x += 1; + x -= 2; + x *= 3; + x /= 4; + x %= 5; + x <<= 6; + x >>= 7; + x +} + +fn test_unsigned(mut x: u128) -> u128 { + x += 1; + x -= 2; + x *= 3; + x /= 4; + x %= 5; + x <<= 6; + x >>= 7; + x +} + +fn main() { + assert_eq!(test_signed(-222), -1); + assert_eq!(test_unsigned(200), 2); +} + +// END RUST SOURCE + +// START rustc.test_signed.Lower128Bit.after.mir +// _2 = const compiler_builtins::int::addsub::rust_i128_addo(_1, const 1i128) -> bb10; +// ... +// _1 = move (_2.0: i128); +// _3 = const compiler_builtins::int::addsub::rust_i128_subo(_1, const 2i128) -> bb11; +// ... +// _1 = move (_3.0: i128); +// _4 = const compiler_builtins::int::mul::rust_i128_mulo(_1, const 3i128) -> bb12; +// ... +// _1 = move (_4.0: i128); +// ... +// _1 = const compiler_builtins::int::sdiv::rust_i128_div(_1, const 4i128) -> bb13; +// ... +// _1 = const compiler_builtins::int::sdiv::rust_i128_rem(_1, const 5i128) -> bb15; +// ... +// _1 = move (_13.0: i128); +// ... +// _17 = const 7i32 as u128 (Misc); +// _14 = const compiler_builtins::int::shift::rust_i128_shro(_1, move _17) -> bb16; +// ... +// _1 = move (_14.0: i128); +// ... +// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; +// ... +// assert(!move (_3.1: bool), "attempt to subtract with overflow") -> bb2; +// ... +// assert(!move (_4.1: bool), "attempt to multiply with overflow") -> bb3; +// ... +// assert(!move (_13.1: bool), "attempt to shift left with overflow") -> bb8; +// ... +// _16 = const 6i32 as u128 (Misc); +// _13 = const compiler_builtins::int::shift::rust_i128_shlo(_1, move _16) -> bb14; +// ... +// assert(!move (_14.1: bool), "attempt to shift right with overflow") -> bb9; +// END rustc.test_signed.Lower128Bit.after.mir + +// START rustc.test_unsigned.Lower128Bit.after.mir +// _2 = const compiler_builtins::int::addsub::rust_u128_addo(_1, const 1u128) -> bb8; +// ... +// _1 = move (_2.0: u128); +// _3 = const compiler_builtins::int::addsub::rust_u128_subo(_1, const 2u128) -> bb9; +// ... +// _1 = move (_3.0: u128); +// _4 = const compiler_builtins::int::mul::rust_u128_mulo(_1, const 3u128) -> bb10; +// ... +// _1 = move (_4.0: u128); +// ... +// _1 = const compiler_builtins::int::udiv::rust_u128_div(_1, const 4u128) -> bb11; +// ... +// _1 = const compiler_builtins::int::udiv::rust_u128_rem(_1, const 5u128) -> bb13; +// ... +// _1 = move (_7.0: u128); +// ... +// _11 = const 7i32 as u128 (Misc); +// _8 = const compiler_builtins::int::shift::rust_u128_shro(_1, move _11) -> bb14; +// ... +// _1 = move (_8.0: u128); +// ... +// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; +// ... +// assert(!move (_3.1: bool), "attempt to subtract with overflow") -> bb2; +// ... +// assert(!move (_4.1: bool), "attempt to multiply with overflow") -> bb3; +// ... +// assert(!move (_7.1: bool), "attempt to shift left with overflow") -> bb6; +// ... +// _10 = const 6i32 as u128 (Misc); +// _7 = const compiler_builtins::int::shift::rust_u128_shlo(_1, move _10) -> bb12; +// ... +// assert(!move (_8.1: bool), "attempt to shift right with overflow") -> bb7; +// END rustc.test_unsigned.Lower128Bit.after.mir diff --git a/src/test/mir-opt/lower_128bit_test.rs b/src/test/mir-opt/lower_128bit_test.rs new file mode 100644 index 0000000000000..4b54f9a6d44f4 --- /dev/null +++ b/src/test/mir-opt/lower_128bit_test.rs @@ -0,0 +1,83 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// asmjs can't even pass i128 as arguments or return values, so ignore it. +// this will hopefully be fixed by the LLVM 5 upgrade (#43370) +// ignore-asmjs +// ignore-emscripten + +// compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=no + +#![feature(i128_type)] + +fn test_signed(mut x: i128) -> i128 { + x += 1; + x -= 2; + x *= 3; + x /= 4; + x %= 5; + x <<= 6; + x >>= 7; + x +} + +fn test_unsigned(mut x: u128) -> u128 { + x += 1; + x -= 2; + x *= 3; + x /= 4; + x %= 5; + x <<= 6; + x >>= 7; + x +} + +fn main() { + assert_eq!(test_signed(-222), -1); + assert_eq!(test_unsigned(200), 2); +} + +// END RUST SOURCE + +// START rustc.test_signed.Lower128Bit.after.mir +// _1 = const compiler_builtins::int::addsub::rust_i128_add(_1, const 1i128) -> bb7; +// ... +// _1 = const compiler_builtins::int::sdiv::rust_i128_div(_1, const 4i128) -> bb8; +// ... +// _1 = const compiler_builtins::int::sdiv::rust_i128_rem(_1, const 5i128) -> bb11; +// ... +// _1 = const compiler_builtins::int::mul::rust_i128_mul(_1, const 3i128) -> bb5; +// ... +// _1 = const compiler_builtins::int::addsub::rust_i128_sub(_1, const 2i128) -> bb6; +// ... +// _11 = const 7i32 as u32 (Misc); +// _1 = const compiler_builtins::int::shift::rust_i128_shr(_1, move _11) -> bb9; +// ... +// _12 = const 6i32 as u32 (Misc); +// _1 = const compiler_builtins::int::shift::rust_i128_shl(_1, move _12) -> bb10; +// END rustc.test_signed.Lower128Bit.after.mir + +// START rustc.test_unsigned.Lower128Bit.after.mir +// _1 = const compiler_builtins::int::addsub::rust_u128_add(_1, const 1u128) -> bb5; +// ... +// _1 = const compiler_builtins::int::udiv::rust_u128_div(_1, const 4u128) -> bb6; +// ... +// _1 = const compiler_builtins::int::udiv::rust_u128_rem(_1, const 5u128) -> bb9; +// ... +// _1 = const compiler_builtins::int::mul::rust_u128_mul(_1, const 3u128) -> bb3; +// ... +// _1 = const compiler_builtins::int::addsub::rust_u128_sub(_1, const 2u128) -> bb4; +// ... +// _5 = const 7i32 as u32 (Misc); +// _1 = const compiler_builtins::int::shift::rust_u128_shr(_1, move _5) -> bb7; +// ... +// _6 = const 6i32 as u32 (Misc); +// _1 = const compiler_builtins::int::shift::rust_u128_shl(_1, move _6) -> bb8; +// END rustc.test_unsigned.Lower128Bit.after.mir diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs new file mode 100644 index 0000000000000..1f892b0f9587a --- /dev/null +++ b/src/test/mir-opt/match_false_edges.rs @@ -0,0 +1,255 @@ +// Copyright 2012-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. + +// compile-flags: -Z borrowck=mir + +fn guard() -> bool { + false +} + +fn guard2(_:i32) -> bool { + true +} + +// no_mangle to make sure this gets instantiated even in an executable. +#[no_mangle] +pub fn full_tested_match() { + let _ = match Some(42) { + Some(x) if guard() => (1, x), + Some(y) => (2, y), + None => (3, 3), + }; +} + +// no_mangle to make sure this gets instantiated even in an executable. +#[no_mangle] +pub fn full_tested_match2() { + let _ = match Some(42) { + Some(x) if guard() => (1, x), + None => (3, 3), + Some(y) => (2, y), + }; +} + +fn main() { + let _ = match Some(1) { + Some(_w) if guard() => 1, + _x => 2, + Some(y) if guard2(y) => 3, + _z => 4, + }; +} + +// END RUST SOURCE +// +// START rustc.full_tested_match.QualifyAndPromoteConstants.after.mir +// bb0: { +// ... +// _2 = std::option::Option::Some(const 42i32,); +// _5 = discriminant(_2); +// switchInt(move _5) -> [0isize: bb6, 1isize: bb4, otherwise: bb8]; +// } +// bb1: { +// resume; +// } +// bb2: { // arm1 +// StorageLive(_7); +// _7 = _3; +// _1 = (const 1i32, move _7); +// StorageDead(_7); +// goto -> bb13; +// } +// bb3: { // binding3(empty) and arm3 +// _1 = (const 3i32, const 3i32); +// goto -> bb13; +// } +// bb4: { +// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 +// } +// bb5: { +// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding2 +// } +// bb6: { +// falseEdges -> [real: bb3, imaginary: bb7]; //pre_binding3 +// } +// bb7: { +// unreachable; +// } +// bb8: { +// unreachable; +// } +// bb9: { // binding1 and guard +// StorageLive(_3); +// _3 = ((_2 as Some).0: i32); +// StorageLive(_6); +// _6 = const guard() -> [return: bb10, unwind: bb1]; +// } +// bb10: { // end of guard +// switchInt(move _6) -> [0u8: bb11, otherwise: bb2]; +// } +// bb11: { // to pre_binding2 +// falseEdges -> [real: bb5, imaginary: bb5]; +// } +// bb12: { // bindingNoLandingPads.before.mir2 and arm2 +// StorageLive(_4); +// _4 = ((_2 as Some).0: i32); +// StorageLive(_8); +// _8 = _4; +// _1 = (const 2i32, move _8); +// StorageDead(_8); +// goto -> bb13; +// } +// bb13: { +// ... +// return; +// } +// END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir +// +// START rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir +// bb0: { +// ... +// _2 = std::option::Option::Some(const 42i32,); +// _5 = discriminant(_2); +// switchInt(move _5) -> [0isize: bb5, 1isize: bb4, otherwise: bb8]; +// } +// bb1: { +// resume; +// } +// bb2: { // arm1 +// StorageLive(_7); +// _7 = _3; +// _1 = (const 1i32, move _7); +// StorageDead(_7); +// goto -> bb13; +// } +// bb3: { // binding3(empty) and arm3 +// _1 = (const 3i32, const 3i32); +// goto -> bb13; +// } +// bb4: { +// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 +// } +// bb5: { +// falseEdges -> [real: bb3, imaginary: bb6]; //pre_binding2 +// } +// bb6: { +// falseEdges -> [real: bb12, imaginary: bb7]; //pre_binding3 +// } +// bb7: { +// unreachable; +// } +// bb8: { +// unreachable; +// } +// bb9: { // binding1 and guard +// StorageLive(_3); +// _3 = ((_2 as Some).0: i32); +// StorageLive(_6); +// _6 = const guard() -> [return: bb10, unwind: bb1]; +// } +// bb10: { // end of guard +// switchInt(move _6) -> [0u8: bb11, otherwise: bb2]; +// } +// bb11: { // to pre_binding2 +// falseEdges -> [real: bb6, imaginary: bb5]; +// } +// bb12: { // binding2 and arm2 +// StorageLive(_4); +// _4 = ((_2 as Some).0: i32); +// StorageLive(_8); +// _8 = _4; +// _1 = (const 2i32, move _8); +// StorageDead(_8); +// goto -> bb13; +// } +// bb13: { +// ... +// return; +// } +// END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir +// +// START rustc.main.QualifyAndPromoteConstants.before.mir +// bb0: { +// ... +// _2 = std::option::Option::Some(const 1i32,); +// _7 = discriminant(_2); +// switchInt(move _7) -> [1isize: bb4, otherwise: bb5]; +// } +// bb1: { +// resume; +// } +// bb2: { // arm1 +// _1 = const 1i32; +// goto -> bb17; +// } +// bb3: { // arm3 +// _1 = const 3i32; +// goto -> bb17; +// } +// +// bb4: { +// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 +// } +// bb5: { +// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding2 +// } +// bb6: { +// falseEdges -> [real: bb13, imaginary: bb7]; //pre_binding3 +// } +// bb7: { +// falseEdges -> [real: bb16, imaginary: bb8]; //pre_binding4 +// } +// bb8: { +// unreachable; +// } +// bb9: { // binding1: Some(w) if guard() +// StorageLive(_3); +// _3 = ((_2 as Some).0: i32); +// StorageLive(_8); +// _8 = const guard() -> [return: bb10, unwind: bb1]; +// } +// bb10: { //end of guard +// switchInt(move _8) -> [0u8: bb11, otherwise: bb2]; +// } +// bb11: { // to pre_binding2 +// falseEdges -> [real: bb5, imaginary: bb5]; +// } +// bb12: { // binding2 & arm2 +// StorageLive(_4); +// _4 = _2; +// _1 = const 2i32; +// goto -> bb17; +// } +// bb13: { // binding3: Some(y) if guard2(y) +// StorageLive(_5); +// _5 = ((_2 as Some).0: i32); +// StorageLive(_10); +// StorageLive(_11); +// _11 = _5; +// _10 = const guard2(move _11) -> [return: bb14, unwind: bb1]; +// } +// bb14: { // end of guard2 +// StorageDead(_11); +// switchInt(move _10) -> [0u8: bb15, otherwise: bb3]; +// } +// bb15: { // to pre_binding4 +// falseEdges -> [real: bb7, imaginary: bb7]; +// } +// bb16: { // binding4 & arm4 +// StorageLive(_6); +// _6 = _2; +// _1 = const 4i32; +// goto -> bb17; +// } +// bb17: { +// ... +// return; +// } +// END rustc.main.QualifyAndPromoteConstants.before.mir diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs new file mode 100644 index 0000000000000..e4dd99f5a1e75 --- /dev/null +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -0,0 +1,45 @@ +// Copyright 2012-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. + +// compile-flags:-Znll + +fn can_panic() -> Box { + Box::new(44) +} + +fn main() { + let mut x = Box::new(22); + x = can_panic(); +} + +// Check that: +// - `_1` is the variable corresponding to `x` +// and +// - `_1` is live when `can_panic` is called (because it may be dropped) +// +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | Live variables on entry to bb0: [] +// bb0: { +// | Live variables at bb0[0]: [] +// StorageLive(_1); +// | Live variables at bb0[1]: [] +// _1 = const >::new(const 22usize) -> [return: bb2, unwind: bb1]; +// } +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// | Live variables on entry to bb2: [_1 (drop)] +// bb2: { +// | Live variables at bb2[0]: [_1 (drop)] +// StorageLive(_2); +// | Live variables at bb2[1]: [_1 (drop)] +// _2 = const can_panic() -> [return: bb3, unwind: bb4]; +// } +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs new file mode 100644 index 0000000000000..8dae773806718 --- /dev/null +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -0,0 +1,41 @@ +// Copyright 2012-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. + +// compile-flags:-Znll + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut x = 22; + loop { + // Key point: `x` not live on entry to this basic block. + x = 55; + if use_x(x) { break; } + } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | Live variables on entry to bb2: [] +// bb2: { +// | Live variables at bb2[0]: [] +// _1 = const 55usize; +// | Live variables at bb2[1]: [_1] +// StorageLive(_3); +// | Live variables at bb2[2]: [_1] +// StorageLive(_4); +// | Live variables at bb2[3]: [_1] +// _4 = _1; +// | Live variables at bb2[4]: [_4] +// _3 = const use_x(move _4) -> [return: bb3, unwind: bb1]; +// } +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs new file mode 100644 index 0000000000000..5d799d3d90b41 --- /dev/null +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -0,0 +1,48 @@ +// Copyright 2012-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. + +// compile-flags:-Znll + +fn cond() -> bool { false } + +fn make_live(_: usize) { } + +fn make_dead() { } + +fn main() { + let x = 5; + + if cond() { + make_live(x); + } else { + // x should be dead on entry to this block + make_dead(); + } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | Live variables on entry to bb3: [_1] +// bb3: { +// | Live variables at bb3[0]: [_1] +// StorageLive(_4); +// | Live variables at bb3[1]: [_1] +// _4 = _1; +// | Live variables at bb3[2]: [_4] +// _3 = const make_live(move _4) -> [return: bb5, unwind: bb1]; +// } +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// | Live variables on entry to bb4: [] +// bb4: { +// | Live variables at bb4[0]: [] +// _5 = const make_dead() -> [return: bb6, unwind: bb1]; +// } +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs new file mode 100644 index 0000000000000..7039de727faa9 --- /dev/null +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -0,0 +1,34 @@ +// Copyright 2012-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. + +// Basic test for named lifetime translation. Check that we +// instantiate the types that appear in function arguments with +// suitable variables and that we setup the outlives relationship +// between R0 and R1 properly. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information +// ignore-tidy-linelength + +#![allow(warnings)] + +fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true } + +fn main() { +} + +// END RUST SOURCE +// START rustc.use_x.nll.0.mir +// | '_#0r: {bb0[0], bb0[1], '_#0r} +// | '_#1r: {bb0[0], bb0[1], '_#1r} +// | '_#2r: {bb0[0], bb0[1], '_#2r} +// | '_#3r: {bb0[0], bb0[1], '_#3r} +// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { +// END rustc.use_x.nll.0.mir diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs new file mode 100644 index 0000000000000..f51e839e4fc35 --- /dev/null +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -0,0 +1,39 @@ +// Copyright 2012-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. + +// Basic test for reborrow constraints: the region (`R5`) that appears +// in the type of `r_a` must outlive the region (`R7`) that appears in +// the type of `r_b` + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: &mut i32) -> bool { true } + +fn main() { + let mut foo: i32 = 22; + let r_a: &mut i32 = &mut foo; + let r_b: &mut i32 = &mut *r_a; + use_x(r_b); +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// ... +// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]} +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// let _2: &'_#6r mut i32; +// ... +// let _4: &'_#8r mut i32; +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs new file mode 100644 index 0000000000000..cfbc51f9e1861 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -0,0 +1,56 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p = &v[0]; + if true { + use_x(*p); + } else { + use_x(22); + } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r: {bb2[1], bb3[0], bb3[1]} +// ... +// let _2: &'_#2r usize; +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// bb2: { +// | Live variables at bb2[0]: [_1, _3] +// _2 = &'_#1r _1[_3]; +// | Live variables at bb2[1]: [_2] +// switchInt(const true) -> [0u8: bb4, otherwise: bb3]; +// } +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// bb3: { +// | Live variables at bb3[0]: [_2] +// StorageLive(_7); +// | Live variables at bb3[1]: [_2] +// _7 = (*_2); +// | Live variables at bb3[2]: [_7] +// _6 = const use_x(move _7) -> [return: bb5, unwind: bb1]; +// } +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs new file mode 100644 index 0000000000000..04a30dc284d77 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -0,0 +1,48 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] +#![feature(dropck_eyepatch)] +#![feature(generic_param_attrs)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] }; + if true { + use_x(*p.value); + } else { + use_x(22); + } + + // `p` will get dropped here. However, because of the + // `#[may_dangle]` attribute, we do not need to consider R4 live. +} + +struct Wrap { + value: T +} + +unsafe impl<#[may_dangle] T> Drop for Wrap { + fn drop(&mut self) { } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs new file mode 100644 index 0000000000000..5569fe7f5748c --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -0,0 +1,50 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// ignore-tidy-linelength +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] }; + if true { + use_x(*p.value); + } else { + use_x(22); + } + + // `p` will get dropped here. Because the `#[may_dangle]` + // attribute is not present on `Wrap`, we must conservatively + // assume that the dtor may access the `value` field, and hence we + // must consider R1 to be live. +} + +struct Wrap { + value: T +} + +// Look ma, no `#[may_dangle]` attribute here. +impl Drop for Wrap { + fn drop(&mut self) { } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs new file mode 100644 index 0000000000000..679f31fdab903 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -0,0 +1,49 @@ +// Copyright 2012-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. + +// Test for the subregion constraints. In this case, the region R3 on +// `p` includes two disjoint regions of the control-flow graph. The +// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3, +// but only at a particular point, and hence they wind up including +// distinct regions. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let mut p = &v[0]; + if true { + use_x(*p); + } else { + use_x(22); + } + + p = &v[1]; + use_x(*p); +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]} +// ... +// | '_#3r: {bb8[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r: {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// ... +// let mut _2: &'_#4r usize; +// ... +// _2 = &'_#1r _1[_3]; +// ... +// _2 = &'_#3r (*_10); +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs new file mode 100644 index 0000000000000..471d77aefac62 --- /dev/null +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -0,0 +1,49 @@ +// Copyright 2012-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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// compile-flags:-Znll -Zverbose +// ^^^^^^^^^ force compiler to dump more region information + +#![allow(warnings)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p = &v[0]; + let q = p; + if true { + use_x(*q); + } else { + use_x(22); + } +} + +// END RUST SOURCE +// START rustc.main.nll.0.mir +// | '_#1r: {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#2r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#3r: {bb2[5], bb2[6], bb3[0], bb3[1]} +// END rustc.main.nll.0.mir +// START rustc.main.nll.0.mir +// let _2: &'_#2r usize; +// ... +// let _6: &'_#3r usize; +// ... +// _2 = &'_#1r _1[_3]; +// ... +// _7 = _2; +// ... +// _6 = move _7; +// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs new file mode 100644 index 0000000000000..1b114419448f1 --- /dev/null +++ b/src/test/mir-opt/packed-struct-drop-aligned.rs @@ -0,0 +1,70 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-wasm32-bare compiled with panic=abort by default + +fn main() { + let mut x = Packed(Aligned(Droppy(0))); + x.0 = Aligned(Droppy(0)); +} + +struct Aligned(Droppy); +#[repr(packed)] +struct Packed(Aligned); + +struct Droppy(usize); +impl Drop for Droppy { + fn drop(&mut self) {} +} + +// END RUST SOURCE +// START rustc.main.EraseRegions.before.mir +// fn main() -> () { +// let mut _0: (); +// scope 1 { +// let mut _1: Packed; +// } +// scope 2 { +// } +// let mut _2: Aligned; +// let mut _3: Droppy; +// let mut _4: Aligned; +// let mut _5: Droppy; +// let mut _6: Aligned; +// +// bb0: { +// StorageLive(_1); +// ... +// _1 = Packed::{{constructor}}(move _2,); +// ... +// StorageLive(_6); +// _6 = move (_1.0: Aligned); +// drop(_6) -> [return: bb4, unwind: bb3]; +// } +// bb1: { +// resume; +// } +// bb2: { +// StorageDead(_1); +// return; +// } +// bb3: { +// (_1.0: Aligned) = move _4; +// drop(_1) -> bb1; +// } +// bb4: { +// StorageDead(_6); +// (_1.0: Aligned) = move _4; +// StorageDead(_4); +// _0 = (); +// drop(_1) -> [return: bb2, unwind: bb1]; +// } +// } +// END rustc.main.EraseRegions.before.mir diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index cff108246a550..35786643648eb 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -15,13 +15,13 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.SimplifyBranches-initial.before.mir +// START rustc.main.SimplifyBranches-initial.before.mir // bb0: { -// switchInt(const false) -> [0u8: bb2, otherwise: bb1]; +// switchInt(const false) -> [0u8: bb3, otherwise: bb2]; // } -// END rustc.node4.SimplifyBranches-initial.before.mir -// START rustc.node4.SimplifyBranches-initial.after.mir +// END rustc.main.SimplifyBranches-initial.before.mir +// START rustc.main.SimplifyBranches-initial.after.mir // bb0: { -// goto -> bb2; +// goto -> bb3; // } -// END rustc.node4.SimplifyBranches-initial.after.mir +// END rustc.main.SimplifyBranches-initial.after.mir diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 9fb725a980e8d..730ef655b13d0 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -44,57 +44,157 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.mir_map.0.mir +// START rustc.XXX.mir_map.0.mir +// let mut _0: &'static Foo; +// let mut _1: &'static Foo; +// let mut _2: Foo; +// let mut _3: &'static [(u32, u32)]; +// let mut _4: &'static [(u32, u32); 42]; +// let mut _5: &'static [(u32, u32); 42]; +// let mut _6: [(u32, u32); 42]; +// let mut _7: (u32, u32); +// let mut _8: (u32, u32); +// let mut _9: (u32, u32); +// let mut _10: (u32, u32); +// let mut _11: (u32, u32); +// let mut _12: (u32, u32); +// let mut _13: (u32, u32); +// let mut _14: (u32, u32); +// let mut _15: (u32, u32); +// let mut _16: (u32, u32); +// let mut _17: (u32, u32); +// let mut _18: (u32, u32); +// let mut _19: (u32, u32); +// let mut _20: (u32, u32); +// let mut _21: (u32, u32); +// let mut _22: (u32, u32); +// let mut _23: (u32, u32); +// let mut _24: (u32, u32); +// let mut _25: (u32, u32); +// let mut _26: (u32, u32); +// let mut _27: (u32, u32); +// let mut _28: (u32, u32); +// let mut _29: (u32, u32); +// let mut _30: (u32, u32); +// let mut _31: (u32, u32); +// let mut _32: (u32, u32); +// let mut _33: (u32, u32); +// let mut _34: (u32, u32); +// let mut _35: (u32, u32); +// let mut _36: (u32, u32); +// let mut _37: (u32, u32); +// let mut _38: (u32, u32); +// let mut _39: (u32, u32); +// let mut _40: (u32, u32); +// let mut _41: (u32, u32); +// let mut _42: (u32, u32); +// let mut _43: (u32, u32); +// let mut _44: (u32, u32); +// let mut _45: (u32, u32); +// let mut _46: (u32, u32); +// let mut _47: (u32, u32); +// let mut _48: (u32, u32); // bb0: { -// _7 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:9: 29:15 -// _8 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:17: 29:23 -// _9 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:25: 29:31 -// _10 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:9: 30:15 -// _11 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:17: 30:23 -// _12 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:25: 30:31 -// _13 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:9: 31:15 -// _14 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:17: 31:23 -// _15 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:25: 31:31 -// _16 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:9: 32:15 -// _17 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:17: 32:23 -// _18 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:25: 32:31 -// _19 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:9: 33:15 -// _20 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:17: 33:23 -// _21 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:25: 33:31 -// _22 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:9: 34:15 -// _23 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:17: 34:23 -// _24 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:25: 34:31 -// _25 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:9: 35:15 -// _26 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:17: 35:23 -// _27 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:25: 35:31 -// _28 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:9: 36:15 -// _29 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:17: 36:23 -// _30 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:25: 36:31 -// _31 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:9: 37:15 -// _32 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:17: 37:23 -// _33 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:25: 37:31 -// _34 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:9: 38:15 -// _35 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:17: 38:23 -// _36 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:25: 38:31 -// _37 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:9: 39:15 -// _38 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:17: 39:23 -// _39 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:25: 39:31 -// _40 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:9: 40:15 -// _41 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:17: 40:23 -// _42 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:25: 40:31 -// _43 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:9: 41:15 -// _44 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:17: 41:23 -// _45 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:25: 41:31 -// _46 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:9: 42:15 -// _47 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:17: 42:23 -// _48 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:25: 42:31 -// _6 = [_7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48]; // scope 0 at src/test/mir-opt/basic_assignment.rs:28:12: 43:6 -// _5 = &_6; // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _4 = &(*_5); // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _3 = _4 as &'static [(u32, u32)] (Unsize); // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _2 = Foo { tup: const "hi", data: _3 }; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:29: 44:2 -// _1 = &_2; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2 -// _0 = &(*_1); // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2 -// return; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:1: 44:3 +// StorageLive(_1); +// StorageLive(_2); +// StorageLive(_3); +// StorageLive(_4); +// StorageLive(_5); +// StorageLive(_6); +// StorageLive(_7); +// _7 = (const 0u32, const 1u32); +// StorageLive(_8); +// _8 = (const 0u32, const 2u32); +// StorageLive(_9); +// _9 = (const 0u32, const 3u32); +// StorageLive(_10); +// _10 = (const 0u32, const 1u32); +// StorageLive(_11); +// _11 = (const 0u32, const 2u32); +// StorageLive(_12); +// _12 = (const 0u32, const 3u32); +// StorageLive(_13); +// _13 = (const 0u32, const 1u32); +// StorageLive(_14); +// _14 = (const 0u32, const 2u32); +// StorageLive(_15); +// _15 = (const 0u32, const 3u32); +// StorageLive(_16); +// _16 = (const 0u32, const 1u32); +// StorageLive(_17); +// _17 = (const 0u32, const 2u32); +// StorageLive(_18); +// _18 = (const 0u32, const 3u32); +// StorageLive(_19); +// _19 = (const 0u32, const 1u32); +// StorageLive(_20); +// _20 = (const 0u32, const 2u32); +// StorageLive(_21); +// _21 = (const 0u32, const 3u32); +// StorageLive(_22); +// _22 = (const 0u32, const 1u32); +// StorageLive(_23); +// _23 = (const 0u32, const 2u32); +// StorageLive(_24); +// _24 = (const 0u32, const 3u32); +// StorageLive(_25); +// _25 = (const 0u32, const 1u32); +// StorageLive(_26); +// _26 = (const 0u32, const 2u32); +// StorageLive(_27); +// _27 = (const 0u32, const 3u32); +// StorageLive(_28); +// _28 = (const 0u32, const 1u32); +// StorageLive(_29); +// _29 = (const 0u32, const 2u32); +// StorageLive(_30); +// _30 = (const 0u32, const 3u32); +// StorageLive(_31); +// _31 = (const 0u32, const 1u32); +// StorageLive(_32); +// _32 = (const 0u32, const 2u32); +// StorageLive(_33); +// _33 = (const 0u32, const 3u32); +// StorageLive(_34); +// _34 = (const 0u32, const 1u32); +// StorageLive(_35); +// _35 = (const 0u32, const 2u32); +// StorageLive(_36); +// _36 = (const 0u32, const 3u32); +// StorageLive(_37); +// _37 = (const 0u32, const 1u32); +// StorageLive(_38); +// _38 = (const 0u32, const 2u32); +// StorageLive(_39); +// _39 = (const 0u32, const 3u32); +// StorageLive(_40); +// _40 = (const 0u32, const 1u32); +// StorageLive(_41); +// _41 = (const 0u32, const 2u32); +// StorageLive(_42); +// _42 = (const 0u32, const 3u32); +// StorageLive(_43); +// _43 = (const 0u32, const 1u32); +// StorageLive(_44); +// _44 = (const 0u32, const 2u32); +// StorageLive(_45); +// _45 = (const 0u32, const 3u32); +// StorageLive(_46); +// _46 = (const 0u32, const 1u32); +// StorageLive(_47); +// _47 = (const 0u32, const 2u32); +// StorageLive(_48); +// _48 = (const 0u32, const 3u32); +// _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; +// _5 = &_6; +// _4 = &(*_5); +// _3 = move _4 as &'static [(u32, u32)] (Unsize); +// _2 = Foo { tup: const "hi", data: move _3 }; +// _1 = &_2; +// _0 = &(*_1); +// StorageDead(_1); +// StorageDead(_5); +// return; // } -// END rustc.node4.mir_map.0.mir +//} +// END rustc.XXX.mir_map.0.mir diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index 3fbd1a36f2f16..41eaf67d292a6 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -19,7 +19,7 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.TypeckMir.before.mir +// START rustc.main.TypeckMir.before.mir // bb0: { // StorageLive(_1); // _1 = const 0i32; @@ -27,7 +27,7 @@ fn main() { // StorageLive(_4); // StorageLive(_5); // _5 = _1; -// _4 = std::option::Option::Some(_5,); +// _4 = std::option::Option::Some(move _5,); // StorageDead(_5); // _3 = &_4; // _2 = (); @@ -38,5 +38,6 @@ fn main() { // _0 = (); // StorageDead(_6); // StorageDead(_1); +// return; // } -// END rustc.node4.TypeckMir.before.mir +// END rustc.main.TypeckMir.before.mir diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs index ec044225b83e8..e6cd535500055 100644 --- a/src/test/mir-opt/validate_1.rs +++ b/src/test/mir-opt/validate_1.rs @@ -28,15 +28,18 @@ fn main() { } // END RUST SOURCE -// START rustc.node12.EraseRegions.after.mir +// START rustc.{{impl}}-foo.EraseRegions.after.mir // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId(0/0:5 ~ validate_1[317d]::{{impl}}[0]::foo[0]), BrAnon(0)) Test, _2: &ReFree(DefId(0/0:5 ~ validate_1[317d]::{{impl}}[0]::foo[0]), BrAnon(1)) mut i32]); +// ... // return; // } -// END rustc.node12.EraseRegions.after.mir -// START rustc.node23.EraseRegions.after.mir +// END rustc.{{impl}}-foo.EraseRegions.after.mir +// START rustc.main.EraseRegions.after.mir // fn main() -> () { +// ... // bb0: { +// ... // Validate(Suspend(ReScope(Node(ItemLocalId(10)))), [_1: i32]); // _6 = &ReErased mut _1; // Validate(Acquire, [(*_6): i32/ReScope(Node(ItemLocalId(10)))]); @@ -44,34 +47,33 @@ fn main() { // _5 = &ReErased mut (*_6); // Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(10)))]); // Validate(Release, [_2: (), _3: &ReScope(Node(ItemLocalId(10))) Test, _5: &ReScope(Node(ItemLocalId(10))) mut i32]); -// _2 = const Test::foo(_3, _5) -> bb1; +// _2 = const Test::foo(move _3, move _5) -> bb1; // } // // bb1: { // Validate(Acquire, [_2: ()]); // EndRegion(ReScope(Node(ItemLocalId(10)))); +// ... // return; // } // } -// END rustc.node23.EraseRegions.after.mir -// START rustc.node50.EraseRegions.after.mir +// END rustc.main.EraseRegions.after.mir +// START rustc.main-{{closure}}.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(50)], _2: &ReErased mut i32) -> i32 { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:11) => validate_1/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:11) => validate_1/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId(0/1:11 ~ validate_1[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId(0/1:11 ~ validate_1[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]); // StorageLive(_3); -// _3 = _2; +// Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))), [(*_2): i32]); +// _3 = &ReErased (*_2); +// Validate(Acquire, [(*_3): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 })) (imm)]); // StorageLive(_4); -// Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))), [(*_3): i32]); -// _4 = &ReErased (*_3); -// Validate(Acquire, [(*_4): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 })) (imm)]); -// StorageLive(_5); -// _5 = (*_4); -// _0 = _5; -// StorageDead(_5); -// EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))); +// _4 = (*_3); +// _0 = move _4; // StorageDead(_4); +// EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))); // StorageDead(_3); // return; // } // } -// END rustc.node50.EraseRegions.after.mir +// END rustc.main-{{closure}}.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_2.rs b/src/test/mir-opt/validate_2.rs index 37ebd720d52da..3776a11b3ab82 100644 --- a/src/test/mir-opt/validate_2.rs +++ b/src/test/mir-opt/validate_2.rs @@ -9,6 +9,8 @@ // except according to those terms. // ignore-tidy-linelength +// ignore-wasm32-bare unwinding being disabled causes differences in output +// ignore-wasm64-bare unwinding being disabled causes differences in output // compile-flags: -Z verbose -Z mir-emit-validate=1 fn main() { @@ -16,12 +18,20 @@ fn main() { } // END RUST SOURCE -// START rustc.node4.EraseRegions.after.mir +// START rustc.main.EraseRegions.after.mir // fn main() -> () { +// ... // bb1: { +// Validate(Acquire, [_2: std::boxed::Box<[i32; 3]>]); // Validate(Release, [_2: std::boxed::Box<[i32; 3]>]); -// _1 = _2 as std::boxed::Box<[i32]> (Unsize); +// _1 = move _2 as std::boxed::Box<[i32]> (Unsize); // Validate(Acquire, [_1: std::boxed::Box<[i32]>]); +// StorageDead(_2); +// StorageDead(_3); +// _0 = (); +// Validate(Release, [_1: std::boxed::Box<[i32]>]); +// drop(_1) -> [return: bb2, unwind: bb3]; // } +// ... // } -// END rustc.node4.EraseRegions.after.mir +// END rustc.main.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_3.rs b/src/test/mir-opt/validate_3.rs index 116e35b2d6f26..80e75fcee8ac4 100644 --- a/src/test/mir-opt/validate_3.rs +++ b/src/test/mir-opt/validate_3.rs @@ -28,10 +28,19 @@ fn main() { } // END RUST SOURCE -// START rustc.node16.EraseRegions.after.mir +// START rustc.main.EraseRegions.after.mir // fn main() -> () { +// ... // let mut _5: &ReErased i32; // bb0: { +// StorageLive(_1); +// _1 = Test { x: const 0i32 }; +// StorageLive(_2); +// Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 }))), [_1: Test]); +// _2 = &ReErased _1; +// Validate(Acquire, [(*_2): Test/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]); +// StorageLive(_4); +// StorageLive(_5); // Validate(Suspend(ReScope(Node(ItemLocalId(17)))), [((*_2).0: i32): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]); // _5 = &ReErased ((*_2).0: i32); // Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(17))) (imm)]); @@ -39,12 +48,18 @@ fn main() { // _4 = &ReErased (*_5); // Validate(Acquire, [(*_4): i32/ReScope(Node(ItemLocalId(17))) (imm)]); // Validate(Release, [_3: (), _4: &ReScope(Node(ItemLocalId(17))) i32]); -// _3 = const foo(_4) -> bb1; +// _3 = const foo(move _4) -> bb1; // } // bb1: { +// Validate(Acquire, [_3: ()]); // EndRegion(ReScope(Node(ItemLocalId(17)))); +// StorageDead(_4); +// StorageDead(_5); +// _0 = (); // EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 }))); +// StorageDead(_2); +// StorageDead(_1); // return; // } // } -// END rustc.node16.EraseRegions.after.mir +// END rustc.main.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs index 571ed4254023a..24a4ebd8429df 100644 --- a/src/test/mir-opt/validate_4.rs +++ b/src/test/mir-opt/validate_4.rs @@ -36,48 +36,55 @@ fn main() { // contain name of the source file, so we cannot test for it. // END RUST SOURCE -// START rustc.node4.EraseRegions.after.mir +// START rustc.write_42.EraseRegions.after.mir // fn write_42(_1: *mut i32) -> bool { +// ... // bb0: { // Validate(Acquire, [_1: *mut i32]); // Validate(Release, [_1: *mut i32]); +// ... // return; // } // } -// END rustc.node4.EraseRegions.after.mir -// START rustc.node22.EraseRegions.after.mir +// END rustc.write_42.EraseRegions.after.mir +// START rustc.write_42-{{closure}}.EraseRegions.after.mir // fn write_42::{{closure}}(_1: &ReErased [closure@NodeId(22)], _2: *mut i32) -> () { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_4/8cd878b::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_4/8cd878b::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); -// StorageLive(_3); -// _3 = _2; -// (*_3) = const 23i32; -// StorageDead(_3); +// Validate(Acquire, [_1: &ReFree(DefId(0/1:9 ~ validate_4[317d]::write_42[0]::{{closure}}[0]), BrEnv) [closure@NodeId(22)], _2: *mut i32]); +// Validate(Release, [_1: &ReFree(DefId(0/1:9 ~ validate_4[317d]::write_42[0]::{{closure}}[0]), BrEnv) [closure@NodeId(22)], _2: *mut i32]); +// (*_2) = const 23i32; +// _0 = (); // return; // } // } -// END rustc.node22.EraseRegions.after.mir -// START rustc.node31.EraseRegions.after.mir +// END rustc.write_42-{{closure}}.EraseRegions.after.mir +// START rustc.test.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]); -// _3 = const write_42(_4) -> bb1; +// Validate(Acquire, [_1: &ReFree(DefId(0/0:4 ~ validate_4[317d]::test[0]), BrAnon(0)) mut i32]); +// Validate(Release, [_1: &ReFree(DefId(0/0:4 ~ validate_4[317d]::test[0]), BrAnon(0)) mut i32]); +// ... +// _2 = const write_42(move _3) -> bb1; // } // bb1: { -// Validate(Acquire, [_3: bool]); -// Validate(Release, [_3: bool]); +// Validate(Acquire, [_2: bool]); +// Validate(Release, [_2: bool]); +// ... // } // } -// END rustc.node31.EraseRegions.after.mir -// START rustc.node60.EraseRegions.after.mir +// END rustc.test.EraseRegions.after.mir +// START rustc.main-{{closure}}.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(60)], _2: &ReErased mut i32) -> bool { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId(0/1:10 ~ validate_4[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId(0/1:10 ~ validate_4[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]); +// Validate(Release, [_1: &ReFree(DefId(0/1:10 ~ validate_4[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId(0/1:10 ~ validate_4[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]); // StorageLive(_3); -// _0 = const write_42(_4) -> bb1; +// ... +// _0 = const write_42(move _3) -> bb1; // } +// ... // } -// END rustc.node60.EraseRegions.after.mir +// END rustc.main-{{closure}}.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs index ff0c781d1e3bb..c9408c1f2f88b 100644 --- a/src/test/mir-opt/validate_5.rs +++ b/src/test/mir-opt/validate_5.rs @@ -33,31 +33,34 @@ fn main() { } // END RUST SOURCE -// START rustc.node17.EraseRegions.after.mir +// START rustc.test.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_5/8cd878b::test[0] }, BrAnon(0)) mut i32]); -// Validate(Release, [_3: bool, _4: *mut i32]); -// _3 = const write_42(_4) -> bb1; +// Validate(Acquire, [_1: &ReFree(DefId(0/0:4 ~ validate_5[317d]::test[0]), BrAnon(0)) mut i32]); +// ... +// Validate(Release, [_2: bool, _3: *mut i32]); +// _2 = const write_42(move _3) -> bb1; // } +// ... // } -// END rustc.node17.EraseRegions.after.mir -// START rustc.node46.EraseRegions.after.mir +// END rustc.test.EraseRegions.after.mir +// START rustc.main-{{closure}}.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(46)], _2: &ReErased mut i32) -> bool { +// ... // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_5/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_5/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId(0/1:9 ~ validate_5[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId(0/1:9 ~ validate_5[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]); // StorageLive(_3); -// _3 = _2; // StorageLive(_4); -// StorageLive(_5); -// Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_3): i32]); -// _5 = &ReErased mut (*_3); -// Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(9)))]); -// _4 = _5 as *mut i32 (Misc); +// Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_2): i32]); +// _4 = &ReErased mut (*_2); +// Validate(Acquire, [(*_4): i32/ReScope(Node(ItemLocalId(9)))]); +// _3 = move _4 as *mut i32 (Misc); // EndRegion(ReScope(Node(ItemLocalId(9)))); -// StorageDead(_5); -// Validate(Release, [_0: bool, _4: *mut i32]); -// _0 = const write_42(_4) -> bb1; +// StorageDead(_4); +// Validate(Release, [_0: bool, _3: *mut i32]); +// _0 = const write_42(move _3) -> bb1; // } +// ... // } -// END rustc.node46.EraseRegions.after.mir +// END rustc.main-{{closure}}.EraseRegions.after.mir diff --git a/src/test/parse-fail/doc-after-struct-field.rs b/src/test/parse-fail/doc-after-struct-field.rs index 1aa6af5b78f5b..a2c60151ac72d 100644 --- a/src/test/parse-fail/doc-after-struct-field.rs +++ b/src/test/parse-fail/doc-after-struct-field.rs @@ -9,12 +9,20 @@ // except according to those terms. // compile-flags: -Z continue-parse-after-error + struct X { a: u8 /** document a */, //~^ ERROR found a documentation comment that doesn't document anything //~| HELP maybe a comment was intended } +struct Y { + a: u8 /// document a + //~^ ERROR found a documentation comment that doesn't document anything + //~| HELP maybe a comment was intended +} + fn main() { - let y = X {a = 1}; + let x = X { a: 1 }; + let y = Y { a: 1 }; } diff --git a/src/test/parse-fail/doc-before-struct-rbrace-1.rs b/src/test/parse-fail/doc-before-struct-rbrace-1.rs index 5ba83190c8e50..6d9b4b05ad9fa 100644 --- a/src/test/parse-fail/doc-before-struct-rbrace-1.rs +++ b/src/test/parse-fail/doc-before-struct-rbrace-1.rs @@ -17,5 +17,5 @@ struct X { } fn main() { - let y = X {a = 1}; + let y = X {a: 1}; } diff --git a/src/test/parse-fail/doc-before-struct-rbrace-2.rs b/src/test/parse-fail/doc-before-struct-rbrace-2.rs index 643e4aa17a1ac..63b2f96379916 100644 --- a/src/test/parse-fail/doc-before-struct-rbrace-2.rs +++ b/src/test/parse-fail/doc-before-struct-rbrace-2.rs @@ -16,5 +16,5 @@ struct X { } fn main() { - let y = X {a = 1}; + let y = X {a: 1}; } diff --git a/src/test/parse-fail/issue-20711-2.rs b/src/test/parse-fail/issue-20711-2.rs index a489864e3f737..3a330d8bee21b 100644 --- a/src/test/parse-fail/issue-20711-2.rs +++ b/src/test/parse-fail/issue-20711-2.rs @@ -16,6 +16,6 @@ impl Foo { fn foo() {} #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` +} //~ ERROR expected one of `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/issue-20711.rs b/src/test/parse-fail/issue-20711.rs index d9789d55a6faf..cd79fa8be7c65 100644 --- a/src/test/parse-fail/issue-20711.rs +++ b/src/test/parse-fail/issue-20711.rs @@ -14,6 +14,6 @@ struct Foo; impl Foo { #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` +} //~ ERROR expected one of `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/issue-22647.rs b/src/test/parse-fail/issue-22647.rs index 1ace57edba3d8..3da9d1a8712ad 100644 --- a/src/test/parse-fail/issue-22647.rs +++ b/src/test/parse-fail/issue-22647.rs @@ -16,6 +16,7 @@ fn main() { println!("Y {}",x); return x; }; + //~^ ERROR expected item, found `;` caller(bar_handler); } diff --git a/src/test/parse-fail/issue-33413.rs b/src/test/parse-fail/issue-33413.rs index 699af8ca7ab4f..25ae7b4c55a2b 100644 --- a/src/test/parse-fail/issue-33413.rs +++ b/src/test/parse-fail/issue-33413.rs @@ -12,5 +12,4 @@ impl S { fn f(*, a: u8) -> u8 {} //~ ERROR expected pattern, found `*` - //~^ ERROR expected one of `)`, `-`, `box`, `false`, `mut`, `ref`, or `true`, found `*` } diff --git a/src/test/parse-fail/issue-37234.rs b/src/test/parse-fail/issue-37234.rs index 651e11d9d21b3..93a1468bf7b19 100644 --- a/src/test/parse-fail/issue-37234.rs +++ b/src/test/parse-fail/issue-37234.rs @@ -11,7 +11,7 @@ macro_rules! failed { () => {{ let x = 5 ""; //~ ERROR found `""` - }} //~ ERROR macro expansion ignores token `}` + }} } fn main() { diff --git a/src/test/parse-fail/mut-patterns.rs b/src/test/parse-fail/mut-patterns.rs index 71d826c67f8bd..ffb455975521a 100644 --- a/src/test/parse-fail/mut-patterns.rs +++ b/src/test/parse-fail/mut-patterns.rs @@ -15,4 +15,5 @@ pub fn main() { struct Foo { x: isize } let mut Foo { x: x } = Foo { x: 3 }; //~ ERROR: expected one of `:`, `;`, `=`, or `@`, found `{` + //~^ ERROR expected item, found `=` } diff --git a/src/test/parse-fail/pat-lt-bracket-5.rs b/src/test/parse-fail/pat-lt-bracket-5.rs index 3345845eee9ae..421d7a05befff 100644 --- a/src/test/parse-fail/pat-lt-bracket-5.rs +++ b/src/test/parse-fail/pat-lt-bracket-5.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let v[0] = v[1]; //~ error: expected one of `:`, `;`, `=`, or `@`, found `[` + let v[0] = v[1]; //~ ERROR expected one of `:`, `;`, `=`, or `@`, found `[` } diff --git a/src/test/parse-fail/range_inclusive_dotdotdot.rs b/src/test/parse-fail/range_inclusive_dotdotdot.rs new file mode 100644 index 0000000000000..a4c36a2f0ba8d --- /dev/null +++ b/src/test/parse-fail/range_inclusive_dotdotdot.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only -Z continue-parse-after-error + +// Make sure that inclusive ranges with `...` syntax don't parse. + +#![feature(inclusive_range_syntax, inclusive_range)] + +use std::ops::RangeToInclusive; + +fn return_range_to() -> RangeToInclusive { + return ...1; //~ERROR `...` syntax cannot be used in expressions + //~^HELP Use `..` if you need an exclusive range (a < b) + //~^^HELP or `..=` if you need an inclusive range (a <= b) +} + +pub fn main() { + let x = ...0; //~ERROR `...` syntax cannot be used in expressions + //~^HELP Use `..` if you need an exclusive range (a < b) + //~^^HELP or `..=` if you need an inclusive range (a <= b) + + let x = 5...5; //~ERROR `...` syntax cannot be used in expressions + //~^HELP Use `..` if you need an exclusive range (a < b) + //~^^HELP or `..=` if you need an inclusive range (a <= b) + + for _ in 0...1 {} //~ERROR `...` syntax cannot be used in expressions + //~^HELP Use `..` if you need an exclusive range (a < b) + //~^^HELP or `..=` if you need an inclusive range (a <= b) +} + diff --git a/src/test/parse-fail/removed-syntax-static-fn.rs b/src/test/parse-fail/removed-syntax-static-fn.rs index b4c25a75c9086..3b783aa79e281 100644 --- a/src/test/parse-fail/removed-syntax-static-fn.rs +++ b/src/test/parse-fail/removed-syntax-static-fn.rs @@ -15,4 +15,4 @@ struct S; impl S { static fn f() {} } -//~^^ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}` +//~^^ ERROR expected one of `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe` diff --git a/src/test/parse-fail/require-parens-for-chained-comparison.rs b/src/test/parse-fail/require-parens-for-chained-comparison.rs index 7e76dbd31f0a3..1ee6996ce9c84 100644 --- a/src/test/parse-fail/require-parens-for-chained-comparison.rs +++ b/src/test/parse-fail/require-parens-for-chained-comparison.rs @@ -21,5 +21,6 @@ fn main() { f(); //~^ ERROR: chained comparison operators require parentheses - //~^^ HELP: use `::<...>` instead of `<...>` + //~| HELP: use `::<...>` instead of `<...>` + //~| HELP: or use `(...)` } diff --git a/src/test/parse-fail/trait-object-bad-parens.rs b/src/test/parse-fail/trait-object-bad-parens.rs index a44c0c3f32fef..3e8c140eb197d 100644 --- a/src/test/parse-fail/trait-object-bad-parens.rs +++ b/src/test/parse-fail/trait-object-bad-parens.rs @@ -17,4 +17,6 @@ fn main() { //~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)` let _: Box<(Copy +) + Copy>; //~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)` + let _: Box<(dyn Copy) + Copy>; + //~^ ERROR expected a path on the left-hand side of `+`, not `(dyn Copy)` } diff --git a/src/test/pretty/default-trait-impl.rs b/src/test/pretty/auto-trait.rs similarity index 90% rename from src/test/pretty/default-trait-impl.rs rename to src/test/pretty/auto-trait.rs index a5246b9300c91..842af49e8a7f9 100644 --- a/src/test/pretty/default-trait-impl.rs +++ b/src/test/pretty/auto-trait.rs @@ -12,8 +12,8 @@ // pp-exact -trait MyTrait { } +auto trait MyTrait { } -impl MyTrait for .. { } +unsafe auto trait UnsafeMyTrait { } pub fn main() { } diff --git a/src/test/pretty/cast-lt.pp b/src/test/pretty/cast-lt.pp new file mode 100644 index 0000000000000..b21158abfe551 --- /dev/null +++ b/src/test/pretty/cast-lt.pp @@ -0,0 +1,24 @@ +#![feature(prelude_import)] +#![no_std] +#[prelude_import] +use std::prelude::v1::*; +#[macro_use] +extern crate std as std; +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:cast-lt.pp + +macro_rules! negative(( $ e : expr ) => { $ e < 0 }); + +fn main() { (1 as i32) < 0; } + diff --git a/src/test/pretty/cast-lt.rs b/src/test/pretty/cast-lt.rs new file mode 100644 index 0000000000000..87b5274545f38 --- /dev/null +++ b/src/test/pretty/cast-lt.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:cast-lt.pp + +macro_rules! negative { + ($e:expr) => { $e < 0 } +} + +fn main() { + negative!(1 as i32); +} + diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 14a499644df8b..02b8425d88bee 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -40,31 +40,31 @@ ((::fmt::format as - fn(std::fmt::Arguments<'_>) -> std::string::String {std::fmt::format})(((<::std::fmt::Arguments>::new_v1 - as - fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test" - as - &'static str)] - as - [&str; 1]) - as - &[&str; 1]), - (&(match (() - as - ()) - { - () - => - ([] - as - [std::fmt::ArgumentV1<'_>; 0]), - } - as - [std::fmt::ArgumentV1<'_>; 0]) - as - &[std::fmt::ArgumentV1<'_>; 0])) - as - std::fmt::Arguments<'_>)) + for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::std::fmt::Arguments>::new_v1 + as + fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test" + as + &'static str)] + as + [&str; 1]) + as + &[&str; 1]), + (&(match (() + as + ()) + { + () + => + ([] + as + [std::fmt::ArgumentV1<'_>; 0]), + } + as + [std::fmt::ArgumentV1<'_>; 0]) + as + &[std::fmt::ArgumentV1<'_>; 0])) + as + std::fmt::Arguments<'_>)) as std::string::String); } as ()) pub type Foo = [i32; (3 as usize)]; diff --git a/src/test/run-fail/binop-fail-3.rs b/src/test/run-fail/binop-fail-3.rs index 5be9cd4a9bc34..efbc49fbece92 100644 --- a/src/test/run-fail/binop-fail-3.rs +++ b/src/test/run-fail/binop-fail-3.rs @@ -12,6 +12,8 @@ fn foo() -> ! { panic!("quux"); } + +#[allow(resolve_trait_on_defaulted_unit)] fn main() { foo() == foo(); // these types wind up being defaulted to () } diff --git a/src/test/parse-fail/keyword-crate-as-identifier.rs b/src/test/run-fail/borrowck-local-borrow.rs similarity index 72% rename from src/test/parse-fail/keyword-crate-as-identifier.rs rename to src/test/run-fail/borrowck-local-borrow.rs index 8a914ca7b178c..daee3903d770d 100644 --- a/src/test/parse-fail/keyword-crate-as-identifier.rs +++ b/src/test/run-fail/borrowck-local-borrow.rs @@ -7,11 +7,13 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +// error-pattern:panic 1 -// compile-flags: -Z parse-only - -// This file was auto-generated using 'src/etc/generate-keyword-tests.py crate' +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir fn main() { - let crate = "foo"; //~ error: expected pattern, found keyword `crate` + let x = 2; + let y = &x; + panic!("panic 1"); } diff --git a/src/test/run-fail/mir_drop_panics.rs b/src/test/run-fail/mir_drop_panics.rs index 98311525ad0f2..51191dd7087e4 100644 --- a/src/test/run-fail/mir_drop_panics.rs +++ b/src/test/run-fail/mir_drop_panics.rs @@ -10,7 +10,6 @@ // error-pattern:panic 1 // error-pattern:drop 2 -use std::io::{self, Write}; struct Droppable(u32); impl Drop for Droppable { @@ -18,7 +17,7 @@ impl Drop for Droppable { if self.0 == 1 { panic!("panic 1"); } else { - write!(io::stderr(), "drop {}", self.0); + eprint!("drop {}", self.0); } } } diff --git a/src/test/run-fail/mir_dynamic_drops_1.rs b/src/test/run-fail/mir_dynamic_drops_1.rs index 6cf2851d93d47..69f934272b75c 100644 --- a/src/test/run-fail/mir_dynamic_drops_1.rs +++ b/src/test/run-fail/mir_dynamic_drops_1.rs @@ -9,7 +9,6 @@ // except according to those terms. // error-pattern:drop 1 // error-pattern:drop 2 -use std::io::{self, Write}; /// Structure which will not allow to be dropped twice. @@ -17,10 +16,10 @@ struct Droppable<'a>(&'a mut bool, u32); impl<'a> Drop for Droppable<'a> { fn drop(&mut self) { if *self.0 { - writeln!(io::stderr(), "{} dropped twice", self.1); + eprintln!("{} dropped twice", self.1); ::std::process::exit(1); } - writeln!(io::stderr(), "drop {}", self.1); + eprintln!("drop {}", self.1); *self.0 = true; } } diff --git a/src/test/run-fail/mir_dynamic_drops_2.rs b/src/test/run-fail/mir_dynamic_drops_2.rs index 7a90298e42253..d2fe50401ab8d 100644 --- a/src/test/run-fail/mir_dynamic_drops_2.rs +++ b/src/test/run-fail/mir_dynamic_drops_2.rs @@ -9,7 +9,6 @@ // except according to those terms. // error-pattern:drop 1 -use std::io::{self, Write}; /// Structure which will not allow to be dropped twice. @@ -17,10 +16,10 @@ struct Droppable<'a>(&'a mut bool, u32); impl<'a> Drop for Droppable<'a> { fn drop(&mut self) { if *self.0 { - writeln!(io::stderr(), "{} dropped twice", self.1); + eprintln!("{} dropped twice", self.1); ::std::process::exit(1); } - writeln!(io::stderr(), "drop {}", self.1); + eprintln!("drop {}", self.1); *self.0 = true; } } diff --git a/src/test/run-fail/mir_dynamic_drops_3.rs b/src/test/run-fail/mir_dynamic_drops_3.rs index 79ecbbb35bc56..ecc35ee9b2409 100644 --- a/src/test/run-fail/mir_dynamic_drops_3.rs +++ b/src/test/run-fail/mir_dynamic_drops_3.rs @@ -12,7 +12,6 @@ // error-pattern:drop 3 // error-pattern:drop 2 // error-pattern:drop 1 -use std::io::{self, Write}; /// Structure which will not allow to be dropped twice. @@ -20,10 +19,10 @@ struct Droppable<'a>(&'a mut bool, u32); impl<'a> Drop for Droppable<'a> { fn drop(&mut self) { if *self.0 { - writeln!(io::stderr(), "{} dropped twice", self.1); + eprintln!("{} dropped twice", self.1); ::std::process::exit(1); } - writeln!(io::stderr(), "drop {}", self.1); + eprintln!("drop {}", self.1); *self.0 = true; } } diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs index 7a7526c5fc1d3..9c851eb7346bb 100644 --- a/src/test/run-fail/mir_trans_calls_converging_drops.rs +++ b/src/test/run-fail/mir_trans_calls_converging_drops.rs @@ -12,17 +12,15 @@ // error-pattern:0 dropped // error-pattern:exit -use std::io::{self, Write}; - struct Droppable(u8); impl Drop for Droppable { fn drop(&mut self) { - write!(io::stderr(), "{} dropped\n", self.0); + eprintln!("{} dropped", self.0); } } fn converging_fn() { - write!(io::stderr(), "converging_fn called\n"); + eprintln!("converging_fn called"); } fn mir(d: Droppable) { diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs index 1301630cc85ea..6f10521155648 100644 --- a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs +++ b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs @@ -12,18 +12,16 @@ // error-pattern:dropped // error-pattern:exit -use std::io::{self, Write}; - struct Droppable; impl Drop for Droppable { fn drop(&mut self) { - write!(io::stderr(), "dropped\n"); + eprintln!("dropped"); } } // return value of this function is copied into the return slot fn complex() -> u64 { - write!(io::stderr(), "complex called\n"); + eprintln!("complex called"); 42 } diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs index c191870492969..f8fbe8f79cc62 100644 --- a/src/test/run-fail/mir_trans_calls_diverging_drops.rs +++ b/src/test/run-fail/mir_trans_calls_diverging_drops.rs @@ -11,12 +11,10 @@ // error-pattern:diverging_fn called // error-pattern:0 dropped -use std::io::{self, Write}; - struct Droppable(u8); impl Drop for Droppable { fn drop(&mut self) { - write!(io::stderr(), "{} dropped", self.0); + eprintln!("{} dropped", self.0); } } diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs index dacb039d89dc5..bafb78fc213e3 100644 --- a/src/test/run-fail/mir_trans_no_landing_pads.rs +++ b/src/test/run-fail/mir_trans_no_landing_pads.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z no-landing-pads +// compile-flags: -Z no-landing-pads -C codegen-units=1 // error-pattern:converging_fn called use std::io::{self, Write}; diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs index 87037c1efed9e..998ee7470bbe9 100644 --- a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs +++ b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z no-landing-pads +// compile-flags: -Z no-landing-pads -C codegen-units=1 // error-pattern:diverging_fn called use std::io::{self, Write}; diff --git a/src/test/run-fail/panic-set-handler.rs b/src/test/run-fail/panic-set-handler.rs index b589544ae1563..68f1c4ed0bced 100644 --- a/src/test/run-fail/panic-set-handler.rs +++ b/src/test/run-fail/panic-set-handler.rs @@ -13,11 +13,10 @@ #![feature(panic_handler)] use std::panic; -use std::io::{self, Write}; fn main() { panic::set_hook(Box::new(|i| { - write!(io::stderr(), "greetings from the panic handler"); + eprint!("greetings from the panic handler"); })); panic!("foobar"); } diff --git a/src/test/run-fail/panic-set-unset-handler.rs b/src/test/run-fail/panic-set-unset-handler.rs index 6741c2d9c2c20..072139a8c9b80 100644 --- a/src/test/run-fail/panic-set-unset-handler.rs +++ b/src/test/run-fail/panic-set-unset-handler.rs @@ -13,11 +13,10 @@ #![feature(panic_handler)] use std::panic; -use std::io::{self, Write}; fn main() { panic::set_hook(Box::new(|i| { - write!(io::stderr(), "greetings from the panic handler"); + eprint!("greetings from the panic handler"); })); panic::take_hook(); panic!("foobar"); diff --git a/src/test/run-make/archive-duplicate-names/Makefile b/src/test/run-make/archive-duplicate-names/Makefile index 5202e6dea541e..93711c41d79f8 100644 --- a/src/test/run-make/archive-duplicate-names/Makefile +++ b/src/test/run-make/archive-duplicate-names/Makefile @@ -5,7 +5,7 @@ all: mkdir $(TMPDIR)/b $(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c) $(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c) - ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o + $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o $(RUSTC) foo.rs $(RUSTC) bar.rs $(call RUN,bar) diff --git a/src/test/run-make/atomic-lock-free/Makefile b/src/test/run-make/atomic-lock-free/Makefile index 7c92adddbafdd..4849b0307423d 100644 --- a/src/test/run-make/atomic-lock-free/Makefile +++ b/src/test/run-make/atomic-lock-free/Makefile @@ -7,36 +7,36 @@ all: ifeq ($(UNAME),Linux) ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86) $(RUSTC) --target=i686-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=x86_64-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif ifeq ($(filter arm,$(LLVM_COMPONENTS)),arm) $(RUSTC) --target=arm-unknown-linux-gnueabi atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=arm-unknown-linux-gnueabihf atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=armv7-unknown-linux-gnueabihf atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif ifeq ($(filter aarch64,$(LLVM_COMPONENTS)),aarch64) $(RUSTC) --target=aarch64-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif ifeq ($(filter mips,$(LLVM_COMPONENTS)),mips) $(RUSTC) --target=mips-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=mipsel-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif ifeq ($(filter powerpc,$(LLVM_COMPONENTS)),powerpc) $(RUSTC) --target=powerpc-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=powerpc64-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add $(RUSTC) --target=s390x-unknown-linux-gnu atomic_lock_free.rs - nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + nm "$(TMPDIR)/libatomic_lock_free.rlib" | $(CGREP) -v __atomic_fetch_add endif endif diff --git a/src/test/run-make/cat-and-grep-sanity-check/Makefile b/src/test/run-make/cat-and-grep-sanity-check/Makefile new file mode 100644 index 0000000000000..fead197ce3906 --- /dev/null +++ b/src/test/run-make/cat-and-grep-sanity-check/Makefile @@ -0,0 +1,46 @@ +-include ../tools.mk + +all: + echo a | $(CGREP) a + ! echo b | $(CGREP) a + echo xyz | $(CGREP) x y z + ! echo abc | $(CGREP) b c d + printf "x\ny\nz" | $(CGREP) x y z + + echo AbCd | $(CGREP) -i a b C D + ! echo AbCd | $(CGREP) a b C D + + true | $(CGREP) -v nothing + ! echo nothing | $(CGREP) -v nothing + ! echo xyz | $(CGREP) -v w x y + ! echo xyz | $(CGREP) -v x y z + echo xyz | $(CGREP) -v a b c + + ! echo 'foo bar baz' | $(CGREP) 'foo baz' + echo 'foo bar baz' | $(CGREP) foo baz + echo 'x a `b` c y z' | $(CGREP) 'a `b` c' + + echo baaac | $(CGREP) -e 'ba*c' + echo bc | $(CGREP) -e 'ba*c' + ! echo aaac | $(CGREP) -e 'ba*c' + + echo aaa | $(CGREP) -e 'a+' + ! echo bbb | $(CGREP) -e 'a+' + + echo abc | $(CGREP) -e 'a|e|i|o|u' + ! echo fgh | $(CGREP) -e 'a|e|i|o|u' + echo abc | $(CGREP) -e '[aeiou]' + ! echo fgh | $(CGREP) -e '[aeiou]' + ! echo abc | $(CGREP) -e '[^aeiou]{3}' + echo fgh | $(CGREP) -e '[^aeiou]{3}' + echo ab cd ef gh | $(CGREP) -e '\bcd\b' + ! echo abcdefgh | $(CGREP) -e '\bcd\b' + echo xyz | $(CGREP) -e '...' + ! echo xy | $(CGREP) -e '...' + ! echo xyz | $(CGREP) -e '\.\.\.' + echo ... | $(CGREP) -e '\.\.\.' + + echo foo bar baz | $(CGREP) -e 'foo.*baz' + ! echo foo bar baz | $(CGREP) -ve 'foo.*baz' + ! echo foo bar baz | $(CGREP) -e 'baz.*foo' + echo foo bar baz | $(CGREP) -ve 'baz.*foo' diff --git a/src/test/run-make/cdylib-fewer-symbols/Makefile b/src/test/run-make/cdylib-fewer-symbols/Makefile new file mode 100644 index 0000000000000..1a0664dfafd7e --- /dev/null +++ b/src/test/run-make/cdylib-fewer-symbols/Makefile @@ -0,0 +1,15 @@ +# Test that allocator-related symbols don't show up as exported from a cdylib as +# they're internal to Rust and not part of the public ABI. + +-include ../tools.mk + +# FIXME: The __rdl_ and __rust_ symbol still remains, no matter using MSVC or GNU +# See https://github.com/rust-lang/rust/pull/46207#issuecomment-347561753 +ifdef IS_WINDOWS +all: + true +else +all: + $(RUSTC) foo.rs + nm -g "$(call DYLIB,foo)" | $(CGREP) -v __rdl_ __rde_ __rg_ __rust_ +endif diff --git a/src/test/run-make/cdylib-fewer-symbols/foo.rs b/src/test/run-make/cdylib-fewer-symbols/foo.rs new file mode 100644 index 0000000000000..4ec8d4ee86079 --- /dev/null +++ b/src/test/run-make/cdylib-fewer-symbols/foo.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "cdylib"] + +#[no_mangle] +pub extern fn foo() -> u32 { + 3 +} diff --git a/src/test/run-make/codegen-options-parsing/Makefile b/src/test/run-make/codegen-options-parsing/Makefile index dc46a8a04ef8c..81e06043c87ae 100644 --- a/src/test/run-make/codegen-options-parsing/Makefile +++ b/src/test/run-make/codegen-options-parsing/Makefile @@ -3,29 +3,29 @@ all: #Option taking a number $(RUSTC) -C codegen-units dummy.rs 2>&1 | \ - grep 'codegen option `codegen-units` requires a number' + $(CGREP) 'codegen option `codegen-units` requires a number' $(RUSTC) -C codegen-units= dummy.rs 2>&1 | \ - grep 'incorrect value `` for codegen option `codegen-units` - a number was expected' + $(CGREP) 'incorrect value `` for codegen option `codegen-units` - a number was expected' $(RUSTC) -C codegen-units=foo dummy.rs 2>&1 | \ - grep 'incorrect value `foo` for codegen option `codegen-units` - a number was expected' + $(CGREP) 'incorrect value `foo` for codegen option `codegen-units` - a number was expected' $(RUSTC) -C codegen-units=1 dummy.rs #Option taking a string $(RUSTC) -C extra-filename dummy.rs 2>&1 | \ - grep 'codegen option `extra-filename` requires a string' + $(CGREP) 'codegen option `extra-filename` requires a string' $(RUSTC) -C extra-filename= dummy.rs 2>&1 $(RUSTC) -C extra-filename=foo dummy.rs 2>&1 #Option taking no argument $(RUSTC) -C lto= dummy.rs 2>&1 | \ - grep 'codegen option `lto` takes no value' + $(CGREP) 'codegen option `lto` takes no value' $(RUSTC) -C lto=1 dummy.rs 2>&1 | \ - grep 'codegen option `lto` takes no value' + $(CGREP) 'codegen option `lto` takes no value' $(RUSTC) -C lto=foo dummy.rs 2>&1 | \ - grep 'codegen option `lto` takes no value' + $(CGREP) 'codegen option `lto` takes no value' $(RUSTC) -C lto dummy.rs # Should not link dead code... $(RUSTC) -Z print-link-args dummy.rs 2>&1 | \ - grep -e '--gc-sections' -e '-z[^ ]* [^ ]*\' -e '-dead_strip' -e '/OPT:REF' + $(CGREP) -e '--gc-sections|-z[^ ]* [^ ]*|-dead_strip|/OPT:REF' # ... unless you specifically ask to keep it $(RUSTC) -Z print-link-args -C link-dead-code dummy.rs 2>&1 | \ - (! grep -e '--gc-sections' -e '-z[^ ]* [^ ]*\' -e '-dead_strip' -e '/OPT:REF') + $(CGREP) -ve '--gc-sections|-z[^ ]* [^ ]*|-dead_strip|/OPT:REF' diff --git a/src/test/run-make/compiler-rt-works-on-mingw/Makefile b/src/test/run-make/compiler-rt-works-on-mingw/Makefile index 4ec54f73e67a5..06d1bb6698ece 100644 --- a/src/test/run-make/compiler-rt-works-on-mingw/Makefile +++ b/src/test/run-make/compiler-rt-works-on-mingw/Makefile @@ -3,8 +3,8 @@ ifneq (,$(findstring MINGW,$(UNAME))) ifndef IS_MSVC all: - g++ foo.cpp -c -o $(TMPDIR)/foo.o - ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o + $(CXX) foo.cpp -c -o $(TMPDIR)/foo.o + $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o $(RUSTC) foo.rs -lfoo -lstdc++ $(call RUN,foo) else diff --git a/src/test/run-make/error-found-staticlib-instead-crate/Makefile b/src/test/run-make/error-found-staticlib-instead-crate/Makefile index 24ff20ea89242..fef12c4da6703 100644 --- a/src/test/run-make/error-found-staticlib-instead-crate/Makefile +++ b/src/test/run-make/error-found-staticlib-instead-crate/Makefile @@ -2,4 +2,4 @@ all: $(RUSTC) foo.rs --crate-type staticlib - $(RUSTC) bar.rs 2>&1 | grep "found staticlib" + $(RUSTC) bar.rs 2>&1 | $(CGREP) "found staticlib" diff --git a/src/test/run-make/error-writing-dependencies/Makefile b/src/test/run-make/error-writing-dependencies/Makefile index 89fbfa0a1bf9d..cbc96901a388a 100644 --- a/src/test/run-make/error-writing-dependencies/Makefile +++ b/src/test/run-make/error-writing-dependencies/Makefile @@ -3,6 +3,6 @@ all: # Let's get a nice error message $(BARE_RUSTC) foo.rs --emit dep-info --out-dir foo/bar/baz 2>&1 | \ - grep "error writing dependencies" + $(CGREP) "error writing dependencies" # Make sure the filename shows up - $(BARE_RUSTC) foo.rs --emit dep-info --out-dir foo/bar/baz 2>&1 | grep "baz" + $(BARE_RUSTC) foo.rs --emit dep-info --out-dir foo/bar/baz 2>&1 | $(CGREP) "baz" diff --git a/src/test/run-make/extern-fn-reachable/main.rs b/src/test/run-make/extern-fn-reachable/main.rs index a1bd1041d145e..27387332c1c1f 100644 --- a/src/test/run-make/extern-fn-reachable/main.rs +++ b/src/test/run-make/extern-fn-reachable/main.rs @@ -10,9 +10,9 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate rustc_metadata; -use rustc_back::dynamic_lib::DynamicLibrary; +use rustc_metadata::dynamic_lib::DynamicLibrary; use std::path::Path; pub fn main() { diff --git a/src/test/run-make/extern-fn-with-extern-types/Makefile b/src/test/run-make/extern-fn-with-extern-types/Makefile new file mode 100644 index 0000000000000..8977e14c3ad1a --- /dev/null +++ b/src/test/run-make/extern-fn-with-extern-types/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: $(call NATIVE_STATICLIB,ctest) + $(RUSTC) test.rs + $(call RUN,test) || exit 1 diff --git a/src/test/run-make/extern-fn-with-extern-types/ctest.c b/src/test/run-make/extern-fn-with-extern-types/ctest.c new file mode 100644 index 0000000000000..c3d6166fb1284 --- /dev/null +++ b/src/test/run-make/extern-fn-with-extern-types/ctest.c @@ -0,0 +1,17 @@ +// ignore-license +#include +#include + +typedef struct data { + uint32_t magic; +} data; + +data* data_create(uint32_t magic) { + static data d; + d.magic = magic; + return &d; +} + +uint32_t data_get(data* p) { + return p->magic; +} diff --git a/src/test/run-make/extern-fn-with-extern-types/test.rs b/src/test/run-make/extern-fn-with-extern-types/test.rs new file mode 100644 index 0000000000000..9d6c87885b16e --- /dev/null +++ b/src/test/run-make/extern-fn-with-extern-types/test.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(extern_types)] + +#[link(name = "ctest", kind = "static")] +extern { + type data; + + fn data_create(magic: u32) -> *mut data; + fn data_get(data: *mut data) -> u32; +} + +const MAGIC: u32 = 0xdeadbeef; +fn main() { + unsafe { + let data = data_create(MAGIC); + assert_eq!(data_get(data), MAGIC); + } +} diff --git a/src/test/run-make/extra-filename-with-temp-outputs/Makefile b/src/test/run-make/extra-filename-with-temp-outputs/Makefile index d33c18a6f3c2c..6de4f97df0c16 100644 --- a/src/test/run-make/extra-filename-with-temp-outputs/Makefile +++ b/src/test/run-make/extra-filename-with-temp-outputs/Makefile @@ -2,5 +2,5 @@ all: $(RUSTC) -C extra-filename=bar foo.rs -C save-temps - rm $(TMPDIR)/foobar.0.o + rm $(TMPDIR)/foobar.foo0.rcgu.o rm $(TMPDIR)/$(call BIN,foobar) diff --git a/src/test/run-make/hir-tree/Makefile b/src/test/run-make/hir-tree/Makefile new file mode 100644 index 0000000000000..bedb2b7d6aa50 --- /dev/null +++ b/src/test/run-make/hir-tree/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +# Test that hir-tree output doens't crash and includes +# the string constant we would expect to see. + +all: + $(RUSTC) -o $(TMPDIR)/input.hir -Z unstable-options \ + --unpretty=hir-tree input.rs + $(CGREP) '"Hello, Rustaceans!\n"' < $(TMPDIR)/input.hir diff --git a/src/test/run-pass/issue-30276.rs b/src/test/run-make/hir-tree/input.rs similarity index 77% rename from src/test/run-pass/issue-30276.rs rename to src/test/run-make/hir-tree/input.rs index 5dd0cd8ba5313..12adc083bcd10 100644 --- a/src/test/run-pass/issue-30276.rs +++ b/src/test/run-make/hir-tree/input.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Test([i32]); fn main() { - let _x: fn(_) -> Test = Test; + println!("Hello, Rustaceans!"); } diff --git a/src/test/run-make/include_bytes_deps/Makefile b/src/test/run-make/include_bytes_deps/Makefile index 0400db412dd29..f7b1d21ace281 100644 --- a/src/test/run-make/include_bytes_deps/Makefile +++ b/src/test/run-make/include_bytes_deps/Makefile @@ -8,8 +8,7 @@ ifneq ($(shell uname),FreeBSD) ifndef IS_WINDOWS all: $(RUSTC) --emit dep-info main.rs - grep "input.txt" $(TMPDIR)/main.d - grep "input.bin" $(TMPDIR)/main.d + $(CGREP) "input.txt" "input.bin" < $(TMPDIR)/main.d else all: diff --git a/src/test/run-make/inline-always-many-cgu/Makefile b/src/test/run-make/inline-always-many-cgu/Makefile new file mode 100644 index 0000000000000..0cab955f6442b --- /dev/null +++ b/src/test/run-make/inline-always-many-cgu/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 + if cat $(TMPDIR)/*.ll | $(CGREP) -e '\bcall\b'; then \ + echo "found call instruction when one wasn't expected"; \ + exit 1; \ + fi diff --git a/src/test/run-make/inline-always-many-cgu/foo.rs b/src/test/run-make/inline-always-many-cgu/foo.rs new file mode 100644 index 0000000000000..539dcdfa9b30b --- /dev/null +++ b/src/test/run-make/inline-always-many-cgu/foo.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub mod a { + #[inline(always)] + pub fn foo() { + } + + pub fn bar() { + } +} + +#[no_mangle] +pub fn bar() { + a::foo(); +} diff --git a/src/test/run-make/intrinsic-unreachable/exit-ret.rs b/src/test/run-make/intrinsic-unreachable/exit-ret.rs index f5be5a055c3e0..1b8b644dd78e8 100644 --- a/src/test/run-make/intrinsic-unreachable/exit-ret.rs +++ b/src/test/run-make/intrinsic-unreachable/exit-ret.rs @@ -11,10 +11,15 @@ #![feature(asm)] #![crate_type="lib"] -pub fn exit(n: usize) { +#[deny(unreachable_code)] +pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. asm!("" :: "r"(n) :: "volatile"); // Can't actually reach this point, but rustc doesn't know that. } + // This return value is just here to generate some extra code for a return + // value, making it easier for the test script to detect whether the + // compiler deleted it. + 42 } diff --git a/src/test/run-make/intrinsic-unreachable/exit-unreachable.rs b/src/test/run-make/intrinsic-unreachable/exit-unreachable.rs index f58d2cd8f91d8..de63809ab6638 100644 --- a/src/test/run-make/intrinsic-unreachable/exit-unreachable.rs +++ b/src/test/run-make/intrinsic-unreachable/exit-unreachable.rs @@ -13,10 +13,15 @@ use std::intrinsics; -pub fn exit(n: usize) -> ! { +#[allow(unreachable_code)] +pub fn exit(n: usize) -> i32 { unsafe { // Pretend this asm is an exit() syscall. asm!("" :: "r"(n) :: "volatile"); intrinsics::unreachable() } + // This return value is just here to generate some extra code for a return + // value, making it easier for the test script to detect whether the + // compiler deleted it. + 42 } diff --git a/src/test/run-make/invalid-library/Makefile b/src/test/run-make/invalid-library/Makefile index 0dbe655b77dfd..b6fb122d98bf2 100644 --- a/src/test/run-make/invalid-library/Makefile +++ b/src/test/run-make/invalid-library/Makefile @@ -2,5 +2,5 @@ all: touch $(TMPDIR)/rust.metadata.bin - ar crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin - $(RUSTC) foo.rs 2>&1 | grep "can't find crate for" + $(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin + $(RUSTC) foo.rs 2>&1 | $(CGREP) "can't find crate for" diff --git a/src/test/run-make/invalid-staticlib/Makefile b/src/test/run-make/invalid-staticlib/Makefile index d4aa6d5e72098..3a91902ccceb6 100644 --- a/src/test/run-make/invalid-staticlib/Makefile +++ b/src/test/run-make/invalid-staticlib/Makefile @@ -2,4 +2,4 @@ all: touch $(TMPDIR)/libfoo.a - echo | $(RUSTC) - --crate-type=rlib -lstatic=foo 2>&1 | grep "failed to add native library" + echo | $(RUSTC) - --crate-type=rlib -lstatic=foo 2>&1 | $(CGREP) "failed to add native library" diff --git a/src/test/run-make/issue-14698/Makefile b/src/test/run-make/issue-14698/Makefile index 28502f67e0747..dbe8317dbc4bc 100644 --- a/src/test/run-make/issue-14698/Makefile +++ b/src/test/run-make/issue-14698/Makefile @@ -1,4 +1,4 @@ -include ../tools.mk all: - TMP=fake TMPDIR=fake $(RUSTC) foo.rs 2>&1 | grep "couldn't create a temp dir:" + TMP=fake TMPDIR=fake $(RUSTC) foo.rs 2>&1 | $(CGREP) "couldn't create a temp dir:" diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs index 461df49b468f9..4db027aaeef71 100644 --- a/src/test/run-make/issue-19371/foo.rs +++ b/src/test/run-make/issue-19371/foo.rs @@ -18,7 +18,6 @@ extern crate rustc_errors; extern crate rustc_trans; extern crate syntax; -use rustc::dep_graph::DepGraph; use rustc::session::{build_session, Session}; use rustc::session::config::{basic_options, build_configuration, Input, OutputType, OutputTypes}; @@ -56,6 +55,9 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc) { let mut opts = basic_options(); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); opts.maybe_sysroot = Some(sysroot); + if let Ok(linker) = std::env::var("RUSTC_LINKER") { + opts.cg.linker = Some(linker); + } let descriptions = Registry::new(&rustc::DIAGNOSTICS); let cstore = Rc::new(CStore::new(Box::new(rustc_trans::LlvmMetadataLoader))); @@ -67,8 +69,7 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc) { fn compile(code: String, output: PathBuf, sysroot: PathBuf) { let (sess, cstore) = basic_sess(sysroot); - let cfg = build_configuration(&sess, HashSet::new()); let control = CompileController::basic(); let input = Input::Str { name: anon_src(), input: code }; - compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control); + let _ = compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control); } diff --git a/src/test/run-make/issue-22131/Makefile b/src/test/run-make/issue-22131/Makefile index 64af91b487b32..6db737a9e72a2 100644 --- a/src/test/run-make/issue-22131/Makefile +++ b/src/test/run-make/issue-22131/Makefile @@ -2,6 +2,6 @@ all: foo.rs $(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs - $(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \ + $(RUSTDOC) --test --cfg 'feature="bar"' \ -L $(TMPDIR) foo.rs |\ - grep -q 'foo.rs - foo (line 11) ... ok' + $(CGREP) 'foo.rs - foo (line 11) ... ok' diff --git a/src/test/run-make/issue-25581/test.c b/src/test/run-make/issue-25581/test.c index ab85d2bb13fb1..5736b1730216d 100644 --- a/src/test/run-make/issue-25581/test.c +++ b/src/test/run-make/issue-25581/test.c @@ -2,10 +2,15 @@ #include #include -size_t slice_len(uint8_t *data, size_t len) { - return len; +struct ByteSlice { + uint8_t *data; + size_t len; +}; + +size_t slice_len(struct ByteSlice bs) { + return bs.len; } -uint8_t slice_elem(uint8_t *data, size_t len, size_t idx) { - return data[idx]; +uint8_t slice_elem(struct ByteSlice bs, size_t idx) { + return bs.data[idx]; } diff --git a/src/test/run-make/issue-26092/Makefile b/src/test/run-make/issue-26092/Makefile index 0d94c99a3948a..27631c31c4a06 100644 --- a/src/test/run-make/issue-26092/Makefile +++ b/src/test/run-make/issue-26092/Makefile @@ -1,5 +1,4 @@ -include ../tools.mk all: - $(RUSTC) -o "" blank.rs 2>&1 | \ - grep -i 'No such file or directory' + $(RUSTC) -o "" blank.rs 2>&1 | $(CGREP) -i 'No such file or directory' diff --git a/src/test/run-make/issue-33329/Makefile b/src/test/run-make/issue-33329/Makefile index c53f51d5bf584..591e4e3dda344 100644 --- a/src/test/run-make/issue-33329/Makefile +++ b/src/test/run-make/issue-33329/Makefile @@ -1,5 +1,5 @@ -include ../tools.mk all: - $(RUSTC) --target x86_64_unknown-linux-musl main.rs 2>&1 | \ - grep "error: Error loading target specification: Could not find specification for target" + $(RUSTC) --target x86_64_unknown-linux-musl main.rs 2>&1 | $(CGREP) \ + "error: Error loading target specification: Could not find specification for target" diff --git a/src/test/run-make/issue-35164/Makefile b/src/test/run-make/issue-35164/Makefile index c7bc26e3f5a9e..6a451656dcb7f 100644 --- a/src/test/run-make/issue-35164/Makefile +++ b/src/test/run-make/issue-35164/Makefile @@ -1,4 +1,4 @@ -include ../tools.mk all: - $(RUSTC) main.rs --error-format json 2>&1 | grep -q '"byte_start":490.*"byte_end":496' + $(RUSTC) main.rs --error-format json 2>&1 | $(CGREP) -e '"byte_start":490\b' '"byte_end":496\b' diff --git a/src/test/run-make/issue-40535/Makefile b/src/test/run-make/issue-40535/Makefile index 7d513a86a7fa5..49db1d43e4718 100644 --- a/src/test/run-make/issue-40535/Makefile +++ b/src/test/run-make/issue-40535/Makefile @@ -1,11 +1,13 @@ +-include ../tools.mk + # The ICE occurred in the following situation: # * `foo` declares `extern crate bar, baz`, depends only on `bar` (forgetting `baz` in `Cargo.toml`) # * `bar` declares and depends on `extern crate baz` # * All crates built in metadata-only mode (`cargo check`) all: # cc https://github.com/rust-lang/rust/issues/40623 - $(RUSTC) baz.rs --emit=metadata --out-dir=$(TMPDIR) - $(RUSTC) bar.rs --emit=metadata --extern baz=$(TMPDIR)/libbaz.rmeta --out-dir=$(TMPDIR) - $(RUSTC) foo.rs --emit=metadata --extern bar=$(TMPDIR)/libbar.rmeta --out-dir=$(TMPDIR) 2>&1 | \ - grep -vq "unexpectedly panicked" + $(RUSTC) baz.rs --emit=metadata + $(RUSTC) bar.rs --emit=metadata --extern baz=$(TMPDIR)/libbaz.rmeta + $(RUSTC) foo.rs --emit=metadata --extern bar=$(TMPDIR)/libbar.rmeta 2>&1 | \ + $(CGREP) -v "unexpectedly panicked" # ^ Succeeds if it doesn't find the ICE message diff --git a/src/test/run-make/issue-46239/Makefile b/src/test/run-make/issue-46239/Makefile new file mode 100644 index 0000000000000..698a605f7e9ba --- /dev/null +++ b/src/test/run-make/issue-46239/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: + $(RUSTC) main.rs -C opt-level=1 + $(call RUN,main) diff --git a/src/test/run-make/issue-46239/main.rs b/src/test/run-make/issue-46239/main.rs new file mode 100644 index 0000000000000..3b3289168abe9 --- /dev/null +++ b/src/test/run-make/issue-46239/main.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn project(x: &(T,)) -> &T { &x.0 } + +fn dummy() {} + +fn main() { + let f = (dummy as fn(),); + (*project(&f))(); +} diff --git a/src/test/run-make/issue-7349/foo.rs b/src/test/run-make/issue-7349/foo.rs index 6c39b33be086e..b75c82afb53d0 100644 --- a/src/test/run-make/issue-7349/foo.rs +++ b/src/test/run-make/issue-7349/foo.rs @@ -13,6 +13,7 @@ fn outer() { fn inner() -> u32 { 8675309 } + inner(); } extern "C" fn outer_foreign() { @@ -20,6 +21,7 @@ extern "C" fn outer_foreign() { fn inner() -> u32 { 11235813 } + inner(); } fn main() { diff --git a/src/test/run-make/link-arg/Makefile b/src/test/run-make/link-arg/Makefile index 0ee239af0fa6c..d7c9fd2711285 100644 --- a/src/test/run-make/link-arg/Makefile +++ b/src/test/run-make/link-arg/Makefile @@ -2,4 +2,4 @@ RUSTC_FLAGS = -C link-arg="-lfoo" -C link-arg="-lbar" -Z print-link-args all: - $(RUSTC) $(RUSTC_FLAGS) empty.rs | grep lfoo | grep lbar + $(RUSTC) $(RUSTC_FLAGS) empty.rs | $(CGREP) lfoo lbar diff --git a/src/test/run-make/link-cfg/Makefile b/src/test/run-make/link-cfg/Makefile index 4abc0caa69864..188cba5fe4127 100644 --- a/src/test/run-make/link-cfg/Makefile +++ b/src/test/run-make/link-cfg/Makefile @@ -2,7 +2,7 @@ all: $(call DYLIB,return1) $(call DYLIB,return2) $(call NATIVE_STATICLIB,return3) ls $(TMPDIR) - $(RUSTC) --print cfg --target x86_64-unknown-linux-musl | grep crt-static + $(RUSTC) --print cfg --target x86_64-unknown-linux-musl | $(CGREP) crt-static $(RUSTC) no-deps.rs --cfg foo $(call RUN,no-deps) diff --git a/src/test/run-make/linker-output-non-utf8/Makefile b/src/test/run-make/linker-output-non-utf8/Makefile index 98fe83f45e40c..76d4b133defe2 100644 --- a/src/test/run-make/linker-output-non-utf8/Makefile +++ b/src/test/run-make/linker-output-non-utf8/Makefile @@ -19,6 +19,6 @@ all: $(RUSTC) library.rs mkdir $(bad_dir) mv $(TMPDIR)/liblibrary.a $(bad_dir) - LIBRARY_PATH=$(bad_dir) $(RUSTC) exec.rs 2>&1 | grep this_symbol_not_defined + LIBRARY_PATH=$(bad_dir) $(RUSTC) exec.rs 2>&1 | $(CGREP) this_symbol_not_defined endif diff --git a/src/test/run-make/llvm-phase/test.rs b/src/test/run-make/llvm-phase/test.rs index 7a63871f19e38..2ff4593a801fe 100644 --- a/src/test/run-make/llvm-phase/test.rs +++ b/src/test/run-make/llvm-phase/test.rs @@ -77,6 +77,7 @@ fn main() { .split(' ').map(|s| s.to_string()).collect(); args.push("--out-dir".to_string()); args.push(env::var("TMPDIR").unwrap()); + args.push("-Ccodegen-units=1".to_string()); let (result, _) = rustc_driver::run_compiler( &args, &mut JitCalls, Some(box JitLoader), None); diff --git a/src/test/run-make/many-crates-but-no-match/Makefile b/src/test/run-make/many-crates-but-no-match/Makefile index 239b689b52645..03a797d95f984 100644 --- a/src/test/run-make/many-crates-but-no-match/Makefile +++ b/src/test/run-make/many-crates-but-no-match/Makefile @@ -27,8 +27,10 @@ all: mv $(TMPDIR)/$(call RLIB_GLOB,crateA) $(A3) # Ensure crateC fails to compile since A1 is "missing" and A2/A3 hashes do not match $(RUSTC) -L $(A2) -L $(A3) crateC.rs >$(LOG) 2>&1 || true - grep "found possibly newer version of crate \`crateA\` which \`crateB\` depends on" $(LOG) - grep "note: perhaps that crate needs to be recompiled?" $(LOG) - grep "note: crate \`crateA\` path #1:" $(LOG) - grep "note: crate \`crateA\` path #2:" $(LOG) - grep "note: crate \`crateB\` path #1:" $(LOG) + $(CGREP) \ + 'found possibly newer version of crate `crateA` which `crateB` depends on' \ + 'note: perhaps that crate needs to be recompiled?' \ + 'crate `crateA`:' \ + 'crate `crateB`:' \ + < $(LOG) + # the 'crate `crateA`' will match two entries. \ No newline at end of file diff --git a/src/test/run-make/mismatching-target-triples/Makefile b/src/test/run-make/mismatching-target-triples/Makefile index e79abf822337c..1636e41b05652 100644 --- a/src/test/run-make/mismatching-target-triples/Makefile +++ b/src/test/run-make/mismatching-target-triples/Makefile @@ -8,4 +8,4 @@ all: $(RUSTC) foo.rs --target=i686-unknown-linux-gnu $(RUSTC) bar.rs --target=x86_64-unknown-linux-gnu 2>&1 \ - | grep "couldn't find crate .foo. with expected target triple x86_64-unknown-linux-gnu" + | $(CGREP) 'couldn'"'"'t find crate `foo` with expected target triple x86_64-unknown-linux-gnu' diff --git a/src/test/run-make/missing-crate-dependency/Makefile b/src/test/run-make/missing-crate-dependency/Makefile index 4275c9b3f9fa3..b5a5bf492abe5 100644 --- a/src/test/run-make/missing-crate-dependency/Makefile +++ b/src/test/run-make/missing-crate-dependency/Makefile @@ -6,4 +6,4 @@ all: $(call REMOVE_RLIBS,crateA) # Ensure crateC fails to compile since dependency crateA is missing $(RUSTC) crateC.rs 2>&1 | \ - grep "can't find crate for \`crateA\` which \`crateB\` depends on" + $(CGREP) 'can'"'"'t find crate for `crateA` which `crateB` depends on' diff --git a/src/test/run-make/msvc-opt-minsize/Makefile b/src/test/run-make/msvc-opt-minsize/Makefile new file mode 100644 index 0000000000000..1095a047dd178 --- /dev/null +++ b/src/test/run-make/msvc-opt-minsize/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs -Copt-level=z 2>&1 + $(call RUN,foo) diff --git a/src/test/run-make/msvc-opt-minsize/foo.rs b/src/test/run-make/msvc-opt-minsize/foo.rs new file mode 100644 index 0000000000000..30b12691afeda --- /dev/null +++ b/src/test/run-make/msvc-opt-minsize/foo.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(test)] +extern crate test; + +fn foo(x: i32, y: i32) -> i64 { + (x + y) as i64 +} + +#[inline(never)] +fn bar() { + let _f = Box::new(0); + // This call used to trigger an LLVM bug in opt-level z where the base + // pointer gets corrupted, see issue #45034 + let y: fn(i32, i32) -> i64 = test::black_box(foo); + test::black_box(y(1, 2)); +} + +fn main() { + bar(); +} diff --git a/src/test/run-make/no-builtins-lto/Makefile b/src/test/run-make/no-builtins-lto/Makefile index 3f70de5f76c3b..b9688f16c6464 100644 --- a/src/test/run-make/no-builtins-lto/Makefile +++ b/src/test/run-make/no-builtins-lto/Makefile @@ -6,4 +6,4 @@ all: # Build an executable that depends on that crate using LTO. The no_builtins crate doesn't # participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by # grepping the linker arguments. - $(RUSTC) main.rs -C lto -Z print-link-args | grep 'libno_builtins.rlib' + $(RUSTC) main.rs -C lto -Z print-link-args | $(CGREP) 'libno_builtins.rlib' diff --git a/src/test/run-make/print-cfg/Makefile b/src/test/run-make/print-cfg/Makefile index 82fa3f6a3c5e0..08303a46d1929 100644 --- a/src/test/run-make/print-cfg/Makefile +++ b/src/test/run-make/print-cfg/Makefile @@ -1,16 +1,16 @@ -include ../tools.mk all: default - $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | grep windows - $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | grep x86_64 - $(RUSTC) --target i686-pc-windows-msvc --print cfg | grep msvc - $(RUSTC) --target i686-apple-darwin --print cfg | grep macos - $(RUSTC) --target i686-unknown-linux-gnu --print cfg | grep gnu + $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) windows + $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) x86_64 + $(RUSTC) --target i686-pc-windows-msvc --print cfg | $(CGREP) msvc + $(RUSTC) --target i686-apple-darwin --print cfg | $(CGREP) macos + $(RUSTC) --target i686-unknown-linux-gnu --print cfg | $(CGREP) gnu ifdef IS_WINDOWS default: - $(RUSTC) --print cfg | grep windows + $(RUSTC) --print cfg | $(CGREP) windows else default: - $(RUSTC) --print cfg | grep unix + $(RUSTC) --print cfg | $(CGREP) unix endif diff --git a/src/test/run-make/rustc-macro-dep-files/Makefile b/src/test/run-make/rustc-macro-dep-files/Makefile index 1ab27397e3146..d2c8e7fd0434c 100644 --- a/src/test/run-make/rustc-macro-dep-files/Makefile +++ b/src/test/run-make/rustc-macro-dep-files/Makefile @@ -8,5 +8,5 @@ else all: $(RUSTC) foo.rs $(RUSTC) bar.rs --emit dep-info - grep "proc-macro source" $(TMPDIR)/bar.d && exit 1 || exit 0 + $(CGREP) -v "proc-macro source" < $(TMPDIR)/bar.d endif diff --git a/src/test/run-make/rustdoc-output-path/Makefile b/src/test/run-make/rustdoc-output-path/Makefile index 4e570718a62f9..8ce1c699526cd 100644 --- a/src/test/run-make/rustdoc-output-path/Makefile +++ b/src/test/run-make/rustdoc-output-path/Makefile @@ -1,4 +1,4 @@ -include ../tools.mk all: - $(HOST_RPATH_ENV) '$(RUSTDOC)' -o "$(TMPDIR)/foo/bar/doc" foo.rs + $(RUSTDOC) -o "$(TMPDIR)/foo/bar/doc" foo.rs diff --git a/src/test/run-make/sanitizer-address/Makefile b/src/test/run-make/sanitizer-address/Makefile index d0ac8903f10f5..207615bfbd5c6 100644 --- a/src/test/run-make/sanitizer-address/Makefile +++ b/src/test/run-make/sanitizer-address/Makefile @@ -24,7 +24,6 @@ endif all: ifeq ($(ASAN_SUPPORT),1) - $(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | grep -q librustc_asan - $(TMPDIR)/overflow 2>&1 | tee $(LOG) - grep -q stack-buffer-overflow $(LOG) + $(RUSTC) -g -Z sanitizer=address -Z print-link-args $(EXTRA_RUSTFLAG) overflow.rs | $(CGREP) librustc_asan + $(TMPDIR)/overflow 2>&1 | $(CGREP) stack-buffer-overflow endif diff --git a/src/test/run-make/sanitizer-cdylib-link/Makefile b/src/test/run-make/sanitizer-cdylib-link/Makefile index 0cc4334f17f4c..bea5519ec5f70 100644 --- a/src/test/run-make/sanitizer-cdylib-link/Makefile +++ b/src/test/run-make/sanitizer-cdylib-link/Makefile @@ -18,6 +18,5 @@ all: ifeq ($(ASAN_SUPPORT),1) $(RUSTC) -g -Z sanitizer=address --crate-type cdylib --target $(TARGET) $(EXTRA_RUSTFLAG) library.rs $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) $(EXTRA_RUSTFLAG) -llibrary program.rs - LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | tee $(LOG) - grep -q stack-buffer-overflow $(LOG) + LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow endif diff --git a/src/test/run-make/sanitizer-dylib-link/Makefile b/src/test/run-make/sanitizer-dylib-link/Makefile index cdf0b91c1efa7..0cc8f73da8b56 100644 --- a/src/test/run-make/sanitizer-dylib-link/Makefile +++ b/src/test/run-make/sanitizer-dylib-link/Makefile @@ -18,6 +18,5 @@ all: ifeq ($(ASAN_SUPPORT),1) $(RUSTC) -g -Z sanitizer=address --crate-type dylib --target $(TARGET) $(EXTRA_RUSTFLAG) library.rs $(RUSTC) -g -Z sanitizer=address --crate-type bin --target $(TARGET) $(EXTRA_RUSTFLAG) -llibrary program.rs - LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | tee $(LOG) - grep -q stack-buffer-overflow $(LOG) + LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow endif diff --git a/src/test/run-make/sanitizer-invalid-cratetype/Makefile b/src/test/run-make/sanitizer-invalid-cratetype/Makefile index d03bbf84c1d11..dc37c0d0bc946 100644 --- a/src/test/run-make/sanitizer-invalid-cratetype/Makefile +++ b/src/test/run-make/sanitizer-invalid-cratetype/Makefile @@ -14,5 +14,5 @@ endif all: ifeq ($(ASAN_SUPPORT),1) - $(RUSTC) -Z sanitizer=address --crate-type proc-macro --target $(TARGET) hello.rs 2>&1 | grep -q -- '-Z sanitizer' + $(RUSTC) -Z sanitizer=address --crate-type proc-macro --target $(TARGET) hello.rs 2>&1 | $(CGREP) '-Z sanitizer' endif diff --git a/src/test/run-make/sanitizer-invalid-target/Makefile b/src/test/run-make/sanitizer-invalid-target/Makefile index 82e32f0995202..df8afee15ce07 100644 --- a/src/test/run-make/sanitizer-invalid-target/Makefile +++ b/src/test/run-make/sanitizer-invalid-target/Makefile @@ -1,4 +1,5 @@ -include ../tools.mk all: - $(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | grep -q 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target' + $(RUSTC) -Z sanitizer=leak --target i686-unknown-linux-gnu hello.rs 2>&1 | \ + $(CGREP) 'LeakSanitizer only works with the `x86_64-unknown-linux-gnu` target' diff --git a/src/test/run-make/sanitizer-leak/Makefile b/src/test/run-make/sanitizer-leak/Makefile index b18dd1d45eda4..ab43fac2e99c3 100644 --- a/src/test/run-make/sanitizer-leak/Makefile +++ b/src/test/run-make/sanitizer-leak/Makefile @@ -1,10 +1,16 @@ -include ../tools.mk +# FIXME(#46126) ThinLTO for libstd broke this test +ifeq (1,0) all: ifeq ($(TARGET),x86_64-unknown-linux-gnu) ifdef SANITIZER_SUPPORT - $(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | grep -q librustc_lsan - $(TMPDIR)/leak 2>&1 | grep -q 'detected memory leaks' + $(RUSTC) -C opt-level=1 -g -Z sanitizer=leak -Z print-link-args leak.rs | $(CGREP) librustc_lsan + $(TMPDIR)/leak 2>&1 | $(CGREP) 'detected memory leaks' endif endif +else +all: +endif + diff --git a/src/test/run-make/sanitizer-memory/Makefile b/src/test/run-make/sanitizer-memory/Makefile index 7502ef0e7a7b7..3507ca2bef2a0 100644 --- a/src/test/run-make/sanitizer-memory/Makefile +++ b/src/test/run-make/sanitizer-memory/Makefile @@ -3,8 +3,8 @@ all: ifeq ($(TARGET),x86_64-unknown-linux-gnu) ifdef SANITIZER_SUPPORT - $(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | grep -q librustc_msan - $(TMPDIR)/uninit 2>&1 | grep -q use-of-uninitialized-value + $(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) librustc_msan + $(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value endif endif diff --git a/src/test/run-make/sanitizer-staticlib-link/Makefile b/src/test/run-make/sanitizer-staticlib-link/Makefile index f92dc52b44575..2b444d667bfa3 100644 --- a/src/test/run-make/sanitizer-staticlib-link/Makefile +++ b/src/test/run-make/sanitizer-staticlib-link/Makefile @@ -13,6 +13,6 @@ all: ifeq ($(ASAN_SUPPORT),1) $(RUSTC) -g -Z sanitizer=address --crate-type staticlib --target $(TARGET) library.rs $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) $(EXTRACFLAGS) $(EXTRACXXFLAGS) - LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | grep -q stack-buffer-overflow + LD_LIBRARY_PATH=$(TMPDIR) $(TMPDIR)/program 2>&1 | $(CGREP) stack-buffer-overflow endif diff --git a/src/test/run-make/save-analysis/foo.rs b/src/test/run-make/save-analysis/foo.rs index 5cb363ac34435..834a7554a555d 100644 --- a/src/test/run-make/save-analysis/foo.rs +++ b/src/test/run-make/save-analysis/foo.rs @@ -441,6 +441,11 @@ fn test_format_args() { print!("x is {}, y is {1}, name is {n}", x, y, n = name); } + +union TestUnion { + f1: u32 +} + struct FrameBuffer; struct SilenceGenerator; diff --git a/src/test/run-make/sepcomp-cci-copies/Makefile b/src/test/run-make/sepcomp-cci-copies/Makefile index 189088219d5b3..ccd4e1b0e715a 100644 --- a/src/test/run-make/sepcomp-cci-copies/Makefile +++ b/src/test/run-make/sepcomp-cci-copies/Makefile @@ -5,5 +5,6 @@ all: $(RUSTC) cci_lib.rs - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*cci_fn)" -eq "2" ] + $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 \ + -Z inline-in-all-cgus + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*cci_fn)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-inlining/Makefile b/src/test/run-make/sepcomp-inlining/Makefile index 720dfff2c0438..1d20d940000f4 100644 --- a/src/test/run-make/sepcomp-inlining/Makefile +++ b/src/test/run-make/sepcomp-inlining/Makefile @@ -7,8 +7,9 @@ # in only one compilation unit. all: - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "0" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ] + $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 \ + -Z inline-in-all-cgus + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ i32\ .*inlined)" -eq "0" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-separate/Makefile b/src/test/run-make/sepcomp-separate/Makefile index a475bdfd74a24..5b8bdb0fad8cd 100644 --- a/src/test/run-make/sepcomp-separate/Makefile +++ b/src/test/run-make/sepcomp-separate/Makefile @@ -6,4 +6,4 @@ all: $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*magic_fn)" -eq "3" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*magic_fn)" -eq "3" ] diff --git a/src/test/run-make/sepcomp-separate/foo.rs b/src/test/run-make/sepcomp-separate/foo.rs index bfa2162e27dd2..64a76e9e0edab 100644 --- a/src/test/run-make/sepcomp-separate/foo.rs +++ b/src/test/run-make/sepcomp-separate/foo.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. + + fn magic_fn() -> usize { 1234 } @@ -24,4 +26,8 @@ mod b { } } -fn main() { } +fn main() { + magic_fn(); + a::magic_fn(); + b::magic_fn(); +} diff --git a/src/test/run-make/simd-ffi/simd.rs b/src/test/run-make/simd-ffi/simd.rs index 8ab8f4715755d..185476fb704f7 100644 --- a/src/test/run-make/simd-ffi/simd.rs +++ b/src/test/run-make/simd-ffi/simd.rs @@ -81,4 +81,5 @@ pub mod marker { #[lang = "freeze"] trait Freeze {} +#[allow(auto_impl)] impl Freeze for .. {} diff --git a/src/test/run-make/static-nobundle/Makefile b/src/test/run-make/static-nobundle/Makefile index 3eac12f5cc9fd..abc32d4423b05 100644 --- a/src/test/run-make/static-nobundle/Makefile +++ b/src/test/run-make/static-nobundle/Makefile @@ -9,13 +9,13 @@ all: $(call NATIVE_STATICLIB,aaa) $(RUSTC) bbb.rs --crate-type=rlib # Check that bbb does NOT contain the definition of `native_func` - nm $(TMPDIR)/libbbb.rlib | (! grep "T _*native_func") - nm $(TMPDIR)/libbbb.rlib | grep "U _*native_func" + nm $(TMPDIR)/libbbb.rlib | $(CGREP) -ve "T _*native_func" + nm $(TMPDIR)/libbbb.rlib | $(CGREP) -e "U _*native_func" # Check that aaa gets linked (either as `-l aaa` or `aaa.lib`) when building ccc. - $(RUSTC) ccc.rs -C prefer-dynamic --crate-type=dylib -Z print-link-args | grep -e "-l[\" ]*aaa" -e "aaa.lib" + $(RUSTC) ccc.rs -C prefer-dynamic --crate-type=dylib -Z print-link-args | $(CGREP) -e '-l[" ]*aaa|aaa\.lib' # Check that aaa does NOT get linked when building ddd. - $(RUSTC) ddd.rs -Z print-link-args | (! grep -e "-l[\" ]*aaa" -e "aaa.lib") + $(RUSTC) ddd.rs -Z print-link-args | $(CGREP) -ve '-l[" ]*aaa|aaa\.lib' $(call RUN,ddd) diff --git a/src/test/run-make/staticlib-blank-lib/Makefile b/src/test/run-make/staticlib-blank-lib/Makefile index 5878eec66bad4..92a278825c242 100644 --- a/src/test/run-make/staticlib-blank-lib/Makefile +++ b/src/test/run-make/staticlib-blank-lib/Makefile @@ -1,6 +1,6 @@ -include ../tools.mk all: - ar crus $(TMPDIR)/libfoo.a foo.rs - ar d $(TMPDIR)/libfoo.a foo.rs + $(AR) crus $(TMPDIR)/libfoo.a foo.rs + $(AR) d $(TMPDIR)/libfoo.a foo.rs $(RUSTC) foo.rs diff --git a/src/test/run-make/symbol-visibility/Makefile b/src/test/run-make/symbol-visibility/Makefile index 988c9473f6a7e..f1ada814bdb80 100644 --- a/src/test/run-make/symbol-visibility/Makefile +++ b/src/test/run-make/symbol-visibility/Makefile @@ -9,17 +9,17 @@ all: else NM=nm -D -DYLIB_EXT=so CDYLIB_NAME=liba_cdylib.so RDYLIB_NAME=liba_rust_dylib.so EXE_NAME=an_executable +COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.so ifeq ($(UNAME),Darwin) NM=nm -gU -DYLIB_EXT=dylib CDYLIB_NAME=liba_cdylib.dylib RDYLIB_NAME=liba_rust_dylib.dylib EXE_NAME=an_executable +COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.dylib endif all: @@ -27,6 +27,7 @@ all: $(RUSTC) a_cdylib.rs $(RUSTC) a_rust_dylib.rs $(RUSTC) an_executable.rs + $(RUSTC) a_cdylib.rs --crate-name combined_rlib_dylib --crate-type=rlib,cdylib # Check that a cdylib exports its public #[no_mangle] functions [ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ] @@ -47,4 +48,13 @@ all: [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ] [ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_rust_function_from_exe)" -eq "0" ] + + # Check the combined case, where we generate a cdylib and an rlib in the same + # compilation session: + # Check that a cdylib exports its public #[no_mangle] functions + [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ] + # Check that a cdylib exports the public #[no_mangle] functions of dependencies + [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ] + # Check that a cdylib DOES NOT export any public Rust functions + [ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ] endif diff --git a/src/test/run-make/symbols-are-reasonable/Makefile b/src/test/run-make/symbols-are-reasonable/Makefile index 1c117cf0c9e62..a6d294d2a1c0a 100644 --- a/src/test/run-make/symbols-are-reasonable/Makefile +++ b/src/test/run-make/symbols-are-reasonable/Makefile @@ -10,6 +10,4 @@ OUT=$(TMPDIR)/lib.s all: $(RUSTC) lib.rs --emit=asm --crate-type=staticlib # just check for symbol declarations with the names we're expecting. - grep 'str.[0-9][0-9]*:' $(OUT) - grep 'byte_str.[0-9][0-9]*:' $(OUT) - grep 'vtable.[0-9][0-9]*' $(OUT) + $(CGREP) -e 'str\.[0-9]+:' 'byte_str\.[0-9]+:' 'vtable\.[0-9]+' < $(OUT) diff --git a/src/test/run-make/symbols-are-reasonable/lib.rs b/src/test/run-make/symbols-are-reasonable/lib.rs index ff56ed62869d0..b9285b24cd63f 100644 --- a/src/test/run-make/symbols-are-reasonable/lib.rs +++ b/src/test/run-make/symbols-are-reasonable/lib.rs @@ -14,7 +14,8 @@ pub static Y: &'static [u8] = include_bytes!("lib.rs"); trait Foo { fn dummy(&self) { } } impl Foo for usize {} -pub fn dummy() { +#[no_mangle] +pub extern "C" fn dummy() { // force the vtable to be created let _x = &1usize as &Foo; } diff --git a/src/test/run-make/symbols-include-type-name/Makefile b/src/test/run-make/symbols-include-type-name/Makefile index 1add39e0cc35b..0850a2633e5d8 100644 --- a/src/test/run-make/symbols-include-type-name/Makefile +++ b/src/test/run-make/symbols-include-type-name/Makefile @@ -6,4 +6,4 @@ OUT=$(TMPDIR)/lib.s all: $(RUSTC) --crate-type staticlib --emit asm lib.rs - grep Def $(OUT) + $(CGREP) Def < $(OUT) diff --git a/src/test/run-make/symbols-include-type-name/lib.rs b/src/test/run-make/symbols-include-type-name/lib.rs index 1c478ed2598e4..d84f1617db53a 100644 --- a/src/test/run-make/symbols-include-type-name/lib.rs +++ b/src/test/run-make/symbols-include-type-name/lib.rs @@ -17,3 +17,8 @@ impl Def { Def { id: id } } } + +#[no_mangle] +pub fn user() { + let _ = Def::new(0); +} diff --git a/src/test/run-make/target-specs/Makefile b/src/test/run-make/target-specs/Makefile index 6b58ad7b6dff0..aff15ce38b4ae 100644 --- a/src/test/run-make/target-specs/Makefile +++ b/src/test/run-make/target-specs/Makefile @@ -1,9 +1,9 @@ -include ../tools.mk all: $(RUSTC) foo.rs --target=my-awesome-platform.json --crate-type=lib --emit=asm - grep -q -v morestack < $(TMPDIR)/foo.s - $(RUSTC) foo.rs --target=my-invalid-platform.json 2>&1 | grep -q "Error loading target specification" - $(RUSTC) foo.rs --target=my-incomplete-platform.json 2>&1 | grep 'Field llvm-target' + $(CGREP) -v morestack < $(TMPDIR)/foo.s + $(RUSTC) foo.rs --target=my-invalid-platform.json 2>&1 | $(CGREP) "Error loading target specification" + $(RUSTC) foo.rs --target=my-incomplete-platform.json 2>&1 | $(CGREP) 'Field llvm-target' RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-awesome-platform --crate-type=lib --emit=asm - RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=x86_64-unknown-linux-gnu --crate-type=lib --emit=asm + RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-x86_64-unknown-linux-gnu-platform --crate-type=lib --emit=asm $(RUSTC) -Z unstable-options --target=my-awesome-platform.json --print target-spec-json > $(TMPDIR)/test-platform.json && $(RUSTC) -Z unstable-options --target=$(TMPDIR)/test-platform.json --print target-spec-json | diff -q $(TMPDIR)/test-platform.json - diff --git a/src/test/run-make/target-specs/foo.rs b/src/test/run-make/target-specs/foo.rs index af24c3b460b2e..a0feb72702834 100644 --- a/src/test/run-make/target-specs/foo.rs +++ b/src/test/run-make/target-specs/foo.rs @@ -19,6 +19,7 @@ trait Sized { } #[lang = "freeze"] trait Freeze {} +#[allow(auto_impl)] impl Freeze for .. {} #[lang="start"] diff --git a/src/test/run-make/target-specs/my-awesome-platform.json b/src/test/run-make/target-specs/my-awesome-platform.json index 14515ad7f00b9..8d028280a8da7 100644 --- a/src/test/run-make/target-specs/my-awesome-platform.json +++ b/src/test/run-make/target-specs/my-awesome-platform.json @@ -4,6 +4,7 @@ "llvm-target": "i686-unknown-linux-gnu", "target-endian": "little", "target-pointer-width": "32", + "target-c-int-width": "32", "arch": "x86", "os": "linux", "morestack": false diff --git a/src/test/run-make/target-specs/my-incomplete-platform.json b/src/test/run-make/target-specs/my-incomplete-platform.json index 74787b28d2233..ceaa25cdf2fef 100644 --- a/src/test/run-make/target-specs/my-incomplete-platform.json +++ b/src/test/run-make/target-specs/my-incomplete-platform.json @@ -3,6 +3,7 @@ "linker-flavor": "gcc", "target-endian": "little", "target-pointer-width": "32", + "target-c-int-width": "32", "arch": "x86", "os": "foo", "morestack": false diff --git a/src/test/run-make/target-specs/x86_64-unknown-linux-gnu.json b/src/test/run-make/target-specs/my-x86_64-unknown-linux-gnu-platform.json similarity index 58% rename from src/test/run-make/target-specs/x86_64-unknown-linux-gnu.json rename to src/test/run-make/target-specs/my-x86_64-unknown-linux-gnu-platform.json index cfe152f9e8728..3ae01d72fcc18 100644 --- a/src/test/run-make/target-specs/x86_64-unknown-linux-gnu.json +++ b/src/test/run-make/target-specs/my-x86_64-unknown-linux-gnu-platform.json @@ -1,10 +1,11 @@ { "pre-link-args": ["-m64"], - "data-layout": "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", "linker-flavor": "gcc", "llvm-target": "x86_64-unknown-linux-gnu", "target-endian": "little", "target-pointer-width": "64", + "target-c-int-width": "32", "arch": "x86_64", "os": "linux", "morestack": false diff --git a/src/test/run-make/target-without-atomics/Makefile b/src/test/run-make/target-without-atomics/Makefile index 48c53a5651114..c5f575ddf84c6 100644 --- a/src/test/run-make/target-without-atomics/Makefile +++ b/src/test/run-make/target-without-atomics/Makefile @@ -2,4 +2,4 @@ # The target used below doesn't support atomic operations. Verify that's the case all: - $(RUSTC) --print cfg --target thumbv6m-none-eabi | grep -qv target_has_atomic + $(RUSTC) --print cfg --target thumbv6m-none-eabi | $(CGREP) -v target_has_atomic diff --git a/src/test/run-make/test-harness/Makefile b/src/test/run-make/test-harness/Makefile index aad8b1b3fbbaf..39477c07ced7c 100644 --- a/src/test/run-make/test-harness/Makefile +++ b/src/test/run-make/test-harness/Makefile @@ -3,7 +3,6 @@ all: # check that #[cfg_attr(..., ignore)] does the right thing. $(RUSTC) --test test-ignore-cfg.rs --cfg ignorecfg - $(call RUN,test-ignore-cfg) | grep 'shouldnotignore ... ok' - $(call RUN,test-ignore-cfg) | grep 'shouldignore ... ignored' - $(call RUN,test-ignore-cfg --quiet) | grep "^i\.$$" - $(call RUN,test-ignore-cfg --quiet) | grep -v 'should' + $(call RUN,test-ignore-cfg) | $(CGREP) 'shouldnotignore ... ok' 'shouldignore ... ignored' + $(call RUN,test-ignore-cfg --quiet) | $(CGREP) -e "^i\.$$" + $(call RUN,test-ignore-cfg --quiet) | $(CGREP) -v 'should' diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk index 27f235d54d46b..d9103e1992735 100644 --- a/src/test/run-make/tools.mk +++ b/src/test/run-make/tools.mk @@ -7,9 +7,16 @@ TARGET_RPATH_ENV = \ RUSTC_ORIGINAL := $(RUSTC) BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)' +BARE_RUSTDOC := $(HOST_RPATH_ENV) '$(RUSTDOC)' RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) +RUSTDOC := $(BARE_RUSTDOC) +ifdef RUSTC_LINKER +RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER) +RUSTDOC := $(RUSTDOC) --linker $(RUSTC_LINKER) -Z unstable-options +endif #CC := $(CC) -L $(TMPDIR) HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py +CGREP := "$(S)/src/etc/cat-and-grep.sh" # This is the name of the binary we will generate and run; use this # e.g. for `$(CC) -o $(RUN_BINFILE)`. @@ -86,7 +93,7 @@ ifeq ($(UNAME),SunOS) EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv else ifeq ($(UNAME),OpenBSD) - EXTRACFLAGS := -lm -lpthread + EXTRACFLAGS := -lm -lpthread -lc++abi RUSTC := $(RUSTC) -C linker="$(word 1,$(CC:ccache=))" else EXTRACFLAGS := -lm -lrt -ldl -lpthread @@ -102,13 +109,13 @@ REMOVE_DYLIBS = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1)) REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1)) %.a: %.o - ar crus $@ $< + $(AR) crus $@ $< ifdef IS_MSVC %.lib: lib%.o $(MSVC_LIB) -out:`cygpath -w $@` $< else %.lib: lib%.o - ar crus $@ $< + $(AR) crus $@ $< endif %.dylib: %.o $(CC) -dynamiclib -Wl,-dylib -o $@ $< diff --git a/src/test/run-make/treat-err-as-bug/Makefile b/src/test/run-make/treat-err-as-bug/Makefile index a8fa2d4e0f82b..f99e4611174ca 100644 --- a/src/test/run-make/treat-err-as-bug/Makefile +++ b/src/test/run-make/treat-err-as-bug/Makefile @@ -2,4 +2,4 @@ all: $(RUSTC) err.rs -Z treat-err-as-bug 2>&1 \ - | grep -q "panicked at 'encountered error with .-Z treat_err_as_bug'" + | $(CGREP) "panicked at 'encountered error with \`-Z treat_err_as_bug'" diff --git a/src/test/run-make/type-mismatch-same-crate-name/Makefile b/src/test/run-make/type-mismatch-same-crate-name/Makefile index 1044d73fd1a36..9fd1377322b94 100644 --- a/src/test/run-make/type-mismatch-same-crate-name/Makefile +++ b/src/test/run-make/type-mismatch-same-crate-name/Makefile @@ -8,12 +8,12 @@ all: $(RUSTC) --crate-type=rlib crateB.rs --extern crateA=$(TMPDIR)/libcrateA-1.rlib # make crateC depend on version 2 of crateA $(RUSTC) crateC.rs --extern crateA=$(TMPDIR)/libcrateA-2.rlib 2>&1 | \ - tr -d '\r\n' | grep \ + tr -d '\r\n' | $(CGREP) -e \ "mismatched types.*\ - crateB::try_foo(foo2);.*\ + crateB::try_foo\(foo2\);.*\ expected struct \`crateA::foo::Foo\`, found struct \`crateA::Foo\`.*\ different versions of crate \`crateA\`.*\ mismatched types.*\ - crateB::try_bar(bar2);.*\ + crateB::try_bar\(bar2\);.*\ expected trait \`crateA::bar::Bar\`, found trait \`crateA::Bar\`.*\ different versions of crate \`crateA\`" diff --git a/src/test/run-make/used/Makefile b/src/test/run-make/used/Makefile index 9d7aa30f87482..47edcbb5d0a1e 100644 --- a/src/test/run-make/used/Makefile +++ b/src/test/run-make/used/Makefile @@ -7,5 +7,5 @@ all: else all: $(RUSTC) -C opt-level=3 --emit=obj used.rs - nm $(TMPDIR)/used.o | grep FOO + nm $(TMPDIR)/used.o | $(CGREP) FOO endif diff --git a/src/test/run-make/volatile-intrinsics/Makefile b/src/test/run-make/volatile-intrinsics/Makefile index 34fa56efee6fa..acbadbef9fb4a 100644 --- a/src/test/run-make/volatile-intrinsics/Makefile +++ b/src/test/run-make/volatile-intrinsics/Makefile @@ -6,5 +6,4 @@ all: $(call RUN,main) # ... and the loads/stores must not be optimized out. $(RUSTC) main.rs --emit=llvm-ir - grep "load volatile" $(TMPDIR)/main.ll - grep "store volatile" $(TMPDIR)/main.ll + $(CGREP) "load volatile" "store volatile" < $(TMPDIR)/main.ll diff --git a/src/test/run-make/weird-output-filenames/Makefile b/src/test/run-make/weird-output-filenames/Makefile index 8b69c68279dca..a5543e3b2c4d0 100644 --- a/src/test/run-make/weird-output-filenames/Makefile +++ b/src/test/run-make/weird-output-filenames/Makefile @@ -3,13 +3,13 @@ all: cp foo.rs $(TMPDIR)/.foo.rs $(RUSTC) $(TMPDIR)/.foo.rs 2>&1 \ - | grep "invalid character.*in crate name:" + | $(CGREP) -e "invalid character.*in crate name:" cp foo.rs $(TMPDIR)/.foo.bar $(RUSTC) $(TMPDIR)/.foo.bar 2>&1 \ - | grep "invalid character.*in crate name:" + | $(CGREP) -e "invalid character.*in crate name:" cp foo.rs $(TMPDIR)/+foo+bar $(RUSTC) $(TMPDIR)/+foo+bar 2>&1 \ - | grep "invalid character.*in crate name:" + | $(CGREP) -e "invalid character.*in crate name:" cp foo.rs $(TMPDIR)/-foo.rs $(RUSTC) $(TMPDIR)/-foo.rs 2>&1 \ - | grep 'crate names cannot start with a `-`' + | $(CGREP) 'crate names cannot start with a `-`' diff --git a/src/test/run-pass-fulldeps/auxiliary/issue_16723_multiple_items_syntax_ext.rs b/src/test/run-pass-fulldeps/auxiliary/issue-16723.rs similarity index 100% rename from src/test/run-pass-fulldeps/auxiliary/issue_16723_multiple_items_syntax_ext.rs rename to src/test/run-pass-fulldeps/auxiliary/issue-16723.rs diff --git a/src/test/run-pass-fulldeps/auxiliary/linkage-visibility.rs b/src/test/run-pass-fulldeps/auxiliary/linkage-visibility.rs index 09a2e8ecd876d..7a15a4cb3a2e8 100644 --- a/src/test/run-pass-fulldeps/auxiliary/linkage-visibility.rs +++ b/src/test/run-pass-fulldeps/auxiliary/linkage-visibility.rs @@ -14,9 +14,9 @@ // do the runtime check that these functions aren't exported. #![allow(private_no_mangle_fns)] -extern crate rustc_back; +extern crate rustc_metadata; -use rustc_back::dynamic_lib::DynamicLibrary; +use rustc_metadata::dynamic_lib::DynamicLibrary; #[no_mangle] pub fn foo() { bar(); } diff --git a/src/test/run-pass-fulldeps/auxiliary/lint_for_crate.rs b/src/test/run-pass-fulldeps/auxiliary/lint_for_crate.rs index fc53031e7f226..d3f921e0878ae 100644 --- a/src/test/run-pass-fulldeps/auxiliary/lint_for_crate.rs +++ b/src/test/run-pass-fulldeps/auxiliary/lint_for_crate.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar, rustc_private)] #![feature(box_syntax)] +#![feature(macro_vis_matcher)] #[macro_use] extern crate rustc; extern crate rustc_plugin; diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_crate_outlive_expansion_phase.rs b/src/test/run-pass-fulldeps/auxiliary/outlive-expansion-phase.rs similarity index 100% rename from src/test/run-pass-fulldeps/auxiliary/plugin_crate_outlive_expansion_phase.rs rename to src/test/run-pass-fulldeps/auxiliary/outlive-expansion-phase.rs diff --git a/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs b/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs index 0c8af013fd12d..26419a51513f2 100644 --- a/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs +++ b/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs @@ -29,8 +29,8 @@ use rustc_plugin::Registry; // WARNING WARNING WARNING WARNING WARNING // ======================================= // -// This code also appears in src/doc/guide-plugin.md. Please keep -// the two copies in sync! FIXME: have rustdoc read this file +// This code also appears in src/doc/unstable-book/src/language-features/plugin.md. +// Please keep the two copies in sync! FIXME: have rustdoc read this file fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box { diff --git a/src/test/run-pass/binary-heap-panic-safe.rs b/src/test/run-pass-fulldeps/binary-heap-panic-safe.rs similarity index 97% rename from src/test/run-pass/binary-heap-panic-safe.rs rename to src/test/run-pass-fulldeps/binary-heap-panic-safe.rs index 93d3345a80991..6139a7d3201c5 100644 --- a/src/test/run-pass/binary-heap-panic-safe.rs +++ b/src/test/run-pass-fulldeps/binary-heap-panic-safe.rs @@ -8,9 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rand, std_panic)] +#![feature(rustc_private, std_panic)] -use std::__rand::{thread_rng, Rng}; +extern crate rand; + +use rand::{thread_rng, Rng}; use std::panic::{self, AssertUnwindSafe}; use std::collections::BinaryHeap; diff --git a/src/test/run-pass-fulldeps/create-dir-all-bare.rs b/src/test/run-pass-fulldeps/create-dir-all-bare.rs index e22736d77856a..ba42cb870c97e 100644 --- a/src/test/run-pass-fulldeps/create-dir-all-bare.rs +++ b/src/test/run-pass-fulldeps/create-dir-all-bare.rs @@ -12,11 +12,11 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate tempdir; use std::env; use std::fs; -use rustc_back::tempdir::TempDir; +use tempdir::TempDir; fn main() { let td = TempDir::new("create-dir-all-bare").unwrap(); diff --git a/src/test/run-pass/env.rs b/src/test/run-pass-fulldeps/env.rs similarity index 96% rename from src/test/run-pass/env.rs rename to src/test/run-pass-fulldeps/env.rs index e602fb2d7d29a..cf2ea732ee1b8 100644 --- a/src/test/run-pass/env.rs +++ b/src/test/run-pass-fulldeps/env.rs @@ -10,14 +10,15 @@ // compile-flags: --test -#![feature(rand, std_panic)] +#![feature(rustc_private, std_panic)] + +extern crate rand; use std::env::*; -use std::__rand as rand; -use std::__rand::Rng; use std::iter::repeat; use std::ffi::{OsString, OsStr}; +use rand::Rng; fn make_rand_name() -> OsString { let mut rng = rand::thread_rng(); diff --git a/src/test/run-pass-fulldeps/flt2dec.rs b/src/test/run-pass-fulldeps/flt2dec.rs new file mode 100644 index 0000000000000..3db0644d1ef3c --- /dev/null +++ b/src/test/run-pass-fulldeps/flt2dec.rs @@ -0,0 +1,163 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--test + +#![feature(rustc_private, flt2dec)] + +extern crate core; +extern crate rand; + +use std::i16; +use std::mem; +use std::str; + +use core::num::flt2dec::MAX_SIG_DIGITS; +use core::num::flt2dec::strategy::grisu::format_exact_opt; +use core::num::flt2dec::strategy::grisu::format_shortest_opt; +use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded}; + +use rand::{Rand, XorShiftRng}; +use rand::distributions::{IndependentSample, Range}; +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {:?} instead", full_decoded) + } +} + + +fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + V: FnMut(usize) -> Decoded { + assert!(k <= 1024); + + let mut npassed = 0; // f(x) = Some(g(x)) + let mut nignored = 0; // f(x) = None + + for i in 0..n { + if (i & 0xfffff) == 0 { + println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})", + i, n, nignored, npassed, i - nignored - npassed); + } + + let decoded = v(i); + let mut buf1 = [0; 1024]; + if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { + let mut buf2 = [0; 1024]; + let (len2, e2) = g(&decoded, &mut buf2[..k]); + if e1 == e2 && &buf1[..len1] == &buf2[..len2] { + npassed += 1; + } else { + println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}", + i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1, + str::from_utf8(&buf2[..len2]).unwrap(), e2); + } + } else { + nignored += 1; + } + } + println!("{}({}): done, ignored={} passed={} failed={}", + func, k, nignored, npassed, n - nignored - npassed); + assert!(nignored + npassed == n, + "{}({}): {} out of {} values returns an incorrect value!", + func, k, n - nignored - npassed, n); + (npassed, nignored) +} + +pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000); + iterate("f32_random_equivalence_test", k, n, f, g, |_| { + let i: u32 = f32_range.ind_sample(&mut rng); + let x: f32 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); + iterate("f64_random_equivalence_test", k, n, f, g, |_| { + let i: u64 = f64_range.ind_sample(&mut rng); + let x: f64 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, + // so why not simply testing all of them? + // + // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), + // but with `-C opt-level=3 -C lto` this only takes about an hour or so. + + // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges + let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", + k, 0x7f7f_ffff, f, g, |i: usize| { + let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; + decode_finite(x) + }); + assert_eq!((npassed, nignored), (2121451881, 17643158)); +} + +#[test] +fn shortest_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); + f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); +} + +#[test] #[ignore] // it is too expensive +fn shortest_f32_exhaustive_equivalence_test() { + // it is hard to directly test the optimality of the output, but we can at least test if + // two different algorithms agree to each other. + // + // this reports the progress and the number of f32 values returned `None`. + // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: + // `done, ignored=17643158 passed=2121451881 failed=0`. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); +} + +#[test] #[ignore] // it is too expensive +fn shortest_f64_hard_random_equivalence_test() { + // this again probably has to use appropriate rustc flags. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, + MAX_SIG_DIGITS, 100_000_000); +} + +#[test] +fn exact_f32_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} + +#[test] +fn exact_f64_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} diff --git a/src/test/run-pass-fulldeps/issue-15149.rs b/src/test/run-pass-fulldeps/issue-15149.rs index c0ed7165afeca..121fd4a9825df 100644 --- a/src/test/run-pass-fulldeps/issue-15149.rs +++ b/src/test/run-pass-fulldeps/issue-15149.rs @@ -13,13 +13,13 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate tempdir; use std::env; use std::fs; use std::process; use std::str; -use rustc_back::tempdir::TempDir; +use tempdir::TempDir; fn main() { // If we're the child, make sure we were invoked correctly diff --git a/src/test/run-pass-fulldeps/issue_16723_multiple_items_syntax_ext.rs b/src/test/run-pass-fulldeps/issue-16723.rs similarity index 86% rename from src/test/run-pass-fulldeps/issue_16723_multiple_items_syntax_ext.rs rename to src/test/run-pass-fulldeps/issue-16723.rs index c1ffeb7c8e2e9..bacfa8d1ead5a 100644 --- a/src/test/run-pass-fulldeps/issue_16723_multiple_items_syntax_ext.rs +++ b/src/test/run-pass-fulldeps/issue-16723.rs @@ -9,9 +9,9 @@ // except according to those terms. // ignore-stage1 -// aux-build:issue_16723_multiple_items_syntax_ext.rs +// aux-build:issue-16723.rs #![feature(plugin)] -#![plugin(issue_16723_multiple_items_syntax_ext)] +#![plugin(issue_16723)] multiple_items!(); diff --git a/src/test/run-pass-fulldeps/issue-35829.rs b/src/test/run-pass-fulldeps/issue-35829.rs index 0a4c15a9236b9..f17a0494a69c4 100644 --- a/src/test/run-pass-fulldeps/issue-35829.rs +++ b/src/test/run-pass-fulldeps/issue-35829.rs @@ -41,8 +41,8 @@ fn main() { let raw_byte_string_lit_kind = LitKind::ByteStr(Rc::new(b"#\"two\"#".to_vec())); assert_eq!(raw_byte_string.node, ExprKind::Lit(P(dummy_spanned(raw_byte_string_lit_kind)))); - // check dotdotdot - let closed_range = quote_expr!(&cx, 0 ... 1); + // check dotdoteq + let closed_range = quote_expr!(&cx, 0 ..= 1); assert_eq!(closed_range.node, ExprKind::Range( Some(quote_expr!(&cx, 0)), Some(quote_expr!(&cx, 1)), diff --git a/src/test/run-pass-fulldeps/macro-crate-outlive-expansion-phase.rs b/src/test/run-pass-fulldeps/outlive-expansion-phase.rs similarity index 83% rename from src/test/run-pass-fulldeps/macro-crate-outlive-expansion-phase.rs rename to src/test/run-pass-fulldeps/outlive-expansion-phase.rs index 9573d0c8c4030..6eb3e510724f3 100644 --- a/src/test/run-pass-fulldeps/macro-crate-outlive-expansion-phase.rs +++ b/src/test/run-pass-fulldeps/outlive-expansion-phase.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:plugin_crate_outlive_expansion_phase.rs +// aux-build:outlive-expansion-phase.rs // ignore-stage1 #![feature(plugin)] -#![plugin(plugin_crate_outlive_expansion_phase)] +#![plugin(outlive_expansion_phase)] pub fn main() {} diff --git a/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs b/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs index 8ba38875eff5b..52a8652e65bcf 100644 --- a/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs +++ b/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:attr-on-trait.rs +// ignore-stage1 #![feature(proc_macro)] diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-40001-plugin.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-40001-plugin.rs index cf1a631937ba4..29b6cc012b393 100644 --- a/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-40001-plugin.rs +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-40001-plugin.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(box_syntax, plugin, plugin_registrar, rustc_private)] +#![feature(macro_vis_matcher)] #![crate_type = "dylib"] #[macro_use] diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-42708.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-42708.rs new file mode 100644 index 0000000000000..58b4b2a5293b3 --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/issue-42708.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro)] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Test)] +pub fn derive(_input: TokenStream) -> TokenStream { + "fn f(s: S) { s.x }".parse().unwrap() +} + +#[proc_macro_attribute] +pub fn attr_test(_attr: TokenStream, input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs new file mode 100644 index 0000000000000..ce6ffcc3cb03d --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs @@ -0,0 +1,45 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro)] + +extern crate proc_macro; + +use proc_macro::*; + +// Re-emits the input tokens by parsing them from strings +#[proc_macro] +pub fn reemit(input: TokenStream) -> TokenStream { + input.to_string().parse().unwrap() +} + +#[proc_macro] +pub fn assert_fake_source_file(input: TokenStream) -> TokenStream { + for tk in input { + let source_file = tk.span.source_file(); + assert!(!source_file.is_real(), "Source file is real: {:?}", source_file); + } + + "".parse().unwrap() +} + +#[proc_macro] +pub fn assert_source_file(input: TokenStream) -> TokenStream { + for tk in input { + let source_file = tk.span.source_file(); + assert!(source_file.is_real(), "Source file is not real: {:?}", source_file); + } + + "".parse().unwrap() +} diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs new file mode 100644 index 0000000000000..b4666e2cb61bd --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_export] +macro_rules! reemit_legacy { + ($($tok:tt)*) => ($($tok)*) +} + +#[macro_export] +macro_rules! say_hello_extern { + ($macname:ident) => ( $macname! { "Hello, world!" }) +} diff --git a/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs b/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs new file mode 100644 index 0000000000000..e53e94ae475b2 --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue-42708.rs +// ignore-stage1 + +#![feature(decl_macro, proc_macro)] +#![allow(unused)] + +extern crate issue_42708; + +macro m() { + #[derive(issue_42708::Test)] + struct S { x: () } + + #[issue_42708::attr_test] + struct S2 { x: () } + + #[derive(Clone)] + struct S3 { x: () } + + fn g(s: S, s2: S2, s3: S3) { + (s.x, s2.x, s3.x); + } +} + +m!(); + +fn main() {} diff --git a/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs b/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs new file mode 100644 index 0000000000000..c2df561b43a11 --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs @@ -0,0 +1,43 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:span-api-tests.rs +// aux-build:span-test-macros.rs + +// ignore-pretty + +#![feature(proc_macro)] + +#[macro_use] +extern crate span_test_macros; + +extern crate span_api_tests; + +use span_api_tests::{reemit, assert_fake_source_file, assert_source_file}; + +macro_rules! say_hello { + ($macname:ident) => ( $macname! { "Hello, world!" }) +} + +assert_source_file! { "Hello, world!" } + +say_hello! { assert_source_file } + +reemit_legacy! { + assert_source_file! { "Hello, world!" } +} + +say_hello_extern! { assert_fake_source_file } + +reemit! { + assert_source_file! { "Hello, world!" } +} + +fn main() {} diff --git a/src/test/run-pass-fulldeps/rename-directory.rs b/src/test/run-pass-fulldeps/rename-directory.rs index f107e1042816f..7a2a4343522bb 100644 --- a/src/test/run-pass-fulldeps/rename-directory.rs +++ b/src/test/run-pass-fulldeps/rename-directory.rs @@ -15,11 +15,11 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate tempdir; use std::ffi::CString; use std::fs::{self, File}; -use rustc_back::tempdir::TempDir; +use tempdir::TempDir; fn rename_directory() { let tmpdir = TempDir::new("rename_directory").ok().expect("rename_directory failed"); diff --git a/src/test/run-pass-fulldeps/sort-unstable.rs b/src/test/run-pass-fulldeps/sort-unstable.rs new file mode 100644 index 0000000000000..af8a691aa3ec6 --- /dev/null +++ b/src/test/run-pass-fulldeps/sort-unstable.rs @@ -0,0 +1,83 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_private, sort_internals)] + +extern crate core; +extern crate rand; + +use std::cmp::Ordering::{Equal, Greater, Less}; +use core::slice::heapsort; + +use rand::{Rng, XorShiftRng}; + +fn main() { + let mut v = [0; 600]; + let mut tmp = [0; 600]; + let mut rng = XorShiftRng::new_unseeded(); + + for len in (2..25).chain(500..510) { + let v = &mut v[0..len]; + let tmp = &mut tmp[0..len]; + + for &modulus in &[5, 10, 100, 1000] { + for _ in 0..100 { + for i in 0..len { + v[i] = rng.gen::() % modulus; + } + + // Sort in default order. + tmp.copy_from_slice(v); + tmp.sort_unstable(); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in ascending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| a.cmp(b)); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in descending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| b.cmp(a)); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + + // Test heapsort using `<` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a < b); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Test heapsort using `>` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a > b); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + } + } + } + + // Sort using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + for i in 0..v.len() { + v[i] = i as i32; + } + v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap()); + v.sort_unstable(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + + // Should not panic. + [0i32; 0].sort_unstable(); + [(); 10].sort_unstable(); + [(); 100].sort_unstable(); + + let mut v = [0xDEADBEEFu64]; + v.sort_unstable(); + assert!(v == [0xDEADBEEF]); +} diff --git a/src/test/run-pass-fulldeps/stdio-from.rs b/src/test/run-pass-fulldeps/stdio-from.rs index f64bbf9312cd8..535ab711f5bcf 100644 --- a/src/test/run-pass-fulldeps/stdio-from.rs +++ b/src/test/run-pass-fulldeps/stdio-from.rs @@ -12,7 +12,7 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate tempdir; use std::env; use std::fs::File; @@ -20,7 +20,7 @@ use std::io; use std::io::{Read, Write}; use std::process::{Command, Stdio}; -use rustc_back::tempdir::TempDir; +use tempdir::TempDir; fn main() { if env::args().len() > 1 { diff --git a/src/test/run-pass-fulldeps/switch-stdout.rs b/src/test/run-pass-fulldeps/switch-stdout.rs index 4542e27545a4c..16f7e28328536 100644 --- a/src/test/run-pass-fulldeps/switch-stdout.rs +++ b/src/test/run-pass-fulldeps/switch-stdout.rs @@ -10,12 +10,12 @@ #![feature(rustc_private)] -extern crate rustc_back; +extern crate tempdir; use std::fs::File; use std::io::{Read, Write}; -use rustc_back::tempdir::TempDir; +use tempdir::TempDir; #[cfg(unix)] fn switch_stdout_to(file: File) { diff --git a/src/test/run-pass/vector-sort-panic-safe.rs b/src/test/run-pass-fulldeps/vector-sort-panic-safe.rs similarity index 98% rename from src/test/run-pass/vector-sort-panic-safe.rs rename to src/test/run-pass-fulldeps/vector-sort-panic-safe.rs index c6a1859de30b8..adc72aa0ea23b 100644 --- a/src/test/run-pass/vector-sort-panic-safe.rs +++ b/src/test/run-pass-fulldeps/vector-sort-panic-safe.rs @@ -10,11 +10,12 @@ // ignore-emscripten no threads support -#![feature(rand)] +#![feature(rustc_private)] #![feature(sort_unstable)] -#![feature(const_atomic_usize_new)] -use std::__rand::{thread_rng, Rng}; +extern crate rand; + +use rand::{thread_rng, Rng}; use std::cell::Cell; use std::cmp::Ordering; use std::panic; diff --git a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs index 439c1080f4731..4466a95cb39b9 100644 --- a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs +++ b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs @@ -11,7 +11,6 @@ // no-prefer-dynamic #![allow(dead_code)] -#![feature(const_atomic_usize_new)] // check dtor calling order when casting enums. diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index b46f024b5bff3..22081678fb999 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,7 +15,6 @@ extern crate helper; -use std::env; use std::heap::{Heap, Alloc, System, Layout, AllocErr}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; @@ -39,8 +38,7 @@ unsafe impl<'a> Alloc for &'a A { static GLOBAL: A = A; fn main() { - env::set_var("FOO", "bar"); - drop(env::var("FOO")); + println!("hello!"); let n = HITS.load(Ordering::SeqCst); assert!(n > 0); diff --git a/src/test/run-pass/allocator/xcrate-use.rs b/src/test/run-pass/allocator/xcrate-use.rs index 4b987b9223d6d..04d2ef466e73d 100644 --- a/src/test/run-pass/allocator/xcrate-use.rs +++ b/src/test/run-pass/allocator/xcrate-use.rs @@ -17,7 +17,6 @@ extern crate custom; extern crate helper; -use std::env; use std::heap::{Heap, Alloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index 7e6cd9fdf4950..155fb5d6c5de9 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -19,7 +19,6 @@ extern crate custom; extern crate custom_as_global; extern crate helper; -use std::env; use std::heap::{Heap, Alloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; diff --git a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs index b40774e2be82f..8c480d7deebd9 100644 --- a/src/test/run-pass/anon-extern-mod-cross-crate-2.rs +++ b/src/test/run-pass/anon-extern-mod-cross-crate-2.rs @@ -10,6 +10,7 @@ // aux-build:anon-extern-mod-cross-crate-1.rs // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test ffi with extern crate anonexternmod; diff --git a/src/test/run-pass/anon-extern-mod.rs b/src/test/run-pass/anon-extern-mod.rs index 208b4df3c3e7e..16ca7bce95b59 100644 --- a/src/test/run-pass/anon-extern-mod.rs +++ b/src/test/run-pass/anon-extern-mod.rs @@ -9,6 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/arbitrary_self_types_silly.rs b/src/test/run-pass/arbitrary_self_types_silly.rs new file mode 100644 index 0000000000000..755a8d7ea294f --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_silly.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(arbitrary_self_types)] + +struct Foo; +struct Bar; + +impl std::ops::Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Foo { + &Foo + } +} + +impl Foo { + fn bar(self: Bar) -> i32 { 3 } +} + +fn main() { + assert_eq!(3, Bar.bar()); +} diff --git a/src/test/run-pass/arbitrary_self_types_struct.rs b/src/test/run-pass/arbitrary_self_types_struct.rs new file mode 100644 index 0000000000000..961717de0463e --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_struct.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(arbitrary_self_types)] + +use std::rc::Rc; + +struct Foo { + x: i32, + y: i32, +} + +impl Foo { + fn x(self: &Rc) -> i32 { + self.x + } + + fn y(self: Rc) -> i32 { + self.y + } +} + +fn main() { + let foo = Rc::new(Foo {x: 3, y: 4}); + assert_eq!(3, foo.x()); + assert_eq!(4, foo.y()); +} diff --git a/src/test/run-pass/arbitrary_self_types_trait.rs b/src/test/run-pass/arbitrary_self_types_trait.rs new file mode 100644 index 0000000000000..e74d614dd6bd9 --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_trait.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(arbitrary_self_types)] + +use std::rc::Rc; + +trait Trait { + fn trait_method<'a>(self: &'a Box>) -> &'a [i32]; +} + +impl Trait for Vec { + fn trait_method<'a>(self: &'a Box>) -> &'a [i32] { + &***self + } +} + +fn main() { + let v = vec![1,2,3]; + + assert_eq!(&[1,2,3], Box::new(Rc::new(v)).trait_method()); +} diff --git a/src/test/run-pass/arbitrary_self_types_unsized_struct.rs b/src/test/run-pass/arbitrary_self_types_unsized_struct.rs new file mode 100644 index 0000000000000..8dc40e7aab111 --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_unsized_struct.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(arbitrary_self_types)] + +use std::rc::Rc; + +struct Foo(T); + +impl Foo<[u8]> { + fn len(self: Rc) -> usize { + self.0.len() + } +} + +fn main() { + let rc = Rc::new(Foo([1u8,2,3])) as Rc>; + assert_eq!(3, rc.len()); +} diff --git a/src/test/run-pass/asm-concat-src.rs b/src/test/run-pass/asm-concat-src.rs index fb257bf7b500c..0380ccbdea47b 100644 --- a/src/test/run-pass/asm-concat-src.rs +++ b/src/test/run-pass/asm-concat-src.rs @@ -9,7 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 -// ignore-emscripten +// ignore-emscripten no asm #![feature(asm)] diff --git a/src/test/ui/macros/assert_eq_trailing_comma.rs b/src/test/run-pass/assert-eq-trailing-comma.rs similarity index 100% rename from src/test/ui/macros/assert_eq_trailing_comma.rs rename to src/test/run-pass/assert-eq-trailing-comma.rs diff --git a/src/test/ui/macros/assert_ne_trailing_comma.rs b/src/test/run-pass/assert-ne-trailing-comma.rs similarity index 100% rename from src/test/ui/macros/assert_ne_trailing_comma.rs rename to src/test/run-pass/assert-ne-trailing-comma.rs diff --git a/src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where-clause.rs b/src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where.rs similarity index 98% rename from src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where-clause.rs rename to src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where.rs index 7cde780cc54ba..5ceb1013ad811 100644 --- a/src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where-clause.rs +++ b/src/test/run-pass/associated-types-project-from-type-param-via-bound-in-where.rs @@ -12,8 +12,6 @@ // `Item` originates in a where-clause, not the declaration of // `T`. Issue #20300. -#![feature(const_atomic_usize_new)] - use std::marker::{PhantomData}; use std::sync::atomic::{AtomicUsize}; use std::sync::atomic::Ordering::SeqCst; diff --git a/src/test/run-pass/auto-is-contextual.rs b/src/test/run-pass/auto-is-contextual.rs new file mode 100644 index 0000000000000..ad433cc26a790 --- /dev/null +++ b/src/test/run-pass/auto-is-contextual.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! auto { + () => (struct S;) +} + +auto!(); + +fn auto() {} + +fn main() { + auto(); + let auto = 10; + auto; + auto as u8; +} diff --git a/src/test/run-pass/auto-traits.rs b/src/test/run-pass/auto-traits.rs new file mode 100644 index 0000000000000..e42aca9ccbd13 --- /dev/null +++ b/src/test/run-pass/auto-traits.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(optin_builtin_traits)] + +auto trait Auto {} +// Redundant but accepted until we remove it. +#[allow(auto_impl)] +impl Auto for .. {} + +unsafe auto trait AutoUnsafe {} + +impl !Auto for bool {} +impl !AutoUnsafe for bool {} + +struct AutoBool(bool); + +impl Auto for AutoBool {} +unsafe impl AutoUnsafe for AutoBool {} + +fn take_auto(_: T) {} +fn take_auto_unsafe(_: T) {} + +fn main() { + take_auto(0); + take_auto(AutoBool(true)); + take_auto_unsafe(0); + take_auto_unsafe(AutoBool(true)); + + /// Auto traits are allowed in trait object bounds. + let _: &(Send + Auto) = &0; +} diff --git a/src/test/run-pass/auxiliary/issue-17718-aux.rs b/src/test/run-pass/auxiliary/issue-17718-aux.rs index 36891a1ecadb8..2bc8b4b7ba032 100644 --- a/src/test/run-pass/auxiliary/issue-17718-aux.rs +++ b/src/test/run-pass/auxiliary/issue-17718-aux.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_atomic_usize_new)] - use std::sync::atomic; pub const C1: usize = 1; diff --git a/src/test/run-pass/auxiliary/issue-3012-1.rs b/src/test/run-pass/auxiliary/issue-3012-1.rs index b6199f59ebe06..f34a97519e77d 100644 --- a/src/test/run-pass/auxiliary/issue-3012-1.rs +++ b/src/test/run-pass/auxiliary/issue-3012-1.rs @@ -10,13 +10,10 @@ #![crate_name="socketlib"] #![crate_type = "lib"] -#![feature(libc)] pub mod socket { - extern crate libc; - pub struct socket_handle { - sockfd: libc::c_int, + sockfd: u32, } impl Drop for socket_handle { @@ -25,7 +22,7 @@ pub mod socket { } } - pub fn socket_handle(x: libc::c_int) -> socket_handle { + pub fn socket_handle(x: u32) -> socket_handle { socket_handle { sockfd: x } diff --git a/src/test/run-pass/auxiliary/thread-local-extern-static.rs b/src/test/run-pass/auxiliary/thread-local-extern-static.rs index bce87ef5a2668..e9457886be80d 100644 --- a/src/test/run-pass/auxiliary/thread-local-extern-static.rs +++ b/src/test/run-pass/auxiliary/thread-local-extern-static.rs @@ -9,7 +9,6 @@ // except according to those terms. #![feature(cfg_target_thread_local, const_fn, thread_local)] -#![feature(const_cell_new)] #![crate_type = "lib"] #[cfg(target_thread_local)] diff --git a/src/test/run-pass/backtrace-debuginfo.rs b/src/test/run-pass/backtrace-debuginfo.rs index f81352e177322..f9233026a1e6e 100644 --- a/src/test/run-pass/backtrace-debuginfo.rs +++ b/src/test/run-pass/backtrace-debuginfo.rs @@ -19,8 +19,6 @@ // ignore-pretty issue #37195 // ignore-emscripten spawning processes is not supported -use std::io; -use std::io::prelude::*; use std::env; #[path = "backtrace-debuginfo-aux.rs"] mod aux; @@ -163,7 +161,7 @@ fn main() { let args: Vec = env::args().collect(); if args.len() >= 2 { let case = args[1].parse().unwrap(); - writeln!(&mut io::stderr(), "test case {}", case).unwrap(); + eprintln!("test case {}", case); outer(case, pos!()); println!("done."); } else { diff --git a/src/test/run-pass/borrowck/borrowck-assignment-to-static-mut.rs b/src/test/run-pass/borrowck/borrowck-assignment-to-static-mut.rs new file mode 100644 index 0000000000000..302a7b96bc07d --- /dev/null +++ b/src/test/run-pass/borrowck/borrowck-assignment-to-static-mut.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test taken from #45641 (https://github.com/rust-lang/rust/issues/45641) + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +static mut Y: u32 = 0; + +unsafe fn should_ok() { + Y = 1; +} + +fn main() {} diff --git a/src/test/ui/span/loan-extend.rs b/src/test/run-pass/borrowck/borrowck-unsafe-static-mutable-borrows.rs similarity index 53% rename from src/test/ui/span/loan-extend.rs rename to src/test/run-pass/borrowck/borrowck-unsafe-static-mutable-borrows.rs index a4b951daab439..de411d3096092 100644 --- a/src/test/ui/span/loan-extend.rs +++ b/src/test/run-pass/borrowck/borrowck-unsafe-static-mutable-borrows.rs @@ -8,17 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait)] +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir -// Helper creating a fake borrow, captured by the impl Trait. -fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () } +// Test file taken from issue 45129 (https://github.com/rust-lang/rust/issues/45129) + +struct Foo { x: [usize; 2] } + +static mut SFOO: Foo = Foo { x: [23, 32] }; + +impl Foo { + fn x(&mut self) -> &mut usize { &mut self.x[0] } +} fn main() { - let long; - let mut short = 0; - long = borrow(&mut short); - //~^ NOTE borrow occurs here + unsafe { + let sfoo: *mut Foo = &mut SFOO; + let x = (*sfoo).x(); + (*sfoo).x[1] += 1; + *x += 1; + } } -//~^ ERROR `short` does not live long enough -//~| NOTE `short` dropped here while still borrowed -//~| NOTE values in a scope are dropped in the opposite order they are created diff --git a/src/test/run-pass/box-of-array-of-drop-1.rs b/src/test/run-pass/box-of-array-of-drop-1.rs index 47b44863a7475..db055e6886a83 100644 --- a/src/test/run-pass/box-of-array-of-drop-1.rs +++ b/src/test/run-pass/box-of-array-of-drop-1.rs @@ -13,8 +13,6 @@ // ignore-emscripten no threads support -#![feature(const_atomic_usize_new)] - use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/test/run-pass/box-of-array-of-drop-2.rs b/src/test/run-pass/box-of-array-of-drop-2.rs index 54be4955baf5b..9dde53bb31dc5 100644 --- a/src/test/run-pass/box-of-array-of-drop-2.rs +++ b/src/test/run-pass/box-of-array-of-drop-2.rs @@ -13,8 +13,6 @@ // ignore-emscripten no threads support -#![feature(const_atomic_usize_new)] - use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/test/run-pass/builtin-clone-unwind.rs b/src/test/run-pass/builtin-clone-unwind.rs index 90a411352869c..7e7c5ee412556 100644 --- a/src/test/run-pass/builtin-clone-unwind.rs +++ b/src/test/run-pass/builtin-clone-unwind.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + // Test that builtin implementations of `Clone` cleanup everything // in case of unwinding. diff --git a/src/test/run-pass/c-stack-as-value.rs b/src/test/run-pass/c-stack-as-value.rs index 5319693405b5d..df4989d89ab5e 100644 --- a/src/test/run-pass/c-stack-as-value.rs +++ b/src/test/run-pass/c-stack-as-value.rs @@ -9,6 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/c-stack-returning-int64.rs b/src/test/run-pass/c-stack-returning-int64.rs index 84f22025a1d7a..46b3fd1f1e746 100644 --- a/src/test/run-pass/c-stack-returning-int64.rs +++ b/src/test/run-pass/c-stack-returning-int64.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +// ignore-wasm32-bare no libc to test with #![feature(libc, std_misc)] diff --git a/src/test/run-pass/cabi-int-widening.rs b/src/test/run-pass/cabi-int-widening.rs index bf94dd178821a..5b1677c184c74 100644 --- a/src/test/run-pass/cabi-int-widening.rs +++ b/src/test/run-pass/cabi-int-widening.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #[link(name = "rust_test_helpers", kind = "static")] extern { fn rust_int8_to_int32(_: i8) -> i32; diff --git a/src/test/run-pass/catch-unwind-bang.rs b/src/test/run-pass/catch-unwind-bang.rs index df54ec90022ee..849132b8ebfab 100644 --- a/src/test/run-pass/catch-unwind-bang.rs +++ b/src/test/run-pass/catch-unwind-bang.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + fn worker() -> ! { panic!() } diff --git a/src/test/run-pass/cfg-family.rs b/src/test/run-pass/cfg-family.rs index 415607aa72bfc..1797ad1019c3d 100644 --- a/src/test/run-pass/cfg-family.rs +++ b/src/test/run-pass/cfg-family.rs @@ -9,6 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no target_family #[cfg(windows)] pub fn main() { diff --git a/src/test/run-pass/cfg-target-family.rs b/src/test/run-pass/cfg-target-family.rs index b6954f7c2eeaf..0b8574e117478 100644 --- a/src/test/run-pass/cfg-target-family.rs +++ b/src/test/run-pass/cfg-target-family.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no target_family + // pretty-expanded FIXME #23616 #[cfg(target_family = "windows")] diff --git a/src/test/run-pass/check-static-recursion-foreign.rs b/src/test/run-pass/check-static-recursion-foreign.rs index 8e718f328ff91..9c87f2ca68203 100644 --- a/src/test/run-pass/check-static-recursion-foreign.rs +++ b/src/test/run-pass/check-static-recursion-foreign.rs @@ -11,6 +11,7 @@ // Static recursion check shouldn't fail when given a foreign item (#18279) // aux-build:check_static_recursion_foreign_helper.rs +// ignore-wasm32-bare no libc to test ffi with // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/closure-expected-type/README.md b/src/test/run-pass/closure-expected-type/README.md new file mode 100644 index 0000000000000..fd493e1ff37d6 --- /dev/null +++ b/src/test/run-pass/closure-expected-type/README.md @@ -0,0 +1,8 @@ +Some tests targeted at how we deduce the types of closure arguments. +This process is a result of some heuristics aimed at combining the +*expected type* we have with the *actual types* that we get from +inputs. This investigation was kicked off by #38714, which revealed +some pretty deep flaws in the ad-hoc way that we were doing things +before. + +See also `src/test/compile-fail/closure-expected-type`. diff --git a/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs b/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs new file mode 100644 index 0000000000000..8a90a491f7e58 --- /dev/null +++ b/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs @@ -0,0 +1,26 @@ +// 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. + +fn with_closure(_: F) + where F: FnOnce(Vec, A) +{ +} + +fn expect_free_supply_free<'x>(x: &'x u32) { + with_closure(|mut x: Vec<_>, y| { + // Shows that the call to `x.push()` is influencing type of `y`... + x.push(22_u32); + + // ...since we now know the type of `y` and can resolve the method call. + y.wrapping_add(1); + }); +} + +fn main() { } diff --git a/src/test/run-pass/closure-expected-type/issue-38714.rs b/src/test/run-pass/closure-expected-type/issue-38714.rs new file mode 100644 index 0000000000000..a1d512105c933 --- /dev/null +++ b/src/test/run-pass/closure-expected-type/issue-38714.rs @@ -0,0 +1,26 @@ +// 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. + +struct UsizeRef<'a> { + a: &'a usize +} + +type RefTo = Box Fn(&'r Vec) -> UsizeRef<'r>>; + +fn ref_to<'a>(vec: &'a Vec) -> UsizeRef<'a> { + UsizeRef{ a: &vec[0]} +} + +fn main() { + // Regression test: this was causing ICEs; it should compile. + let a: RefTo = Box::new(|vec: &Vec| { + UsizeRef{ a: &vec[0] } + }); +} diff --git a/src/test/run-pass/closure-expected-type/supply-just-return-type.rs b/src/test/run-pass/closure-expected-type/supply-just-return-type.rs new file mode 100644 index 0000000000000..0b930b338fd72 --- /dev/null +++ b/src/test/run-pass/closure-expected-type/supply-just-return-type.rs @@ -0,0 +1,35 @@ +// 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. + +fn with_closure(f: F) -> Result + where F: FnOnce(&char) -> Result, +{ + f(&'a') +} + +fn main() { + // Test that supplying the `-> Result` manually here + // (which is needed to constrain `R`) still allows us to figure + // out that the type of `x` is `&'a char` where `'a` is bound in + // the closure (if we didn't, we'd get a type-error because + // `with_closure` requires a bound region). + // + // This pattern was found in the wild. + let z = with_closure(|x| -> Result { Ok(*x) }); + assert_eq!(z.unwrap(), 'a'); + + // It also works with `_`: + let z = with_closure(|x: _| -> Result { Ok(*x) }); + assert_eq!(z.unwrap(), 'a'); + + // It also works with `&_`: + let z = with_closure(|x: &_| -> Result { Ok(*x) }); + assert_eq!(z.unwrap(), 'a'); +} diff --git a/src/test/run-pass/closure-expected-type/supply-nothing.rs b/src/test/run-pass/closure-expected-type/supply-nothing.rs new file mode 100644 index 0000000000000..15d8b393c152f --- /dev/null +++ b/src/test/run-pass/closure-expected-type/supply-nothing.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn with_closure(f: F) -> u32 + where F: FnOnce(&u32, &u32) -> u32 +{ + f(&22, &44) +} + +fn main() { + let z = with_closure(|x, y| x + y).wrapping_add(1); + assert_eq!(z, 22 + 44 + 1); +} diff --git a/src/test/run-pass/command-before-exec.rs b/src/test/run-pass/command-before-exec.rs index 5b83ce48e5dad..9e782cca218fd 100644 --- a/src/test/run-pass/command-before-exec.rs +++ b/src/test/run-pass/command-before-exec.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-windows - this is a unix-specific test -// ignore-emscripten +// ignore-emscripten no processes #![feature(process_exec, libc)] diff --git a/src/test/run-pass/command-exec.rs b/src/test/run-pass/command-exec.rs index 5be9b97aac7e6..e378f55dffa1e 100644 --- a/src/test/run-pass/command-exec.rs +++ b/src/test/run-pass/command-exec.rs @@ -10,7 +10,8 @@ // ignore-windows - this is a unix-specific test // ignore-pretty issue #37199 -// ignore-emscripten +// ignore-emscripten no processes + #![feature(process_exec)] use std::env; diff --git a/src/test/run-pass/const-cast.rs b/src/test/run-pass/const-cast.rs index 411df2b3e0759..e77fd5f71394e 100644 --- a/src/test/run-pass/const-cast.rs +++ b/src/test/run-pass/const-cast.rs @@ -8,21 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(libc)] - -extern crate libc; - struct TestStruct { - x: *const libc::c_void + x: *const u8, } unsafe impl Sync for TestStruct {} extern fn foo() {} const x: extern "C" fn() = foo; -static y: TestStruct = TestStruct { x: x as *const libc::c_void }; +static y: TestStruct = TestStruct { x: x as *const u8 }; pub fn main() { - assert_eq!(x as *const libc::c_void, y.x); + assert_eq!(x as *const u8, y.x); } diff --git a/src/test/run-pass/const-fn-feature-flags.rs b/src/test/run-pass/const-fn-feature-flags.rs index 1e27a3edac808..a7736a2eb3431 100644 --- a/src/test/run-pass/const-fn-feature-flags.rs +++ b/src/test/run-pass/const-fn-feature-flags.rs @@ -8,9 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test use of const fns in std using individual feature gates. - -#![feature(const_cell_new)] +// Test use of stabilized const fns in std formerly using individual feature gates. use std::cell::Cell; diff --git a/src/test/run-pass/const-size_of-align_of.rs b/src/test/run-pass/const-size_of-align_of.rs index d5547ea5add00..06fbe9bf4f639 100644 --- a/src/test/run-pass/const-size_of-align_of.rs +++ b/src/test/run-pass/const-size_of-align_of.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_fn, const_size_of, const_align_of)] +#![feature(const_fn)] use std::mem; diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs index 22fbeb2d5d0ed..863f3cf30e96a 100644 --- a/src/test/run-pass/core-run-destroy.rs +++ b/src/test/run-pass/core-run-destroy.rs @@ -9,7 +9,7 @@ // except according to those terms. // compile-flags:--test -// ignore-emscripten +// ignore-emscripten no processes // NB: These tests kill child processes. Valgrind sees these children as leaking // memory, which makes for some *confusing* logs. That's why these are here diff --git a/src/test/run-pass/ctfe/assoc-const.rs b/src/test/run-pass/ctfe/assoc-const.rs new file mode 100644 index 0000000000000..6a740dc1dd30b --- /dev/null +++ b/src/test/run-pass/ctfe/assoc-const.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Nat { + const VALUE: usize; +} + +struct Zero; +struct Succ(N); + +impl Nat for Zero { + const VALUE: usize = 0; +} + +impl Nat for Succ { + const VALUE: usize = N::VALUE + 1; +} + +fn main() { + let x: [i32; >>>>::VALUE] = [1, 2, 3, 4]; +} diff --git a/src/test/run-pass/dead-code-alias-in-pat.rs b/src/test/run-pass/dead-code-alias-in-pat.rs new file mode 100644 index 0000000000000..a37d671e5c171 --- /dev/null +++ b/src/test/run-pass/dead-code-alias-in-pat.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(dead_code)] + +fn main() { + struct Foo { x: T } + type Bar = Foo; + let spam = |Bar { x }| x != 0; + println!("{}", spam(Foo { x: 10 })); +} diff --git a/src/test/run-pass/deriving-with-repr-packed.rs b/src/test/run-pass/deriving-with-repr-packed.rs new file mode 100644 index 0000000000000..f5130908c0b21 --- /dev/null +++ b/src/test/run-pass/deriving-with-repr-packed.rs @@ -0,0 +1,45 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// check that derive on a packed struct does not call field +// methods with a misaligned field. + +use std::mem; + +#[derive(Copy, Clone)] +struct Aligned(usize); + +#[inline(never)] +fn check_align(ptr: *const Aligned) { + assert_eq!(ptr as usize % mem::align_of::(), + 0); +} + +impl PartialEq for Aligned { + fn eq(&self, other: &Self) -> bool { + check_align(self); + check_align(other); + self.0 == other.0 + } +} + +#[repr(packed)] +#[derive(Copy, Clone, PartialEq)] +struct Packed(Aligned, Aligned); + +#[derive(PartialEq)] +#[repr(C)] +struct Dealigned(u8, T); + +fn main() { + let d1 = Dealigned(0, Packed(Aligned(1), Aligned(2))); + let ck = d1 == d1; + assert!(ck); +} diff --git a/src/test/run-pass/diverging-fn-tail-35849.rs b/src/test/run-pass/diverging-fn-tail-35849.rs index 6c05a02e7183c..dfd99bcc9fb47 100644 --- a/src/test/run-pass/diverging-fn-tail-35849.rs +++ b/src/test/run-pass/diverging-fn-tail-35849.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[allow(coerce_never)] fn assert_sizeof() -> ! { unsafe { ::std::mem::transmute::(panic!()) @@ -15,4 +16,3 @@ fn assert_sizeof() -> ! { } fn main() { } - diff --git a/src/test/run-pass/duplicated-external-mods.rs b/src/test/run-pass/duplicated-external-mods.rs index 91c9887300935..4cb3dbe027a16 100644 --- a/src/test/run-pass/duplicated-external-mods.rs +++ b/src/test/run-pass/duplicated-external-mods.rs @@ -11,6 +11,7 @@ // aux-build:anon-extern-mod-cross-crate-1.rs // aux-build:anon-extern-mod-cross-crate-1.rs // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test ffi with extern crate anonexternmod; diff --git a/src/test/run-pass/dyn-trait.rs b/src/test/run-pass/dyn-trait.rs new file mode 100644 index 0000000000000..91930852a57f6 --- /dev/null +++ b/src/test/run-pass/dyn-trait.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(dyn_trait)] + +use std::fmt::Display; + +static BYTE: u8 = 33; + +fn main() { + let x: &(dyn 'static + Display) = &BYTE; + let y: Box = Box::new(BYTE); + let xstr = format!("{}", x); + let ystr = format!("{}", y); + assert_eq!(xstr, "33"); + assert_eq!(ystr, "33"); +} diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 1aba47af1e9dc..09318e7256fd7 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(untagged_unions)] +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(generators, generator_trait, untagged_unions, slice_patterns, advanced_slice_patterns)] use std::cell::{Cell, RefCell}; +use std::ops::Generator; use std::panic; use std::usize; @@ -161,11 +164,64 @@ fn vec_simple(a: &Allocator) { let _x = vec![a.alloc(), a.alloc(), a.alloc(), a.alloc()]; } +fn generator(a: &Allocator, run_count: usize) { + assert!(run_count < 4); + + let mut gen = || { + (a.alloc(), + yield a.alloc(), + a.alloc(), + yield a.alloc() + ); + }; + for _ in 0..run_count { + gen.resume(); + } +} + +fn mixed_drop_and_nondrop(a: &Allocator) { + // check that destructor panics handle drop + // and non-drop blocks in the same scope correctly. + // + // Surprisingly enough, this used to not work. + let (x, y, z); + x = a.alloc(); + y = 5; + z = a.alloc(); +} + #[allow(unreachable_code)] fn vec_unreachable(a: &Allocator) { let _x = vec![a.alloc(), a.alloc(), a.alloc(), return]; } +fn slice_pattern_first(a: &Allocator) { + let[_x, ..] = [a.alloc(), a.alloc(), a.alloc()]; +} + +fn slice_pattern_middle(a: &Allocator) { + let[_, _x, _] = [a.alloc(), a.alloc(), a.alloc()]; +} + +fn slice_pattern_two(a: &Allocator) { + let[_x, _, _y, _] = [a.alloc(), a.alloc(), a.alloc(), a.alloc()]; +} + +fn slice_pattern_last(a: &Allocator) { + let[.., _y] = [a.alloc(), a.alloc(), a.alloc(), a.alloc()]; +} + +fn slice_pattern_one_of(a: &Allocator, i: usize) { + let array = [a.alloc(), a.alloc(), a.alloc(), a.alloc()]; + let _x = match i { + 0 => { let [a, ..] = array; a } + 1 => { let [_, a, ..] = array; a } + 2 => { let [_, _, a, _] = array; a } + 3 => { let [_, _, _, a] = array; a } + _ => panic!("unmatched"), + }; +} + fn run_test(mut f: F) where F: FnMut(&Allocator) { @@ -228,5 +284,21 @@ fn main() { run_test(|a| field_assignment(a, false)); run_test(|a| field_assignment(a, true)); + run_test(|a| generator(a, 0)); + run_test(|a| generator(a, 1)); + run_test(|a| generator(a, 2)); + run_test(|a| generator(a, 3)); + + run_test(|a| mixed_drop_and_nondrop(a)); + + run_test(|a| slice_pattern_first(a)); + run_test(|a| slice_pattern_middle(a)); + run_test(|a| slice_pattern_two(a)); + run_test(|a| slice_pattern_last(a)); + run_test(|a| slice_pattern_one_of(a, 0)); + run_test(|a| slice_pattern_one_of(a, 1)); + run_test(|a| slice_pattern_one_of(a, 2)); + run_test(|a| slice_pattern_one_of(a, 3)); + run_test_nopanic(|a| union1(a)); } diff --git a/src/test/run-pass/enum-discrim-manual-sizing.rs b/src/test/run-pass/enum-discrim-manual-sizing.rs index 3bbc107e0b99e..8557c065dc69c 100644 --- a/src/test/run-pass/enum-discrim-manual-sizing.rs +++ b/src/test/run-pass/enum-discrim-manual-sizing.rs @@ -108,6 +108,9 @@ pub fn main() { let array_expected_size = round_up(28, align_of::>()); assert_eq!(size_of::>(), array_expected_size); assert_eq!(size_of::>(), 32); + + assert_eq!(align_of::(), align_of::()); + assert_eq!(align_of::>(), align_of::()); } // Rounds x up to the next multiple of a diff --git a/src/test/run-pass/enum-non-c-like-repr-c-and-int.rs b/src/test/run-pass/enum-non-c-like-repr-c-and-int.rs new file mode 100644 index 0000000000000..86453fdf6fae6 --- /dev/null +++ b/src/test/run-pass/enum-non-c-like-repr-c-and-int.rs @@ -0,0 +1,177 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test deserializes an enum in-place by transmuting to a union that +// should have the same layout, and manipulating the tag and payloads +// independently. This verifies that `repr(some_int)` has a stable representation, +// and that we don't miscompile these kinds of manipulations. + +use std::time::Duration; +use std::mem; + +#[repr(C, u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum MyEnum { + A(u32), // Single primitive value + B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal + C, // Empty + D(Option), // Contains an enum + E(Duration), // Contains a struct +} + +#[repr(C)] +struct MyEnumRepr { + tag: MyEnumTag, + payload: MyEnumPayload, +} + +#[repr(C)] +#[allow(non_snake_case)] +union MyEnumPayload { + A: MyEnumVariantA, + B: MyEnumVariantB, + D: MyEnumVariantD, + E: MyEnumVariantE, +} + +#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(u32); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16 } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(Option); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(Duration); + +fn main() { + let result: Vec> = vec![ + Ok(MyEnum::A(17)), + Ok(MyEnum::B { x: 206, y: 1145 }), + Ok(MyEnum::C), + Err(()), + Ok(MyEnum::D(Some(407))), + Ok(MyEnum::D(None)), + Ok(MyEnum::E(Duration::from_secs(100))), + Err(()), + ]; + + // Binary serialized version of the above (little-endian) + let input: Vec = vec![ + 0, 17, 0, 0, 0, + 1, 206, 121, 4, + 2, + 8, /* invalid tag value */ + 3, 0, 151, 1, 0, 0, + 3, 1, + 4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* incomplete value */ + ]; + + let mut output = vec![]; + let mut buf = &input[..]; + + unsafe { + // This should be safe, because we don't match on it unless it's fully formed, + // and it doesn't have a destructor. + let mut dest: MyEnum = mem::uninitialized(); + while buf.len() > 0 { + match parse_my_enum(&mut dest, &mut buf) { + Ok(()) => output.push(Ok(dest)), + Err(()) => output.push(Err(())), + } + } + } + + assert_eq!(output, result); +} + +fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> { + unsafe { + // Should be correct to do this transmute. + let dest: &'a mut MyEnumRepr = mem::transmute(dest); + let tag = read_u8(buf)?; + + dest.tag = match tag { + 0 => MyEnumTag::A, + 1 => MyEnumTag::B, + 2 => MyEnumTag::C, + 3 => MyEnumTag::D, + 4 => MyEnumTag::E, + _ => return Err(()), + }; + + match dest.tag { + MyEnumTag::A => { + dest.payload.A.0 = read_u32_le(buf)?; + } + MyEnumTag::B => { + dest.payload.B.x = read_u8(buf)?; + dest.payload.B.y = read_u16_le(buf)? as i16; + } + MyEnumTag::C => { + /* do nothing */ + } + MyEnumTag::D => { + let is_some = read_u8(buf)? == 0; + if is_some { + dest.payload.D.0 = Some(read_u32_le(buf)?); + } else { + dest.payload.D.0 = None; + } + } + MyEnumTag::E => { + let secs = read_u64_le(buf)?; + let nanos = read_u32_le(buf)?; + dest.payload.E.0 = Duration::new(secs, nanos); + } + } + Ok(()) + } +} + + + +// reader helpers + +fn read_u64_le(buf: &mut &[u8]) -> Result { + if buf.len() < 8 { return Err(()) } + let val = (buf[0] as u64) << 0 + | (buf[1] as u64) << 8 + | (buf[2] as u64) << 16 + | (buf[3] as u64) << 24 + | (buf[4] as u64) << 32 + | (buf[5] as u64) << 40 + | (buf[6] as u64) << 48 + | (buf[7] as u64) << 56; + *buf = &buf[8..]; + Ok(val) +} + +fn read_u32_le(buf: &mut &[u8]) -> Result { + if buf.len() < 4 { return Err(()) } + let val = (buf[0] as u32) << 0 + | (buf[1] as u32) << 8 + | (buf[2] as u32) << 16 + | (buf[3] as u32) << 24; + *buf = &buf[4..]; + Ok(val) +} + +fn read_u16_le(buf: &mut &[u8]) -> Result { + if buf.len() < 2 { return Err(()) } + let val = (buf[0] as u16) << 0 + | (buf[1] as u16) << 8; + *buf = &buf[2..]; + Ok(val) +} + +fn read_u8(buf: &mut &[u8]) -> Result { + if buf.len() < 1 { return Err(()) } + let val = buf[0]; + *buf = &buf[1..]; + Ok(val) +} diff --git a/src/test/run-pass/enum-non-c-like-repr-c.rs b/src/test/run-pass/enum-non-c-like-repr-c.rs new file mode 100644 index 0000000000000..b4e0fe8d4572a --- /dev/null +++ b/src/test/run-pass/enum-non-c-like-repr-c.rs @@ -0,0 +1,177 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test deserializes an enum in-place by transmuting to a union that +// should have the same layout, and manipulating the tag and payloads +// independently. This verifies that `repr(some_int)` has a stable representation, +// and that we don't miscompile these kinds of manipulations. + +use std::time::Duration; +use std::mem; + +#[repr(C)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum MyEnum { + A(u32), // Single primitive value + B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal + C, // Empty + D(Option), // Contains an enum + E(Duration), // Contains a struct +} + +#[repr(C)] +struct MyEnumRepr { + tag: MyEnumTag, + payload: MyEnumPayload, +} + +#[repr(C)] +#[allow(non_snake_case)] +union MyEnumPayload { + A: MyEnumVariantA, + B: MyEnumVariantB, + D: MyEnumVariantD, + E: MyEnumVariantE, +} + +#[repr(C)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(u32); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB {x: u8, y: i16 } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(Option); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(Duration); + +fn main() { + let result: Vec> = vec![ + Ok(MyEnum::A(17)), + Ok(MyEnum::B { x: 206, y: 1145 }), + Ok(MyEnum::C), + Err(()), + Ok(MyEnum::D(Some(407))), + Ok(MyEnum::D(None)), + Ok(MyEnum::E(Duration::from_secs(100))), + Err(()), + ]; + + // Binary serialized version of the above (little-endian) + let input: Vec = vec![ + 0, 17, 0, 0, 0, + 1, 206, 121, 4, + 2, + 8, /* invalid tag value */ + 3, 0, 151, 1, 0, 0, + 3, 1, + 4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* incomplete value */ + ]; + + let mut output = vec![]; + let mut buf = &input[..]; + + unsafe { + // This should be safe, because we don't match on it unless it's fully formed, + // and it doesn't have a destructor. + let mut dest: MyEnum = mem::uninitialized(); + while buf.len() > 0 { + match parse_my_enum(&mut dest, &mut buf) { + Ok(()) => output.push(Ok(dest)), + Err(()) => output.push(Err(())), + } + } + } + + assert_eq!(output, result); +} + +fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> { + unsafe { + // Should be correct to do this transmute. + let dest: &'a mut MyEnumRepr = mem::transmute(dest); + let tag = read_u8(buf)?; + + dest.tag = match tag { + 0 => MyEnumTag::A, + 1 => MyEnumTag::B, + 2 => MyEnumTag::C, + 3 => MyEnumTag::D, + 4 => MyEnumTag::E, + _ => return Err(()), + }; + + match dest.tag { + MyEnumTag::A => { + dest.payload.A.0 = read_u32_le(buf)?; + } + MyEnumTag::B => { + dest.payload.B.x = read_u8(buf)?; + dest.payload.B.y = read_u16_le(buf)? as i16; + } + MyEnumTag::C => { + /* do nothing */ + } + MyEnumTag::D => { + let is_some = read_u8(buf)? == 0; + if is_some { + dest.payload.D.0 = Some(read_u32_le(buf)?); + } else { + dest.payload.D.0 = None; + } + } + MyEnumTag::E => { + let secs = read_u64_le(buf)?; + let nanos = read_u32_le(buf)?; + dest.payload.E.0 = Duration::new(secs, nanos); + } + } + Ok(()) + } +} + + + +// reader helpers + +fn read_u64_le(buf: &mut &[u8]) -> Result { + if buf.len() < 8 { return Err(()) } + let val = (buf[0] as u64) << 0 + | (buf[1] as u64) << 8 + | (buf[2] as u64) << 16 + | (buf[3] as u64) << 24 + | (buf[4] as u64) << 32 + | (buf[5] as u64) << 40 + | (buf[6] as u64) << 48 + | (buf[7] as u64) << 56; + *buf = &buf[8..]; + Ok(val) +} + +fn read_u32_le(buf: &mut &[u8]) -> Result { + if buf.len() < 4 { return Err(()) } + let val = (buf[0] as u32) << 0 + | (buf[1] as u32) << 8 + | (buf[2] as u32) << 16 + | (buf[3] as u32) << 24; + *buf = &buf[4..]; + Ok(val) +} + +fn read_u16_le(buf: &mut &[u8]) -> Result { + if buf.len() < 2 { return Err(()) } + let val = (buf[0] as u16) << 0 + | (buf[1] as u16) << 8; + *buf = &buf[2..]; + Ok(val) +} + +fn read_u8(buf: &mut &[u8]) -> Result { + if buf.len() < 1 { return Err(()) } + let val = buf[0]; + *buf = &buf[1..]; + Ok(val) +} diff --git a/src/test/run-pass/enum-non-c-like-repr-int.rs b/src/test/run-pass/enum-non-c-like-repr-int.rs new file mode 100644 index 0000000000000..6e147b00ef9af --- /dev/null +++ b/src/test/run-pass/enum-non-c-like-repr-int.rs @@ -0,0 +1,173 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test deserializes an enum in-place by transmuting to a union that +// should have the same layout, and manipulating the tag and payloads +// independently. This verifies that `repr(some_int)` has a stable representation, +// and that we don't miscompile these kinds of manipulations. + +use std::time::Duration; +use std::mem; + +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum MyEnum { + A(u32), // Single primitive value + B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal + C, // Empty + D(Option), // Contains an enum + E(Duration), // Contains a struct +} + +#[allow(non_snake_case)] +#[repr(C)] +union MyEnumRepr { + A: MyEnumVariantA, + B: MyEnumVariantB, + C: MyEnumVariantC, + D: MyEnumVariantD, + E: MyEnumVariantE, +} + +#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16 } +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option); +#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration); + +fn main() { + let result: Vec> = vec![ + Ok(MyEnum::A(17)), + Ok(MyEnum::B { x: 206, y: 1145 }), + Ok(MyEnum::C), + Err(()), + Ok(MyEnum::D(Some(407))), + Ok(MyEnum::D(None)), + Ok(MyEnum::E(Duration::from_secs(100))), + Err(()), + ]; + + // Binary serialized version of the above (little-endian) + let input: Vec = vec![ + 0, 17, 0, 0, 0, + 1, 206, 121, 4, + 2, + 8, /* invalid tag value */ + 3, 0, 151, 1, 0, 0, + 3, 1, + 4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* incomplete value */ + ]; + + let mut output = vec![]; + let mut buf = &input[..]; + + unsafe { + // This should be safe, because we don't match on it unless it's fully formed, + // and it doesn't have a destructor. + let mut dest: MyEnum = mem::uninitialized(); + while buf.len() > 0 { + match parse_my_enum(&mut dest, &mut buf) { + Ok(()) => output.push(Ok(dest)), + Err(()) => output.push(Err(())), + } + } + } + + assert_eq!(output, result); +} + +fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> { + unsafe { + // Should be correct to do this transmute. + let dest: &'a mut MyEnumRepr = mem::transmute(dest); + let tag = read_u8(buf)?; + + dest.A.0 = match tag { + 0 => MyEnumTag::A, + 1 => MyEnumTag::B, + 2 => MyEnumTag::C, + 3 => MyEnumTag::D, + 4 => MyEnumTag::E, + _ => return Err(()), + }; + + match dest.B.tag { + MyEnumTag::A => { + dest.A.1 = read_u32_le(buf)?; + } + MyEnumTag::B => { + dest.B.x = read_u8(buf)?; + dest.B.y = read_u16_le(buf)? as i16; + } + MyEnumTag::C => { + /* do nothing */ + } + MyEnumTag::D => { + let is_some = read_u8(buf)? == 0; + if is_some { + dest.D.1 = Some(read_u32_le(buf)?); + } else { + dest.D.1 = None; + } + } + MyEnumTag::E => { + let secs = read_u64_le(buf)?; + let nanos = read_u32_le(buf)?; + dest.E.1 = Duration::new(secs, nanos); + } + } + Ok(()) + } +} + + + +// reader helpers + +fn read_u64_le(buf: &mut &[u8]) -> Result { + if buf.len() < 8 { return Err(()) } + let val = (buf[0] as u64) << 0 + | (buf[1] as u64) << 8 + | (buf[2] as u64) << 16 + | (buf[3] as u64) << 24 + | (buf[4] as u64) << 32 + | (buf[5] as u64) << 40 + | (buf[6] as u64) << 48 + | (buf[7] as u64) << 56; + *buf = &buf[8..]; + Ok(val) +} + +fn read_u32_le(buf: &mut &[u8]) -> Result { + if buf.len() < 4 { return Err(()) } + let val = (buf[0] as u32) << 0 + | (buf[1] as u32) << 8 + | (buf[2] as u32) << 16 + | (buf[3] as u32) << 24; + *buf = &buf[4..]; + Ok(val) +} + +fn read_u16_le(buf: &mut &[u8]) -> Result { + if buf.len() < 2 { return Err(()) } + let val = (buf[0] as u16) << 0 + | (buf[1] as u16) << 8; + *buf = &buf[2..]; + Ok(val) +} + +fn read_u8(buf: &mut &[u8]) -> Result { + if buf.len() < 1 { return Err(()) } + let val = buf[0]; + *buf = &buf[1..]; + Ok(val) +} diff --git a/src/test/run-pass/enum-univariant-repr.rs b/src/test/run-pass/enum-univariant-repr.rs index ef4cc60bf0da1..17d614b54969c 100644 --- a/src/test/run-pass/enum-univariant-repr.rs +++ b/src/test/run-pass/enum-univariant-repr.rs @@ -22,6 +22,11 @@ enum UnivariantWithoutDescr { Y } +#[repr(u8)] +enum UnivariantWithData { + Z(u8), +} + pub fn main() { { assert_eq!(4, mem::size_of::()); @@ -44,4 +49,12 @@ pub fn main() { // check it has the same memory layout as u16 assert_eq!(&[descr, descr, descr], ints); } + + { + assert_eq!(2, mem::size_of::()); + + match UnivariantWithData::Z(4) { + UnivariantWithData::Z(x) => assert_eq!(x, 4), + } + } } diff --git a/src/test/run-pass/env-args-reverse-iterator.rs b/src/test/run-pass/env-args-reverse-iterator.rs index 89af1db7c78f1..dddf1ae054659 100644 --- a/src/test/run-pass/env-args-reverse-iterator.rs +++ b/src/test/run-pass/env-args-reverse-iterator.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::env::args; use std::process::Command; diff --git a/src/test/run-pass/env-funky-keys.rs b/src/test/run-pass/env-funky-keys.rs index 8b4a633d61358..86ffaf1e24f62 100644 --- a/src/test/run-pass/env-funky-keys.rs +++ b/src/test/run-pass/env-funky-keys.rs @@ -12,7 +12,7 @@ // ignore-android // ignore-windows -// ignore-emscripten +// ignore-emscripten no execve // no-prefer-dynamic #![feature(libc)] diff --git a/src/test/run-pass/env-home-dir.rs b/src/test/run-pass/env-home-dir.rs index bcb0c62d9fef4..3693e86ba2428 100644 --- a/src/test/run-pass/env-home-dir.rs +++ b/src/test/run-pass/env-home-dir.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten env vars don't work? #![feature(path)] diff --git a/src/test/run-pass/env-vars.rs b/src/test/run-pass/env-vars.rs index 933d9a728dbe7..af44a6567e405 100644 --- a/src/test/run-pass/env-vars.rs +++ b/src/test/run-pass/env-vars.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no env vars use std::env::*; diff --git a/src/test/run-pass/extern-call-deep.rs b/src/test/run-pass/extern-call-deep.rs index 6a9da767ad6eb..a41ab8da9de11 100644 --- a/src/test/run-pass/extern-call-deep.rs +++ b/src/test/run-pass/extern-call-deep.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #![feature(libc)] extern crate libc; diff --git a/src/test/run-pass/extern-call-indirect.rs b/src/test/run-pass/extern-call-indirect.rs index 256eedccb8bfa..ba108955c28d6 100644 --- a/src/test/run-pass/extern-call-indirect.rs +++ b/src/test/run-pass/extern-call-indirect.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #![feature(libc)] extern crate libc; diff --git a/src/test/run-pass/extern-crosscrate.rs b/src/test/run-pass/extern-crosscrate.rs index 7157d0658be34..825b502d95485 100644 --- a/src/test/run-pass/extern-crosscrate.rs +++ b/src/test/run-pass/extern-crosscrate.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//aux-build:extern-crosscrate-source.rs +// aux-build:extern-crosscrate-source.rs +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/extern-pass-TwoU16s.rs b/src/test/run-pass/extern-pass-TwoU16s.rs index afdd53db775a8..a1aa9b69fadaa 100644 --- a/src/test/run-pass/extern-pass-TwoU16s.rs +++ b/src/test/run-pass/extern-pass-TwoU16s.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a foreign function that accepts and returns a struct // by value. diff --git a/src/test/run-pass/extern-pass-TwoU32s.rs b/src/test/run-pass/extern-pass-TwoU32s.rs index 035084ae9bd3a..6442f230d3024 100644 --- a/src/test/run-pass/extern-pass-TwoU32s.rs +++ b/src/test/run-pass/extern-pass-TwoU32s.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a foreign function that accepts and returns a struct // by value. diff --git a/src/test/run-pass/extern-pass-TwoU64s.rs b/src/test/run-pass/extern-pass-TwoU64s.rs index cb1a4d278256a..bfb649c3f74ea 100644 --- a/src/test/run-pass/extern-pass-TwoU64s.rs +++ b/src/test/run-pass/extern-pass-TwoU64s.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a foreign function that accepts and returns a struct // by value. diff --git a/src/test/run-pass/extern-pass-TwoU8s.rs b/src/test/run-pass/extern-pass-TwoU8s.rs index 657348c99aad8..1f90d9c436677 100644 --- a/src/test/run-pass/extern-pass-TwoU8s.rs +++ b/src/test/run-pass/extern-pass-TwoU8s.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a foreign function that accepts and returns a struct // by value. diff --git a/src/test/run-pass/extern-pass-char.rs b/src/test/run-pass/extern-pass-char.rs index 9042aed6639b8..920e15bf2c119 100644 --- a/src/test/run-pass/extern-pass-char.rs +++ b/src/test/run-pass/extern-pass-char.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a function that takes/returns a u8. diff --git a/src/test/run-pass/extern-pass-double.rs b/src/test/run-pass/extern-pass-double.rs index 38d29180fbc8f..9c184477ad340 100644 --- a/src/test/run-pass/extern-pass-double.rs +++ b/src/test/run-pass/extern-pass-double.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing #[link(name = "rust_test_helpers", kind = "static")] extern { diff --git a/src/test/run-pass/extern-pass-empty.rs b/src/test/run-pass/extern-pass-empty.rs index 2606c92868004..f4ee3b6e9e800 100644 --- a/src/test/run-pass/extern-pass-empty.rs +++ b/src/test/run-pass/extern-pass-empty.rs @@ -12,7 +12,7 @@ // pretty-expanded FIXME #23616 // ignore-msvc -// ignore-emscripten +// ignore-emscripten emcc asserts on an empty struct as an argument #[repr(C)] struct TwoU8s { diff --git a/src/test/run-pass/extern-pass-u32.rs b/src/test/run-pass/extern-pass-u32.rs index ed254ac46f20b..691bd6295b143 100644 --- a/src/test/run-pass/extern-pass-u32.rs +++ b/src/test/run-pass/extern-pass-u32.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a function that takes/returns a u32. diff --git a/src/test/run-pass/extern-pass-u64.rs b/src/test/run-pass/extern-pass-u64.rs index 6fc630e6d7e1a..4b351ed2836cc 100644 --- a/src/test/run-pass/extern-pass-u64.rs +++ b/src/test/run-pass/extern-pass-u64.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc for ffi testing + // Test a call to a function that takes/returns a u64. diff --git a/src/test/run-pass/extern-return-TwoU16s.rs b/src/test/run-pass/extern-return-TwoU16s.rs index ec1c6130e7adc..b9ce3f4588688 100644 --- a/src/test/run-pass/extern-return-TwoU16s.rs +++ b/src/test/run-pass/extern-return-TwoU16s.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with pub struct TwoU16s { one: u16, two: u16 diff --git a/src/test/run-pass/extern-return-TwoU32s.rs b/src/test/run-pass/extern-return-TwoU32s.rs index e829e993052a5..686ff16fe4d6b 100644 --- a/src/test/run-pass/extern-return-TwoU32s.rs +++ b/src/test/run-pass/extern-return-TwoU32s.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with pub struct TwoU32s { one: u32, two: u32 diff --git a/src/test/run-pass/extern-return-TwoU64s.rs b/src/test/run-pass/extern-return-TwoU64s.rs index ef7325b33fe3d..e754674fd1ebd 100644 --- a/src/test/run-pass/extern-return-TwoU64s.rs +++ b/src/test/run-pass/extern-return-TwoU64s.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with pub struct TwoU64s { one: u64, two: u64 diff --git a/src/test/run-pass/extern-return-TwoU8s.rs b/src/test/run-pass/extern-return-TwoU8s.rs index 46f2e81a5564a..68fe3832e90ab 100644 --- a/src/test/run-pass/extern-return-TwoU8s.rs +++ b/src/test/run-pass/extern-return-TwoU8s.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with pub struct TwoU8s { one: u8, two: u8 diff --git a/src/test/run-pass/extern-types-inherent-impl.rs b/src/test/run-pass/extern-types-inherent-impl.rs new file mode 100644 index 0000000000000..4e44af3690064 --- /dev/null +++ b/src/test/run-pass/extern-types-inherent-impl.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that inherent impls can be defined for extern types. + +#![feature(extern_types)] + +extern { + type A; +} + +impl A { + fn foo(&self) { } +} + +fn use_foo(x: &A) { + x.foo(); +} + +fn main() { } diff --git a/src/test/run-pass/extern-types-manual-sync-send.rs b/src/test/run-pass/extern-types-manual-sync-send.rs new file mode 100644 index 0000000000000..c6530c3ea773a --- /dev/null +++ b/src/test/run-pass/extern-types-manual-sync-send.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that unsafe impl for Sync/Send can be provided for extern types. + +#![feature(extern_types)] + +extern { + type A; +} + +unsafe impl Sync for A { } +unsafe impl Send for A { } + +fn assert_sync() { } +fn assert_send() { } + +fn main() { + assert_sync::(); + assert_send::(); +} diff --git a/src/test/run-pass/extern-types-pointer-cast.rs b/src/test/run-pass/extern-types-pointer-cast.rs new file mode 100644 index 0000000000000..628a570665a33 --- /dev/null +++ b/src/test/run-pass/extern-types-pointer-cast.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that pointers to extern types can be casted from/to usize, +// despite being !Sized. + +#![feature(extern_types)] + +extern { + type A; +} + +struct Foo { + x: u8, + tail: A, +} + +struct Bar { + x: u8, + tail: T, +} + +#[cfg(target_pointer_width = "32")] +const MAGIC: usize = 0xdeadbeef; +#[cfg(target_pointer_width = "64")] +const MAGIC: usize = 0x12345678deadbeef; + +fn main() { + assert_eq!((MAGIC as *const A) as usize, MAGIC); + assert_eq!((MAGIC as *const Foo) as usize, MAGIC); + assert_eq!((MAGIC as *const Bar) as usize, MAGIC); + assert_eq!((MAGIC as *const Bar>) as usize, MAGIC); +} diff --git a/src/test/run-pass/extern-types-size_of_val.rs b/src/test/run-pass/extern-types-size_of_val.rs new file mode 100644 index 0000000000000..0aabce99debe8 --- /dev/null +++ b/src/test/run-pass/extern-types-size_of_val.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(extern_types)] + +use std::mem::{size_of_val, align_of_val}; + +extern { + type A; +} + +fn main() { + let x: &A = unsafe { + &*(1usize as *const A) + }; + + assert_eq!(size_of_val(x), 0); + assert_eq!(align_of_val(x), 1); +} diff --git a/src/test/run-pass/extern-types-thin-pointer.rs b/src/test/run-pass/extern-types-thin-pointer.rs new file mode 100644 index 0000000000000..c2444a58b5a1b --- /dev/null +++ b/src/test/run-pass/extern-types-thin-pointer.rs @@ -0,0 +1,51 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that pointers and references to extern types are thin, ie they have the same size and +// alignment as a pointer to (). + +#![feature(extern_types)] + +use std::mem::{align_of, size_of}; + +extern { + type A; +} + +struct Foo { + x: u8, + tail: A, +} + +struct Bar { + x: u8, + tail: T, +} + +fn assert_thin() { + assert_eq!(size_of::<*const T>(), size_of::<*const ()>()); + assert_eq!(align_of::<*const T>(), align_of::<*const ()>()); + + assert_eq!(size_of::<*mut T>(), size_of::<*mut ()>()); + assert_eq!(align_of::<*mut T>(), align_of::<*mut ()>()); + + assert_eq!(size_of::<&T>(), size_of::<&()>()); + assert_eq!(align_of::<&T>(), align_of::<&()>()); + + assert_eq!(size_of::<&mut T>(), size_of::<&mut ()>()); + assert_eq!(align_of::<&mut T>(), align_of::<&mut ()>()); +} + +fn main() { + assert_thin::(); + assert_thin::(); + assert_thin::>(); + assert_thin::>>(); +} diff --git a/src/test/run-pass/extern-types-trait-impl.rs b/src/test/run-pass/extern-types-trait-impl.rs new file mode 100644 index 0000000000000..0f61c936deb61 --- /dev/null +++ b/src/test/run-pass/extern-types-trait-impl.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that traits can be implemented for extern types. + +#![feature(extern_types)] + +extern { + type A; +} + +trait Foo { + fn foo(&self) { } +} + +impl Foo for A { + fn foo(&self) { } +} + +fn assert_foo() { } + +fn use_foo(x: &Foo) { + x.foo(); +} + +fn main() { + assert_foo::(); +} diff --git a/src/test/run-pass/extern_fat_drop.rs b/src/test/run-pass/extern_fat_drop.rs index 8ce1f744dee17..123458ff97e13 100644 --- a/src/test/run-pass/extern_fat_drop.rs +++ b/src/test/run-pass/extern_fat_drop.rs @@ -14,7 +14,8 @@ extern crate fat_drop; fn main() { unsafe { - let s: &mut fat_drop::S = std::mem::uninitialized(); + let data: &mut [u8] = &mut [0]; + let s: &mut fat_drop::S = std::mem::transmute::<&mut [u8], _>(data); std::ptr::drop_in_place(s); assert!(fat_drop::DROPPED); } diff --git a/src/test/run-pass/fds-are-cloexec.rs b/src/test/run-pass/fds-are-cloexec.rs index a1b7d42a196e9..f55876115c09c 100644 --- a/src/test/run-pass/fds-are-cloexec.rs +++ b/src/test/run-pass/fds-are-cloexec.rs @@ -10,7 +10,7 @@ // ignore-windows // ignore-android -// ignore-emscripten +// ignore-emscripten no processes // ignore-haiku #![feature(libc)] diff --git a/src/test/run-pass/fmt-pointer-trait.rs b/src/test/run-pass/fmt-pointer-trait.rs index 96f31891f2f34..4ecb9da4eb9c6 100644 --- a/src/test/run-pass/fmt-pointer-trait.rs +++ b/src/test/run-pass/fmt-pointer-trait.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(libc)] -extern crate libc; use std::ptr; use std::rc::Rc; use std::sync::Arc; fn main() { - let p: *const libc::c_void = ptr::null(); + let p: *const u8 = ptr::null(); let rc = Rc::new(1usize); let arc = Arc::new(1usize); let b = Box::new("hi"); diff --git a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs index b36afcf87b3ee..0bfc4d2264c62 100644 --- a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs +++ b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs @@ -9,12 +9,12 @@ // except according to those terms. // Test that the type of `sum` falls back to `i32` here, -// and that the for loop desugaring doesn't inferfere with +// and that the for loop desugaring doesn't interfere with // that. fn main() { let mut sum = 0; for i in Vec::new() { - sum += i; + sum += &i; } } diff --git a/src/test/run-pass/foreign-dupe.rs b/src/test/run-pass/foreign-dupe.rs index fb162d8793356..b79e6e98fc056 100644 --- a/src/test/run-pass/foreign-dupe.rs +++ b/src/test/run-pass/foreign-dupe.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:foreign_lib.rs +// ignore-wasm32-bare no libc to test ffi with // Check that we can still call duplicated extern (imported) functions // which were declared in another crate. See issues #32740 and #32783. diff --git a/src/test/run-pass/foreign-fn-linkname.rs b/src/test/run-pass/foreign-fn-linkname.rs index a9001a3cdcf6e..ab8e4fbedcff7 100644 --- a/src/test/run-pass/foreign-fn-linkname.rs +++ b/src/test/run-pass/foreign-fn-linkname.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +// ignore-wasm32-bare no libc to test ffi with #![feature(std_misc, libc)] diff --git a/src/test/run-pass/foreign-fn-with-byval.rs b/src/test/run-pass/foreign-fn-with-byval.rs index 2d4542540e7a3..65efa571a9b4b 100644 --- a/src/test/run-pass/foreign-fn-with-byval.rs +++ b/src/test/run-pass/foreign-fn-with-byval.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with #[derive(Copy, Clone)] pub struct S { diff --git a/src/test/run-pass/foreign-mod-unused-const.rs b/src/test/run-pass/foreign-mod-unused-const.rs index 70d4801ae3b3c..5e9e529669a01 100644 --- a/src/test/run-pass/foreign-mod-unused-const.rs +++ b/src/test/run-pass/foreign-mod-unused-const.rs @@ -8,18 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - // pretty-expanded FIXME #23616 -#![feature(libc)] - -extern crate libc; - mod foo { - use libc::c_int; - extern { - pub static errno: c_int; + pub static errno: u32; } } diff --git a/src/test/run-pass/foreign-no-abi.rs b/src/test/run-pass/foreign-no-abi.rs index 979e57eba9d1a..b516cdf4416b8 100644 --- a/src/test/run-pass/foreign-no-abi.rs +++ b/src/test/run-pass/foreign-no-abi.rs @@ -10,6 +10,7 @@ // ABI is cdecl by default +// ignore-wasm32-bare no libc to test ffi with // pretty-expanded FIXME #23616 #![feature(libc)] diff --git a/src/test/run-pass/foreign2.rs b/src/test/run-pass/foreign2.rs index d83bd940d1978..d69d6976e4a90 100644 --- a/src/test/run-pass/foreign2.rs +++ b/src/test/run-pass/foreign2.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +// ignore-wasm32-bare no libc to test ffi with // pretty-expanded FIXME #23616 #![feature(libc)] diff --git a/src/test/run-pass/format-no-std.rs b/src/test/run-pass/format-no-std.rs index 9e8a32185188a..e23accfcc235d 100644 --- a/src/test/run-pass/format-no-std.rs +++ b/src/test/run-pass/format-no-std.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten missing rust_begin_unwind +// ignore-emscripten no no_std executables #![feature(lang_items, start, alloc)] #![no_std] diff --git a/src/test/run-pass/generator/panic-drops.rs b/src/test/run-pass/generator/panic-drops.rs index 53cd3235d9d0c..36e401a54bcdd 100644 --- a/src/test/run-pass/generator/panic-drops.rs +++ b/src/test/run-pass/generator/panic-drops.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled as panic=abort by default + #![feature(generators, generator_trait)] use std::ops::Generator; diff --git a/src/test/run-pass/generator/panic-safe.rs b/src/test/run-pass/generator/panic-safe.rs index a583f42b93d8c..0d5bae7876bd3 100644 --- a/src/test/run-pass/generator/panic-safe.rs +++ b/src/test/run-pass/generator/panic-safe.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + #![feature(generators, generator_trait)] use std::ops::Generator; diff --git a/src/test/run-pass/generator/resume-after-return.rs b/src/test/run-pass/generator/resume-after-return.rs index b2e2a3e7e9d5b..56511dcd53a6a 100644 --- a/src/test/run-pass/generator/resume-after-return.rs +++ b/src/test/run-pass/generator/resume-after-return.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + #![feature(generators, generator_trait)] use std::ops::{GeneratorState, Generator}; diff --git a/src/test/run-pass/generator/smoke.rs b/src/test/run-pass/generator/smoke.rs index e9bdfbf28ea9c..8693964665d34 100644 --- a/src/test/run-pass/generator/smoke.rs +++ b/src/test/run-pass/generator/smoke.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no threads support // compile-flags: --test #![feature(generators, generator_trait)] diff --git a/src/test/run-pass/hygiene/specialization.rs b/src/test/run-pass/hygiene/specialization.rs new file mode 100644 index 0000000000000..3d46d2ec99efb --- /dev/null +++ b/src/test/run-pass/hygiene/specialization.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +trait Tr { + fn f(&self) -> &'static str { + "This shouldn't happen" + } +} + +pub macro m($t:ty) { + impl Tr for $t { + fn f(&self) -> &'static str { + "Run me" + } + } +} + +struct S; +m!(S); + +fn main() { + assert_eq!(S.f(), "Run me"); +} diff --git a/src/test/run-pass/i128.rs b/src/test/run-pass/i128.rs index 7c14d34b0ee12..5369b138b0d58 100644 --- a/src/test/run-pass/i128.rs +++ b/src/test/run-pass/i128.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten i128 doesn't work #![feature(i128_type, test)] diff --git a/src/test/run-pass/impl-trait/auxiliary/xcrate.rs b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs index e9074f8c23096..e4f525a982610 100644 --- a/src/test/run-pass/impl-trait/auxiliary/xcrate.rs +++ b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs @@ -10,9 +10,10 @@ #![feature(conservative_impl_trait)] -pub fn fourway_add(a: i32) -> impl Fn(i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 { - move |b| move |c| move |d| a + b + c + d -} +// NOTE commented out due to issue #45994 +//pub fn fourway_add(a: i32) -> impl Fn(i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 { +// move |b| move |c| move |d| a + b + c + d +//} fn some_internal_fn() -> u32 { 1 diff --git a/src/test/run-pass/impl-trait/example-calendar.rs b/src/test/run-pass/impl-trait/example-calendar.rs index 84d86cfdf65a4..0b612c2d3ff34 100644 --- a/src/test/run-pass/impl-trait/example-calendar.rs +++ b/src/test/run-pass/impl-trait/example-calendar.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016-2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait, fn_traits, step_trait, unboxed_closures)] +#![feature(conservative_impl_trait, + universal_impl_trait, + fn_traits, + step_trait, + unboxed_closures +)] //! Derived from: . //! @@ -457,9 +462,9 @@ fn test_group_by() { /// /// Groups an iterator of dates by month. /// -fn by_month(it: It) - -> impl Iterator + Clone)> + Clone -where It: Iterator + Clone { +fn by_month(it: impl Iterator + Clone) + -> impl Iterator + Clone)> + Clone +{ it.group_by(|d| d.month()) } @@ -474,9 +479,9 @@ fn test_by_month() { /// /// Groups an iterator of dates by week. /// -fn by_week(it: It) - -> impl Iterator + Clone -where It: DateIterator { +fn by_week(it: impl DateIterator) + -> impl Iterator + Clone +{ // We go forward one day because `isoweekdate` considers the week to start on a Monday. it.group_by(|d| d.succ().isoweekdate().1) } @@ -548,8 +553,7 @@ const COLS_PER_WEEK: u32 = 7 * COLS_PER_DAY; /// /// Formats an iterator of weeks into an iterator of strings. /// -fn format_weeks(it: It) -> impl Iterator -where It: Iterator, It::Item: DateIterator { +fn format_weeks(it: impl Iterator) -> impl Iterator { it.map(|week| { let mut buf = String::with_capacity((COLS_PER_DAY * COLS_PER_WEEK + 2) as usize); @@ -627,7 +631,7 @@ fn test_month_title() { /// /// Formats a month. /// -fn format_month(it: It) -> impl Iterator { +fn format_month(it: impl DateIterator) -> impl Iterator { let mut month_days = it.peekable(); let title = month_title(month_days.peek().unwrap().month()); @@ -659,8 +663,9 @@ fn test_format_month() { /// /// Formats an iterator of months. /// -fn format_months(it: It) -> impl Iterator> -where It: Iterator, It::Item: DateIterator { +fn format_months(it: impl Iterator) + -> impl Iterator> +{ it.map(format_month) } diff --git a/src/test/run-pass/impl-trait/lifetimes.rs b/src/test/run-pass/impl-trait/lifetimes.rs new file mode 100644 index 0000000000000..1e19e7f6a132a --- /dev/null +++ b/src/test/run-pass/impl-trait/lifetimes.rs @@ -0,0 +1,97 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] +#![allow(warnings)] + +use std::fmt::Debug; + +fn any_lifetime<'a>() -> &'a u32 { &5 } + +fn static_lifetime() -> &'static u32 { &5 } + +fn any_lifetime_as_static_impl_trait() -> impl Debug { + any_lifetime() +} + +fn lifetimes_as_static_impl_trait() -> impl Debug { + static_lifetime() +} + +fn no_params_or_lifetimes_is_static() -> impl Debug + 'static { + lifetimes_as_static_impl_trait() +} + +fn static_input_type_is_static(x: T) -> impl Debug + 'static { x } + +fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x } + +trait SingleRegionTrait<'a> {} +impl<'a> SingleRegionTrait<'a> for u32 {} + +fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 } +fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () } + +fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () } +fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() } + +trait MultiRegionTrait<'a, 'b>: Debug {} + +#[derive(Debug)] +struct MultiRegionStruct<'a, 'b>(&'a u32, &'b u32); +impl<'a, 'b> MultiRegionTrait<'a, 'b> for MultiRegionStruct<'a, 'b> {} + +#[derive(Debug)] +struct NoRegionStruct; +impl<'a, 'b> MultiRegionTrait<'a, 'b> for NoRegionStruct {} + +fn finds_least_region<'a: 'b, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + MultiRegionStruct(x, y) +} + +fn finds_explicit_bound<'a: 'b, 'b> + (x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b +{ + MultiRegionStruct(x, y) +} + +fn finds_explicit_bound_even_without_least_region<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b +{ + NoRegionStruct +} + +/* FIXME: `impl Trait<'a> + 'b` should live as long as 'b, even if 'b outlives 'a +fn outlives_bounds_even_with_contained_regions<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl Debug + 'b +{ + finds_explicit_bound_even_without_least_region(x, y) +} +*/ + +fn unnamed_lifetimes_arent_contained_in_impl_trait_and_will_unify<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl Debug +{ + fn deref<'lt>(x: &'lt u32) -> impl Debug { *x } + + if true { deref(x) } else { deref(y) } +} + +fn can_add_region_bound_to_static_type<'a, 'b>(_: &'a u32) -> impl Debug + 'a { 5 } + +struct MyVec(Vec>); + +impl<'unnecessary_lifetime> MyVec { + fn iter_doesnt_capture_unnecessary_lifetime<'s>(&'s self) -> impl Iterator { + self.0.iter().flat_map(|inner_vec| inner_vec.iter()) + } +} + +fn main() {} diff --git a/src/test/run-pass/impl-trait/universal_hrtb_anon.rs b/src/test/run-pass/impl-trait/universal_hrtb_anon.rs new file mode 100644 index 0000000000000..48874ef41de55 --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_hrtb_anon.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +fn hrtb(f: impl Fn(&u32) -> u32) -> u32 { + f(&22) + f(&44) +} + +fn main() { + let sum = hrtb(|x| x * 2); + assert_eq!(sum, 22*2 + 44*2); +} diff --git a/src/test/run-pass/impl-trait/universal_hrtb_named.rs b/src/test/run-pass/impl-trait/universal_hrtb_named.rs new file mode 100644 index 0000000000000..95147a542005e --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_hrtb_named.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +fn hrtb(f: impl for<'a> Fn(&'a u32) -> &'a u32) -> u32 { + f(&22) + f(&44) +} + +fn main() { + let sum = hrtb(|x| x); + assert_eq!(sum, 22 + 44); +} diff --git a/src/test/run-pass/impl-trait/universal_in_adt_in_parameters.rs b/src/test/run-pass/impl-trait/universal_in_adt_in_parameters.rs new file mode 100644 index 0000000000000..d0f18575297b2 --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_in_adt_in_parameters.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] +use std::fmt::Display; + +fn check_display_eq(iter: &Vec) { + let mut collected = String::new(); + for it in iter { + let disp = format!("{} ", it); + collected.push_str(&disp); + } + assert_eq!("0 3 27 823 4891 1 0", collected.trim()); +} + +fn main() { + let i32_list_vec = vec![0i32, 3, 27, 823, 4891, 1, 0]; + let u32_list_vec = vec![0u32, 3, 27, 823, 4891, 1, 0]; + let str_list_vec = vec!["0", "3", "27", "823", "4891", "1", "0"]; + + check_display_eq(&i32_list_vec); + check_display_eq(&u32_list_vec); + check_display_eq(&str_list_vec); +} diff --git a/src/test/run-pass/impl-trait/universal_in_impl_trait_in_parameters.rs b/src/test/run-pass/impl-trait/universal_in_impl_trait_in_parameters.rs new file mode 100644 index 0000000000000..ccf24b77a6b77 --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_in_impl_trait_in_parameters.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] +use std::fmt::Display; + +fn check_display_eq(iter: impl IntoIterator) { + let mut collected = String::new(); + for it in iter { + let disp = format!("{} ", it); + collected.push_str(&disp); + } + assert_eq!("0 3 27 823 4891 1 0", collected.trim()); +} + +fn main() { + let i32_list = [0i32, 3, 27, 823, 4891, 1, 0]; + let i32_list_vec = vec![0i32, 3, 27, 823, 4891, 1, 0]; + let u32_list = [0u32, 3, 27, 823, 4891, 1, 0]; + let u32_list_vec = vec![0u32, 3, 27, 823, 4891, 1, 0]; + let u16_list = [0u16, 3, 27, 823, 4891, 1, 0]; + let str_list = ["0", "3", "27", "823", "4891", "1", "0"]; + let str_list_vec = vec!["0", "3", "27", "823", "4891", "1", "0"]; + + check_display_eq(&i32_list); + check_display_eq(i32_list_vec); + check_display_eq(&u32_list); + check_display_eq(u32_list_vec); + check_display_eq(&u16_list); + check_display_eq(&str_list); + check_display_eq(str_list_vec); +} diff --git a/src/test/run-pass/impl-trait/universal_in_trait_defn_parameters.rs b/src/test/run-pass/impl-trait/universal_in_trait_defn_parameters.rs new file mode 100644 index 0000000000000..af0201b5f871e --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_in_trait_defn_parameters.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +use std::fmt::Debug; + +trait InTraitDefnParameters { + fn in_parameters(_: impl Debug) -> String; +} + +impl InTraitDefnParameters for () { + fn in_parameters(v: impl Debug) -> String { + format!("() + {:?}", v) + } +} + +fn main() { + let s = <() as InTraitDefnParameters>::in_parameters(22); + assert_eq!(s, "() + 22"); +} diff --git a/src/test/run-pass/impl-trait/universal_multiple_bounds.rs b/src/test/run-pass/impl-trait/universal_multiple_bounds.rs new file mode 100644 index 0000000000000..bb332c0c96cb5 --- /dev/null +++ b/src/test/run-pass/impl-trait/universal_multiple_bounds.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +use std::fmt::Display; + +fn foo(f: impl Display + Clone) -> String { + let g = f.clone(); + format!("{} + {}", f, g) +} + +fn main() { + let sum = foo(format!("22")); + assert_eq!(sum, r"22 + 22"); +} diff --git a/src/test/run-pass/impl-trait/xcrate.rs b/src/test/run-pass/impl-trait/xcrate.rs index 6d00c46fa3508..35ae185b3e1de 100644 --- a/src/test/run-pass/impl-trait/xcrate.rs +++ b/src/test/run-pass/impl-trait/xcrate.rs @@ -13,6 +13,7 @@ extern crate xcrate; fn main() { - assert_eq!(xcrate::fourway_add(1)(2)(3)(4), 10); +// NOTE line below commeted out due to issue #45994 +// assert_eq!(xcrate::fourway_add(1)(2)(3)(4), 10); xcrate::return_closure_accessing_internal_fn()(); } diff --git a/src/test/run-pass/implied-bounds-closure-arg-outlives.rs b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs new file mode 100644 index 0000000000000..0e5cc574f0022 --- /dev/null +++ b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs @@ -0,0 +1,44 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we are able to handle the relationships between free +// regions bound in a closure callback. + +#[derive(Copy, Clone)] +struct MyCx<'short, 'long: 'short> { + short: &'short u32, + long: &'long u32, +} + +impl<'short, 'long> MyCx<'short, 'long> { + fn short(self) -> &'short u32 { self.short } + fn long(self) -> &'long u32 { self.long } + fn set_short(&mut self, v: &'short u32) { self.short = v; } +} + +fn with(op: F) -> R +where + F: for<'short, 'long> FnOnce(MyCx<'short, 'long>) -> R, +{ + op(MyCx { + short: &22, + long: &22, + }) +} + +fn main() { + with(|mut cx| { + // For this to type-check, we need to be able to deduce that + // the lifetime of `l` can be `'short`, even though it has + // input from `'long`. + let l = if true { cx.long() } else { cx.short() }; + cx.set_short(l); + }); +} diff --git a/src/test/run-pass/in-band-lifetimes.rs b/src/test/run-pass/in-band-lifetimes.rs new file mode 100644 index 0000000000000..95cc3c3759e60 --- /dev/null +++ b/src/test/run-pass/in-band-lifetimes.rs @@ -0,0 +1,104 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes, universal_impl_trait, conservative_impl_trait)] + +fn foo(x: &'x u8) -> &'x u8 { x } +fn foo2(x: &'a u8, y: &u8) -> &'a u8 { x } + +fn check_in_band_can_be_late_bound() { + let _: for<'x> fn(&'x u8, &u8) -> &'x u8 = foo2; +} + +struct ForInherentNoParams; + +impl ForInherentNoParams { + fn foo(x: &'a u32, y: &u32) -> &'a u32 { x } +} + +struct X<'a>(&'a u8); + +impl<'a> X<'a> { + fn inner(&self) -> &'a u8 { + self.0 + } + + fn same_lifetime_as_parameter(&mut self, x: &'a u8) { + self.0 = x; + } +} + +impl X<'b> { + fn inner_2(&self) -> &'b u8 { + self.0 + } + + fn reference_already_introduced_in_band_from_method_with_explicit_binders<'a>( + &'b self, x: &'a u32 + ) {} +} + +struct Y(T); + +impl Y<&'a u8> { + fn inner(&self) -> &'a u8 { + self.0 + } +} + +trait MyTrait<'a> { + fn my_lifetime(&self) -> &'a u8; + fn any_lifetime() -> &'b u8; + fn borrowed_lifetime(&'b self) -> &'b u8; + fn default_impl(&self, x: &'b u32, y: &u32) -> &'b u32 { x } + fn in_band_def_explicit_impl(&self, x: &'b u8); +} + +impl MyTrait<'a> for Y<&'a u8> { + fn my_lifetime(&self) -> &'a u8 { self.0 } + fn any_lifetime() -> &'b u8 { &0 } + fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + fn in_band_def_explicit_impl<'b>(&self, x: &'b u8) {} +} + +fn test_hrtb_defined_lifetime_where(_: F) where for<'a> F: Fn(&'a u8) {} +fn test_hrtb_defined_lifetime_polytraitref(_: F) where F: for<'a> Fn(&'a u8) {} + +fn reference_in_band_from_locals(x: &'test u32) -> &'test u32 { + let y: &'test u32 = x; + y +} + +fn in_generics_in_band>(x: &T) {} +fn where_clause_in_band(x: &T) where T: MyTrait<'a> {} +fn impl_trait_in_band(x: &impl MyTrait<'a>) {} + +// Tests around using in-band lifetimes within existential traits. + +trait FunkyTrait<'a> { } +impl<'a, T> FunkyTrait<'a> for T { } +fn existential_impl_trait_in_band_outlives(x: &'a u32) -> impl ::std::fmt::Debug + 'a { + x +} +fn existential_impl_trait_in_band_param(x: &'a u32) -> impl FunkyTrait<'a> { + x +} +fn existential_impl_trait_in_band_param_static(x: &'a u32) -> impl FunkyTrait<'static> + 'a { + x +} +fn existential_impl_trait_in_band_param_outlives(x: &'a u32) -> impl FunkyTrait<'a> + 'a { + x +} +fn existential_impl_trait_in_band_higher_ranked(x: &'a u32) -> impl for<'b> FunkyTrait<'b> + 'a { + x +} + +fn main() {} diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index cfae9903a95e5..99edd2550c01b 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare seems not important to test here #![feature(intrinsics, main)] diff --git a/src/test/run-pass/invoke-external-foreign.rs b/src/test/run-pass/invoke-external-foreign.rs index 1aae8131d8008..d01c3b67895e6 100644 --- a/src/test/run-pass/invoke-external-foreign.rs +++ b/src/test/run-pass/invoke-external-foreign.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:foreign_lib.rs +// ignore-wasm32-bare no libc to test ffi with // The purpose of this test is to check that we can // successfully (and safely) invoke external, cdecl diff --git a/src/test/run-pass/issue-10626.rs b/src/test/run-pass/issue-10626.rs index b350bd1a4ccbf..e9d37817ee2e2 100644 --- a/src/test/run-pass/issue-10626.rs +++ b/src/test/run-pass/issue-10626.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes // Make sure that if a process doesn't have its stdio/stderr descriptors set up // that we don't die in a large ball of fire diff --git a/src/test/run-pass/issue-1251.rs b/src/test/run-pass/issue-1251.rs index ddd30ed3bb0c0..00e8aa69a8942 100644 --- a/src/test/run-pass/issue-1251.rs +++ b/src/test/run-pass/issue-1251.rs @@ -9,6 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/issue-12699.rs b/src/test/run-pass/issue-12699.rs index 1e9f30bb766b2..4620d982c1ea5 100644 --- a/src/test/run-pass/issue-12699.rs +++ b/src/test/run-pass/issue-12699.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare can't block the thread + use std::thread; fn main() { diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs index 5a743d7b54784..9214d6b04bd2e 100644 --- a/src/test/run-pass/issue-13304.rs +++ b/src/test/run-pass/issue-13304.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes + #![feature(io, process_capture)] use std::env; diff --git a/src/test/run-pass/issue-13507-2.rs b/src/test/run-pass/issue-13507-2.rs index 084b7a166cdd3..75a724e55fb11 100644 --- a/src/test/run-pass/issue-13507-2.rs +++ b/src/test/run-pass/issue-13507-2.rs @@ -10,9 +10,6 @@ // aux-build:issue13507.rs - -#![feature(core)] - extern crate issue13507; use issue13507::testtypes; diff --git a/src/test/run-pass/issue-14456.rs b/src/test/run-pass/issue-14456.rs index 513ab91489c8d..2edb45cc1c414 100644 --- a/src/test/run-pass/issue-14456.rs +++ b/src/test/run-pass/issue-14456.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes #![feature(io, process_capture)] diff --git a/src/test/run-pass/issue-14875.rs b/src/test/run-pass/issue-14875.rs index ad19a9be76f88..e705539bb4772 100644 --- a/src/test/run-pass/issue-14875.rs +++ b/src/test/run-pass/issue-14875.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare always compiled as panic=abort right now + // Check that values are not leaked when a dtor panics (#14875) use std::panic::{self, UnwindSafe}; @@ -29,7 +31,6 @@ impl Drop for PanicOnDrop { } } - fn main() { let mut set_on_drop = false; { diff --git a/src/test/run-pass/issue-14940.rs b/src/test/run-pass/issue-14940.rs index ffe6b646794e1..588fa63cfdfb2 100644 --- a/src/test/run-pass/issue-14940.rs +++ b/src/test/run-pass/issue-14940.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::env; use std::process::Command; diff --git a/src/test/run-pass/issue-16272.rs b/src/test/run-pass/issue-16272.rs index f86be2d7c9933..cd02cbf3dad3e 100644 --- a/src/test/run-pass/issue-16272.rs +++ b/src/test/run-pass/issue-16272.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/issue-16671.rs b/src/test/run-pass/issue-16671.rs index 49dc970ba3f0d..9f34ad6726cb6 100644 --- a/src/test/run-pass/issue-16671.rs +++ b/src/test/run-pass/issue-16671.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//compile-flags: -Z borrowck=compare -Z emit-end-regions + #![deny(warnings)] fn foo(_f: F) { } diff --git a/src/test/run-pass/issue-17718-static-unsafe-interior.rs b/src/test/run-pass/issue-17718-static-unsafe-interior.rs index 66f70cdaeb08b..5f7629fa26777 100644 --- a/src/test/run-pass/issue-17718-static-unsafe-interior.rs +++ b/src/test/run-pass/issue-17718-static-unsafe-interior.rs @@ -10,9 +10,6 @@ // pretty-expanded FIXME #23616 -#![feature(core)] -#![feature(const_unsafe_cell_new)] - use std::marker; use std::cell::UnsafeCell; diff --git a/src/test/run-pass/issue-17718.rs b/src/test/run-pass/issue-17718.rs index 1b8fbc1ad2f5c..502e4a816640a 100644 --- a/src/test/run-pass/issue-17718.rs +++ b/src/test/run-pass/issue-17718.rs @@ -10,10 +10,6 @@ // aux-build:issue-17718-aux.rs - -#![feature(core)] -#![feature(const_atomic_usize_new)] - extern crate issue_17718_aux as other; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/test/run-pass/issue-20091.rs b/src/test/run-pass/issue-20091.rs index 1ee47a69d0c87..c8ba5dbd84cdf 100644 --- a/src/test/run-pass/issue-20091.rs +++ b/src/test/run-pass/issue-20091.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes + #![feature(std_misc, os)] #[cfg(unix)] diff --git a/src/test/run-pass/issue-21486.rs b/src/test/run-pass/issue-21486.rs index 23d06c4324da6..a61f294465d98 100644 --- a/src/test/run-pass/issue-21486.rs +++ b/src/test/run-pass/issue-21486.rs @@ -12,8 +12,6 @@ // created via FRU and control-flow breaks in the middle of // construction. -#![feature(const_atomic_usize_new)] - use std::sync::atomic::{Ordering, AtomicUsize}; #[derive(Debug)] diff --git a/src/test/run-pass/issue-2190-1.rs b/src/test/run-pass/issue-2190-1.rs index 393757035141d..8cc23c883ed6a 100644 --- a/src/test/run-pass/issue-2190-1.rs +++ b/src/test/run-pass/issue-2190-1.rs @@ -9,7 +9,7 @@ // except according to those terms. // pretty-expanded FIXME #23616 -// ignore-emscripten +// ignore-emscripten no threads use std::thread::Builder; diff --git a/src/test/run-pass/issue-2214.rs b/src/test/run-pass/issue-2214.rs index 316e379e664ae..7b7ecc91484ba 100644 --- a/src/test/run-pass/issue-2214.rs +++ b/src/test/run-pass/issue-2214.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/issue-24313.rs b/src/test/run-pass/issue-24313.rs index 9b2b474351df6..ad385ee78e0c7 100644 --- a/src/test/run-pass/issue-24313.rs +++ b/src/test/run-pass/issue-24313.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no threads use std::thread; use std::env; diff --git a/src/test/run-pass/issue-25185.rs b/src/test/run-pass/issue-25185.rs index d8d2d5078c5e7..9a2dbd1c96e31 100644 --- a/src/test/run-pass/issue-25185.rs +++ b/src/test/run-pass/issue-25185.rs @@ -10,6 +10,7 @@ // aux-build:issue-25185-1.rs // aux-build:issue-25185-2.rs +// ignore-wasm32-bare no libc for ffi testing extern crate issue_25185_2; diff --git a/src/test/run-pass/issue-26655.rs b/src/test/run-pass/issue-26655.rs index 3e252b8629e12..6d43451af6b91 100644 --- a/src/test/run-pass/issue-26655.rs +++ b/src/test/run-pass/issue-26655.rs @@ -12,8 +12,6 @@ // Check that the destructors of simple enums are run on unwinding -#![feature(const_atomic_usize_new)] - use std::sync::atomic::{Ordering, AtomicUsize}; use std::thread; diff --git a/src/test/run-pass/issue-27060.rs b/src/test/run-pass/issue-27060.rs new file mode 100644 index 0000000000000..809c45466f088 --- /dev/null +++ b/src/test/run-pass/issue-27060.rs @@ -0,0 +1,42 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(packed)] +pub struct Good { + data: &'static u32, + data2: [&'static u32; 2], + aligned: [u8; 32], +} + +#[repr(packed)] +pub struct JustArray { + array: [u32] +} + +// kill this test when that turns to a hard error +#[allow(safe_packed_borrows)] +fn main() { + let good = Good { + data: &0, + data2: [&0, &0], + aligned: [0; 32] + }; + + unsafe { + let _ = &good.data; // ok + let _ = &good.data2[0]; // ok + } + + let _ = &good.data; + let _ = &good.data2[0]; + let _ = &*good.data; // ok, behind a pointer + let _ = &good.aligned; // ok, has align 1 + let _ = &good.aligned[2]; // ok, has align 1 +} diff --git a/src/test/run-pass/issue-27997.rs b/src/test/run-pass/issue-27997.rs index dab42e48e1645..9dba477a7e5e5 100644 --- a/src/test/run-pass/issue-27997.rs +++ b/src/test/run-pass/issue-27997.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_atomic_usize_new)] - use std::sync::atomic::{Ordering, AtomicUsize}; use std::mem; diff --git a/src/test/run-pass/issue-28676.rs b/src/test/run-pass/issue-28676.rs index 8f83e51f0a02d..a7bee427a813c 100644 --- a/src/test/run-pass/issue-28676.rs +++ b/src/test/run-pass/issue-28676.rs @@ -7,7 +7,8 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// + +// ignore-wasm32-bare no libc to test ffi with #[derive(Copy, Clone)] pub struct Quad { a: u64, b: u64, c: u64, d: u64 } diff --git a/src/test/run-pass/issue-28950.rs b/src/test/run-pass/issue-28950.rs index a70c2b3ae1b7b..4e4530789c8dd 100644 --- a/src/test/run-pass/issue-28950.rs +++ b/src/test/run-pass/issue-28950.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no threads // compile-flags: -O // Tests that the `vec!` macro does not overflow the stack when it is diff --git a/src/test/run-pass/issue-29485.rs b/src/test/run-pass/issue-29485.rs index 9252762d1bdad..828b495d40813 100644 --- a/src/test/run-pass/issue-29485.rs +++ b/src/test/run-pass/issue-29485.rs @@ -9,7 +9,7 @@ // except according to those terms. // aux-build:issue-29485.rs -// ignore-emscripten +// ignore-emscripten no threads #[feature(recover)] diff --git a/src/test/run-pass/issue-29516.rs b/src/test/run-pass/issue-29516.rs index b586abc29e243..5fa0a002a10db 100644 --- a/src/test/run-pass/issue-29516.rs +++ b/src/test/run-pass/issue-29516.rs @@ -11,6 +11,7 @@ #![feature(optin_builtin_traits)] trait NotSame {} +#[allow(auto_impl)] impl NotSame for .. {} impl !NotSame for (A, A) {} diff --git a/src/test/run-pass/issue-29663.rs b/src/test/run-pass/issue-29663.rs index cf925662fc3f3..9a77be049feeb 100644 --- a/src/test/run-pass/issue-29663.rs +++ b/src/test/run-pass/issue-29663.rs @@ -10,8 +10,6 @@ // write_volatile causes an LLVM assert with composite types -// ignore-emscripten See #41299: probably a bad optimization - #![feature(volatile)] use std::ptr::{read_volatile, write_volatile}; diff --git a/src/test/run-pass/issue-29948.rs b/src/test/run-pass/issue-29948.rs index 281dde15bd336..3a7e9ba93e190 100644 --- a/src/test/run-pass/issue-29948.rs +++ b/src/test/run-pass/issue-29948.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + use std::panic; impl<'a> panic::UnwindSafe for Foo<'a> {} diff --git a/src/test/run-pass/issue-3012-2.rs b/src/test/run-pass/issue-3012-2.rs index ecce5df0fc200..bcf8a9180918c 100644 --- a/src/test/run-pass/issue-3012-2.rs +++ b/src/test/run-pass/issue-3012-2.rs @@ -12,15 +12,11 @@ // pretty-expanded FIXME #23616 -#![allow(unknown_features)] -#![feature(box_syntax, libc)] - extern crate socketlib; -extern crate libc; use socketlib::socket; pub fn main() { - let fd: libc::c_int = 1 as libc::c_int; - let _sock: Box<_> = box socket::socket_handle(fd); + let fd: u32 = 1 as u32; + let _sock: Box<_> = Box::new(socket::socket_handle(fd)); } diff --git a/src/test/run-pass/issue-30490.rs b/src/test/run-pass/issue-30490.rs index 7658abc00c599..4296107dd457e 100644 --- a/src/test/run-pass/issue-30490.rs +++ b/src/test/run-pass/issue-30490.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes // Previously libstd would set stdio descriptors of a child process // by `dup`ing the requested descriptors to inherit directly into the diff --git a/src/test/run-pass/issue-32008.rs b/src/test/run-pass/issue-32008.rs new file mode 100644 index 0000000000000..cb489acf1d919 --- /dev/null +++ b/src/test/run-pass/issue-32008.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that binary operators allow subtyping on both the LHS and RHS, +// and as such do not introduce unnecesarily strict lifetime constraints. + +use std::ops::Add; + +struct Foo; + +impl<'a> Add<&'a Foo> for &'a Foo { + type Output = (); + fn add(self, rhs: &'a Foo) {} +} + +fn try_to_add(input: &Foo) { + let local = Foo; + + // Manual reborrow worked even with invariant trait search. + &*input + &local; + + // Direct use of the reference on the LHS requires additional + // subtyping before searching (invariantly) for `LHS: Add`. + input + &local; +} + +fn main() { +} diff --git a/src/test/run-pass/issue-33770.rs b/src/test/run-pass/issue-33770.rs index 76728a0d354b2..3806270070783 100644 --- a/src/test/run-pass/issue-33770.rs +++ b/src/test/run-pass/issue-33770.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::process::{Command, Stdio}; use std::env; diff --git a/src/test/run-pass/issue-35600.rs b/src/test/run-pass/issue-35600.rs new file mode 100644 index 0000000000000..88358eff08d05 --- /dev/null +++ b/src/test/run-pass/issue-35600.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + type bar; + fn bar(); +} + +impl Foo for () { + type bar = (); + fn bar() {} +} + +fn main() { + let x: <() as Foo>::bar = (); + <()>::bar(); +} diff --git a/src/test/run-pass/issue-36023.rs b/src/test/run-pass/issue-36023.rs index 53a8a403b6410..f6c03b384f23d 100644 --- a/src/test/run-pass/issue-36023.rs +++ b/src/test/run-pass/issue-36023.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// min-llvm-version 3.9 - use std::ops::Deref; fn main() { diff --git a/src/test/run-pass/issue-3656.rs b/src/test/run-pass/issue-3656.rs index 10930474799c5..c278dcef9dde7 100644 --- a/src/test/run-pass/issue-3656.rs +++ b/src/test/run-pass/issue-3656.rs @@ -13,6 +13,7 @@ // the alignment of elements into account. // pretty-expanded FIXME #23616 +// ignore-wasm32-bare no libc to test with #![feature(libc)] diff --git a/src/test/run-pass/issue-40003.rs b/src/test/run-pass/issue-40003.rs new file mode 100644 index 0000000000000..103a365af0eb3 --- /dev/null +++ b/src/test/run-pass/issue-40003.rs @@ -0,0 +1,186 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + if false { test(); } +} + +fn test() { + let rx = Err::, u32>(1).into_future(); + + rx.map(|l: Vec| stream::iter(l.into_iter().map(|i| Ok(i)))) + .flatten_stream() + .chunks(50) + .buffer_unordered(5); +} + +use future::{Future, IntoFuture}; +mod future { + use std::result; + + use {stream, Stream}; + + pub trait Future { + type Item; + type Error; + + fn map(self, _: F) -> Map + where F: FnOnce(Self::Item) -> U, + Self: Sized, + { + panic!() + } + + fn flatten_stream(self) -> FlattenStream + where ::Item: stream::Stream, + Self: Sized + { + panic!() + } + } + + pub trait IntoFuture { + type Future: Future; + type Item; + type Error; + fn into_future(self) -> Self::Future; + } + + impl IntoFuture for F { + type Future = F; + type Item = F::Item; + type Error = F::Error; + + fn into_future(self) -> F { + panic!() + } + } + + impl IntoFuture for result::Result { + type Future = FutureResult; + type Item = T; + type Error = E; + + fn into_future(self) -> FutureResult { + panic!() + } + } + + pub struct Map { + _a: (A, F), + } + + impl Future for Map + where A: Future, + F: FnOnce(A::Item) -> U, + { + type Item = U; + type Error = A::Error; + } + + pub struct FlattenStream { + _f: F, + } + + impl Stream for FlattenStream + where F: Future, + ::Item: Stream, + { + type Item = ::Item; + type Error = ::Error; + } + + pub struct FutureResult { + _inner: (T, E), + } + + impl Future for FutureResult { + type Item = T; + type Error = E; + } +} + +mod stream { + use IntoFuture; + + pub trait Stream { + type Item; + type Error; + + fn buffer_unordered(self, amt: usize) -> BufferUnordered + where Self::Item: IntoFuture::Error>, + Self: Sized + { + new(self, amt) + } + + fn chunks(self, _capacity: usize) -> Chunks + where Self: Sized + { + panic!() + } + } + + pub struct IterStream { + _iter: I, + } + + pub fn iter(_: J) -> IterStream + where J: IntoIterator>, + { + panic!() + } + + impl Stream for IterStream + where I: Iterator>, + { + type Item = T; + type Error = E; + } + + pub struct Chunks { + _stream: S + } + + impl Stream for Chunks + where S: Stream + { + type Item = Result::Item>, u32>; + type Error = ::Error; + } + + pub struct BufferUnordered { + _stream: S, + } + + enum Slot { + Next(usize), + _Data { _a: T }, + } + + fn new(_s: S, _amt: usize) -> BufferUnordered + where S: Stream, + S::Item: IntoFuture::Error>, + { + (0..0).map(|_| { + Slot::Next::<::Future>(1) + }).collect::>(); + panic!() + } + + impl Stream for BufferUnordered + where S: Stream, + S::Item: IntoFuture::Error>, + { + type Item = ::Item; + type Error = ::Error; + } +} +use stream::Stream; diff --git a/src/test/run-pass/issue-43853.rs b/src/test/run-pass/issue-43853.rs index f55d584ea24fb..e9f8d2744a16a 100644 --- a/src/test/run-pass/issue-43853.rs +++ b/src/test/run-pass/issue-43853.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + use std::panic; fn test() { diff --git a/src/test/run-pass/issue-44247.rs b/src/test/run-pass/issue-44247.rs new file mode 100644 index 0000000000000..27b0aeaac5599 --- /dev/null +++ b/src/test/run-pass/issue-44247.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait T { + type X; + const X: Self::X; +} +fn foo() { + let _: X::X = X::X; +} + +trait S { + const X: Self::X; + type X; +} +fn bar() { + let _: X::X = X::X; +} + +fn main() {} diff --git a/src/test/run-pass/issue-44402.rs b/src/test/run-pass/issue-44402.rs new file mode 100644 index 0000000000000..244aa65a3d568 --- /dev/null +++ b/src/test/run-pass/issue-44402.rs @@ -0,0 +1,36 @@ +// 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. + +#![feature(never_type)] + +// Regression test for inhabitedness check. The old +// cache used to cause us to incorrectly decide +// that `test_b` was invalid. + +struct Foo { + field1: !, + field2: Option<&'static Bar>, +} + +struct Bar { + field1: &'static Foo +} + +fn test_a() { + let x: Option = None; + match x { None => () } +} + +fn test_b() { + let x: Option = None; + match x { None => () } +} + +fn main() { } diff --git a/src/test/run-pass/issue-44851.rs b/src/test/run-pass/issue-44851.rs new file mode 100644 index 0000000000000..62d675b13be30 --- /dev/null +++ b/src/test/run-pass/issue-44851.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! a { + () => { "a" } +} + +macro_rules! b { + ($doc:expr) => { + #[doc = $doc] + pub struct B; + } +} + +b!(a!()); + +fn main() {} diff --git a/src/test/run-pass/issue-45124.rs b/src/test/run-pass/issue-45124.rs new file mode 100644 index 0000000000000..c65823e460be3 --- /dev/null +++ b/src/test/run-pass/issue-45124.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(catch_expr)] + +fn main() { + let mut a = 0; + let () = { + let _: Result<(), ()> = do catch { + let _ = Err(())?; + return + }; + a += 1; + }; + a += 2; + assert_eq!(a, 3); +} diff --git a/src/test/run-pass/issue-45425.rs b/src/test/run-pass/issue-45425.rs new file mode 100644 index 0000000000000..06ffa6b3dea93 --- /dev/null +++ b/src/test/run-pass/issue-45425.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Add; + +fn ref_add(a: &T, b: &T) -> T +where + for<'x> &'x T: Add<&'x T, Output = T>, +{ + a + b +} + +fn main() {} diff --git a/src/test/run-pass/issue-45731.rs b/src/test/run-pass/issue-45731.rs new file mode 100644 index 0000000000000..ec35035dab447 --- /dev/null +++ b/src/test/run-pass/issue-45731.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--test -g + +use std::{env, panic, fs}; + +#[cfg(target_os = "macos")] +#[test] +fn simple_test() { + // Find our dSYM and replace the DWARF binary with an empty file + let mut dsym_path = env::current_exe().unwrap(); + let executable_name = dsym_path.file_name().unwrap().to_str().unwrap().to_string(); + assert!(dsym_path.pop()); // Pop executable + dsym_path.push(format!("{}.dSYM/Contents/Resources/DWARF/{0}", executable_name)); + { + let file = fs::OpenOptions::new().read(false).write(true).truncate(true).create(false) + .open(&dsym_path).unwrap(); + } + + env::set_var("RUST_BACKTRACE", "1"); + + // We don't need to die of panic, just trigger a backtrace + let _ = panic::catch_unwind(|| { + assert!(false); + }); +} diff --git a/src/test/run-pass/issue-46069.rs b/src/test/run-pass/issue-46069.rs new file mode 100644 index 0000000000000..70db20e4a6c92 --- /dev/null +++ b/src/test/run-pass/issue-46069.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::iter::{Fuse, Cloned}; +use std::slice::Iter; + +struct Foo<'a, T: 'a>(&'a T); +impl<'a, T: 'a> Copy for Foo<'a, T> {} +impl<'a, T: 'a> Clone for Foo<'a, T> { + fn clone(&self) -> Self { *self } +} + +fn copy_ex() { + let s = 2; + let k1 = || s; + let upvar = Foo(&k1); + let k = || upvar; + k(); +} + +fn main() { + let _f = 0 as *mut >> as Iterator>::Item; + + copy_ex(); +} diff --git a/src/test/run-pass/issue-4735.rs b/src/test/run-pass/issue-4735.rs index 56e69a9f36e78..7eb09e055a292 100644 --- a/src/test/run-pass/issue-4735.rs +++ b/src/test/run-pass/issue-4735.rs @@ -8,28 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - // pretty-expanded FIXME #23616 -#![allow(unknown_features)] -#![feature(box_syntax, libc)] - -extern crate libc; - use std::mem::transmute; -use libc::c_void; -struct NonCopyable(*const c_void); +struct NonCopyable(*const u8); impl Drop for NonCopyable { fn drop(&mut self) { let NonCopyable(p) = *self; - let _v = unsafe { transmute::<*const c_void, Box>(p) }; + let _v = unsafe { transmute::<*const u8, Box>(p) }; } } pub fn main() { - let t = box 0; - let p = unsafe { transmute::, *const c_void>(t) }; + let t = Box::new(0); + let p = unsafe { transmute::, *const u8>(t) }; let _z = NonCopyable(p); } diff --git a/src/test/run-pass/issue-5791.rs b/src/test/run-pass/issue-5791.rs index aad90bd4181d9..2e93279f756f0 100644 --- a/src/test/run-pass/issue-5791.rs +++ b/src/test/run-pass/issue-5791.rs @@ -10,15 +10,11 @@ // pretty-expanded FIXME #23616 -#![feature(libc)] - -extern crate libc; - extern { #[link_name = "malloc"] - fn malloc1(len: libc::c_int) -> *const libc::c_void; + fn malloc1(len: i32) -> *const u8; #[link_name = "malloc"] - fn malloc2(len: libc::c_int, foo: libc::c_int) -> *const libc::c_void; + fn malloc2(len: i32, foo: i32) -> *const u8; } pub fn main () {} diff --git a/src/test/run-pass/issue-8860.rs b/src/test/run-pass/issue-8860.rs index ff562aac1614b..127f9e355c7a2 100644 --- a/src/test/run-pass/issue-8860.rs +++ b/src/test/run-pass/issue-8860.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z borrowck=compare static mut DROP: isize = 0; static mut DROP_S: isize = 0; diff --git a/src/test/run-pass/item-attributes.rs b/src/test/run-pass/item-attributes.rs index f1ac96ab63e5c..0fc13319b8734 100644 --- a/src/test/run-pass/item-attributes.rs +++ b/src/test/run-pass/item-attributes.rs @@ -12,8 +12,7 @@ // for completeness since .rs files linked from .rc files support this // notation to specify their module's attributes - -#![feature(custom_attribute, libc)] +#![feature(custom_attribute)] #![allow(unused_attribute)] #![attr1 = "val"] #![attr2 = "val"] @@ -159,13 +158,11 @@ mod test_other_forms { mod test_foreign_items { pub mod rustrt { - extern crate libc; - extern { #![attr] #[attr] - fn rust_get_test_int() -> libc::intptr_t; + fn rust_get_test_int() -> u32; } } } diff --git a/src/test/run-pass/iter-step-overflow-debug.rs b/src/test/run-pass/iter-step-overflow-debug.rs index 5b9b58f028894..c45a10eac52b1 100644 --- a/src/test/run-pass/iter-step-overflow-debug.rs +++ b/src/test/run-pass/iter-step-overflow-debug.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -C debug_assertions=yes use std::panic; diff --git a/src/test/run-pass/iter-sum-overflow-debug.rs b/src/test/run-pass/iter-sum-overflow-debug.rs index 6c07afb37b8a0..c640f7cd28045 100644 --- a/src/test/run-pass/iter-sum-overflow-debug.rs +++ b/src/test/run-pass/iter-sum-overflow-debug.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -C debug_assertions=yes use std::panic; diff --git a/src/test/run-pass/iter-sum-overflow-overflow-checks.rs b/src/test/run-pass/iter-sum-overflow-overflow-checks.rs index a3a7179fb7112..517cd139cf354 100644 --- a/src/test/run-pass/iter-sum-overflow-overflow-checks.rs +++ b/src/test/run-pass/iter-sum-overflow-overflow-checks.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -C overflow-checks use std::panic; diff --git a/src/test/run-pass/lib-defaults.rs b/src/test/run-pass/lib-defaults.rs index 6e5dccae0a07d..fcaeda9a5495b 100644 --- a/src/test/run-pass/lib-defaults.rs +++ b/src/test/run-pass/lib-defaults.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + // compile-flags: -lrust_test_helpers #[link(name = "rust_test_helpers", kind = "static")] diff --git a/src/test/run-pass/linkage1.rs b/src/test/run-pass/linkage1.rs index 591610e88b1a6..f12a233f493ee 100644 --- a/src/test/run-pass/linkage1.rs +++ b/src/test/run-pass/linkage1.rs @@ -10,7 +10,7 @@ // ignore-windows // ignore-macos -// ignore-emscripten +// ignore-emscripten doesn't support this linkage // aux-build:linkage1.rs #![feature(linkage)] diff --git a/src/test/run-pass/lto-many-codegen-units.rs b/src/test/run-pass/lto-many-codegen-units.rs new file mode 100644 index 0000000000000..bed675cee5604 --- /dev/null +++ b/src/test/run-pass/lto-many-codegen-units.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C lto -C codegen-units=8 +// no-prefer-dynamic + +fn main() { +} diff --git a/src/test/run-pass/lto-still-runs-thread-dtors.rs b/src/test/run-pass/lto-still-runs-thread-dtors.rs new file mode 100644 index 0000000000000..91fb7aa51d4b0 --- /dev/null +++ b/src/test/run-pass/lto-still-runs-thread-dtors.rs @@ -0,0 +1,41 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C lto +// no-prefer-dynamic +// ignore-emscripten no threads support + +use std::thread; + +static mut HIT: usize = 0; + +thread_local!(static A: Foo = Foo); + +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) { + unsafe { + HIT += 1; + } + } +} + +fn main() { + unsafe { + assert_eq!(HIT, 0); + thread::spawn(|| { + assert_eq!(HIT, 0); + A.with(|_| ()); + assert_eq!(HIT, 0); + }).join().unwrap(); + assert_eq!(HIT, 1); + } +} diff --git a/src/test/run-pass/lub-glb-with-unbound-infer-var.rs b/src/test/run-pass/lub-glb-with-unbound-infer-var.rs new file mode 100644 index 0000000000000..6b9bd67f9a5a4 --- /dev/null +++ b/src/test/run-pass/lub-glb-with-unbound-infer-var.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test for a specific corner case: when we compute the LUB of two fn +// types and their parameters have unbound variables. In that case, we +// wind up relating those two variables. This was causing an ICE in an +// in-progress PR. + +fn main() { + let a_f: fn(_) = |_| (); + let b_f: fn(_) = |_| (); + let c_f = match 22 { + 0 => a_f, + _ => b_f, + }; + c_f(4); +} diff --git a/src/test/run-pass/macro-pub-matcher.rs b/src/test/run-pass/macro-pub-matcher.rs index d79f4b65b69e1..32145277252cb 100644 --- a/src/test/run-pass/macro-pub-matcher.rs +++ b/src/test/run-pass/macro-pub-matcher.rs @@ -9,7 +9,7 @@ // except according to those terms. #![allow(dead_code, unused_imports)] -#![feature(macro_vis_matcher)] +#![feature(macro_vis_matcher, crate_visibility_modifier)] /** Ensure that `:vis` matches can be captured in existing positions, and passed @@ -64,6 +64,18 @@ mod with_pub_restricted { vis_passthru! { pub(crate) use A as I; } } +mod with_crate { + vis_passthru! { crate const A: i32 = 0; } + vis_passthru! { crate enum B {} } + vis_passthru! { crate extern "C" fn c() {} } + vis_passthru! { crate mod d {} } + vis_passthru! { crate static E: i32 = 0; } + vis_passthru! { crate struct F; } + vis_passthru! { crate trait G {} } + vis_passthru! { crate type H = i32; } + vis_passthru! { crate use A as I; } +} + mod garden { mod with_pub_restricted_path { vis_passthru! { pub(in garden) const A: i32 = 0; } diff --git a/src/test/run-pass/mir-inlining/ice-issue-45493.rs b/src/test/run-pass/mir-inlining/ice-issue-45493.rs new file mode 100644 index 0000000000000..bd178f0e5bdaf --- /dev/null +++ b/src/test/run-pass/mir-inlining/ice-issue-45493.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Zmir-opt-level=2 + +trait Array { + type Item; +} + +fn foo() { + let _: *mut A::Item = std::ptr::null_mut(); +} + +struct Foo; +impl Array for Foo { type Item = i32; } + +fn main() { + foo::(); +} diff --git a/src/test/run-pass/mir-inlining/ice-issue-45885.rs b/src/test/run-pass/mir-inlining/ice-issue-45885.rs new file mode 100644 index 0000000000000..702cb6a9bc165 --- /dev/null +++ b/src/test/run-pass/mir-inlining/ice-issue-45885.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Zmir-opt-level=2 + +pub enum Enum { + A, + B, +} + +trait SliceIndex { + type Output; + fn get(&self) -> &Self::Output; +} + +impl SliceIndex for usize { + type Output = Enum; + #[inline(never)] + fn get(&self) -> &Enum { + &Enum::A + } +} + +#[inline(always)] +fn index(t: &T) -> &T::Output { + t.get() +} + +fn main() { + match *index(&0) { Enum::A => true, _ => false }; +} diff --git a/src/test/run-pass/mir_calls_to_shims.rs b/src/test/run-pass/mir_calls_to_shims.rs index 7300a322ec4b7..9641ed282936f 100644 --- a/src/test/run-pass/mir_calls_to_shims.rs +++ b/src/test/run-pass/mir_calls_to_shims.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + #![feature(fn_traits)] #![feature(never_type)] diff --git a/src/test/run-pass/mir_drop_order.rs b/src/test/run-pass/mir_drop_order.rs index e7da43597f169..41cb458c0b8b4 100644 --- a/src/test/run-pass/mir_drop_order.rs +++ b/src/test/run-pass/mir_drop_order.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + use std::cell::RefCell; use std::panic; diff --git a/src/test/run-pass/mir_misc_casts.rs b/src/test/run-pass/mir_misc_casts.rs index ae719ac2800ee..81c8b75fb9b6a 100644 --- a/src/test/run-pass/mir_misc_casts.rs +++ b/src/test/run-pass/mir_misc_casts.rs @@ -8,10 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(libc)] - -extern crate libc; - fn func(){} const STR: &'static str = "hello"; @@ -19,7 +15,7 @@ const BSTR: &'static [u8; 5] = b"hello"; fn from_ptr() -> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, *const ()) { - let f = 1_usize as *const libc::FILE; + let f = 1_usize as *const String; let c1 = f as isize; let c2 = f as usize; let c3 = f as i8; @@ -35,7 +31,7 @@ fn from_ptr() } fn from_1() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1 as isize; let c2 = 1 as usize; let c3 = 1 as i8; @@ -48,12 +44,12 @@ fn from_1() let c10 = 1 as u64; let c11 = 1 as f32; let c12 = 1 as f64; - let c13 = 1 as *const libc::FILE; + let c13 = 1 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1usize() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_usize as isize; let c2 = 1_usize as usize; let c3 = 1_usize as i8; @@ -66,12 +62,12 @@ fn from_1usize() let c10 = 1_usize as u64; let c11 = 1_usize as f32; let c12 = 1_usize as f64; - let c13 = 1_usize as *const libc::FILE; + let c13 = 1_usize as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1isize() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_isize as isize; let c2 = 1_isize as usize; let c3 = 1_isize as i8; @@ -84,12 +80,12 @@ fn from_1isize() let c10 = 1_isize as u64; let c11 = 1_isize as f32; let c12 = 1_isize as f64; - let c13 = 1_isize as *const libc::FILE; + let c13 = 1_isize as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1u8() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_u8 as isize; let c2 = 1_u8 as usize; let c3 = 1_u8 as i8; @@ -102,12 +98,12 @@ fn from_1u8() let c10 = 1_u8 as u64; let c11 = 1_u8 as f32; let c12 = 1_u8 as f64; - let c13 = 1_u8 as *const libc::FILE; + let c13 = 1_u8 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1i8() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_i8 as isize; let c2 = 1_i8 as usize; let c3 = 1_i8 as i8; @@ -120,12 +116,12 @@ fn from_1i8() let c10 = 1_i8 as u64; let c11 = 1_i8 as f32; let c12 = 1_i8 as f64; - let c13 = 1_i8 as *const libc::FILE; + let c13 = 1_i8 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1u16() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_u16 as isize; let c2 = 1_u16 as usize; let c3 = 1_u16 as i8; @@ -138,12 +134,12 @@ fn from_1u16() let c10 = 1_u16 as u64; let c11 = 1_u16 as f32; let c12 = 1_u16 as f64; - let c13 = 1_u16 as *const libc::FILE; + let c13 = 1_u16 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1i16() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_i16 as isize; let c2 = 1_i16 as usize; let c3 = 1_i16 as i8; @@ -156,12 +152,12 @@ fn from_1i16() let c10 = 1_i16 as u64; let c11 = 1_i16 as f32; let c12 = 1_i16 as f64; - let c13 = 1_i16 as *const libc::FILE; + let c13 = 1_i16 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1u32() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_u32 as isize; let c2 = 1_u32 as usize; let c3 = 1_u32 as i8; @@ -174,12 +170,12 @@ fn from_1u32() let c10 = 1_u32 as u64; let c11 = 1_u32 as f32; let c12 = 1_u32 as f64; - let c13 = 1_u32 as *const libc::FILE; + let c13 = 1_u32 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1i32() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_i32 as isize; let c2 = 1_i32 as usize; let c3 = 1_i32 as i8; @@ -192,12 +188,12 @@ fn from_1i32() let c10 = 1_i32 as u64; let c11 = 1_i32 as f32; let c12 = 1_i32 as f64; - let c13 = 1_i32 as *const libc::FILE; + let c13 = 1_i32 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1u64() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_u64 as isize; let c2 = 1_u64 as usize; let c3 = 1_u64 as i8; @@ -210,12 +206,12 @@ fn from_1u64() let c10 = 1_u64 as u64; let c11 = 1_u64 as f32; let c12 = 1_u64 as f64; - let c13 = 1_u64 as *const libc::FILE; + let c13 = 1_u64 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } fn from_1i64() --> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) { +-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const String) { let c1 = 1_i64 as isize; let c2 = 1_i64 as usize; let c3 = 1_i64 as i8; @@ -228,7 +224,7 @@ fn from_1i64() let c10 = 1_i64 as u64; let c11 = 1_i64 as f32; let c12 = 1_i64 as f64; - let c13 = 1_i64 as *const libc::FILE; + let c13 = 1_i64 as *const String; (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) } @@ -297,9 +293,9 @@ fn other_casts() } pub fn assert_eq_13(l: (isize, usize, i8, i16, i32, i64, u8, - u16, u32, u64, f32, f64, *const libc::FILE), + u16, u32, u64, f32, f64, *const String), r: (isize, usize, i8, i16, i32, i64, u8, - u16, u32, u64, f32, f64, *const libc::FILE)) -> bool { + u16, u32, u64, f32, f64, *const String)) -> bool { let (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13) = l; let (r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13) = r; l1 == r1 && l2 == r2 && l3 == r3 && l4 == r4 && l5 == r5 && l6 == r6 && l7 == r7 && @@ -308,7 +304,7 @@ pub fn assert_eq_13(l: (isize, usize, i8, i16, i32, i64, u8, pub fn main() { - let f = 1_usize as *const libc::FILE; + let f = 1_usize as *const String; let t13 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, f); let t12 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0); assert_eq_13(from_1(), t13); diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs index d429c681bbe4a..d02e3287bc38a 100644 --- a/src/test/run-pass/mir_trans_calls.rs +++ b/src/test/run-pass/mir_trans_calls.rs @@ -8,7 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(fn_traits)] +#![feature(fn_traits, test)] + +extern crate test; fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { // Test passing a number of arguments including a fat pointer. @@ -156,6 +158,16 @@ fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) { (z.0, z.1) } +fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 { + // Mutate the by-reference argument, which won't work with + // a non-immediate constant unless it's copied to the stack. + let a = test::black_box(&mut a); + a[0] += a[1]; + a[0] += a[2]; + a[0] += a[3]; + a[0] +} + fn main() { assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); assert_eq!(test2(98), 98); @@ -182,4 +194,7 @@ fn main() { assert_eq!(test_fn_ignored_pair_0(), ()); assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo)); assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0)); + + const ARRAY: [u64; 4] = [1, 2, 3, 4]; + assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4); } diff --git a/src/test/run-pass/mir_trans_calls_variadic.rs b/src/test/run-pass/mir_trans_calls_variadic.rs index e4d528e80e104..7845c9426e23b 100644 --- a/src/test/run-pass/mir_trans_calls_variadic.rs +++ b/src/test/run-pass/mir_trans_calls_variadic.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #[link(name = "rust_test_helpers", kind = "static")] extern { fn rust_interesting_average(_: i64, ...) -> f64; diff --git a/src/test/run-pass/multi-panic.rs b/src/test/run-pass/multi-panic.rs index 86fe06b176532..c15b40d4dda20 100644 --- a/src/test/run-pass/multi-panic.rs +++ b/src/test/run-pass/multi-panic.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes fn check_for_no_backtrace(test: std::process::Output) { assert!(!test.status.success()); diff --git a/src/test/run-pass/multiple-reprs.rs b/src/test/run-pass/multiple-reprs.rs index c2fe943eed85a..d8eafb806f747 100644 --- a/src/test/run-pass/multiple-reprs.rs +++ b/src/test/run-pass/multiple-reprs.rs @@ -9,7 +9,8 @@ // except according to those terms. -use std::mem::size_of; +use std::mem::{size_of, align_of}; +use std::os::raw::c_int; // The two enums that follow are designed so that bugs trigger layout optimization. // Specifically, if either of the following reprs used here is not detected by the compiler, @@ -27,6 +28,38 @@ enum E2 { B(u8, u16, u8) } +// Check that repr(int) and repr(C) are in fact different from the above + +#[repr(u8)] +enum E3 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +#[repr(u16)] +enum E4 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +#[repr(u32)] +enum E5 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +#[repr(u64)] +enum E6 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +#[repr(C)] +enum E7 { + A(u8, u16, u8), + B(u8, u16, u8) +} + // From pr 37429 #[repr(C,packed)] @@ -37,7 +70,20 @@ pub struct p0f_api_query { } pub fn main() { - assert_eq!(size_of::(), 6); - assert_eq!(size_of::(), 6); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::(), 6); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::(), align_size(10, align_of::())); + assert_eq!(size_of::(), align_size(14, align_of::())); + assert_eq!(size_of::(), align_size(6 + size_of::(), align_of::())); assert_eq!(size_of::(), 21); } + +fn align_size(size: usize, align: usize) -> usize { + if size % align != 0 { + size + (align - (size % align)) + } else { + size + } +} diff --git a/src/test/run-pass/nested-vec-3.rs b/src/test/run-pass/nested-vec-3.rs index 9141b5f29ceb9..d1a63b4439276 100644 --- a/src/test/run-pass/nested-vec-3.rs +++ b/src/test/run-pass/nested-vec-3.rs @@ -14,8 +14,6 @@ // the contents implement Drop and we hit a panic in the middle of // construction. -#![feature(const_atomic_usize_new)] - use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/test/run-pass/newtype-struct-with-dtor.rs b/src/test/run-pass/newtype-struct-with-dtor.rs index d1ad5614e3f35..07c76e27284f5 100644 --- a/src/test/run-pass/newtype-struct-with-dtor.rs +++ b/src/test/run-pass/newtype-struct-with-dtor.rs @@ -8,21 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - // pretty-expanded FIXME #23616 -#![feature(libc)] - -extern crate libc; -use libc::c_int; +pub struct Fd(u32); -pub struct Fd(c_int); +fn foo(a: u32) {} impl Drop for Fd { fn drop(&mut self) { unsafe { let Fd(s) = *self; - libc::close(s); + foo(s); } } } diff --git a/src/test/run-pass/next-power-of-two-overflow-debug.rs b/src/test/run-pass/next-power-of-two-overflow-debug.rs new file mode 100644 index 0000000000000..2782f8c2a598c --- /dev/null +++ b/src/test/run-pass/next-power-of-two-overflow-debug.rs @@ -0,0 +1,37 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C debug_assertions=yes +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(i128_type)] + +use std::panic; + +fn main() { + macro_rules! overflow_test { + ($t:ident) => ( + let r = panic::catch_unwind(|| { + ($t::max_value()).next_power_of_two() + }); + assert!(r.is_err()); + + let r = panic::catch_unwind(|| { + (($t::max_value() >> 1) + 2).next_power_of_two() + }); + assert!(r.is_err()); + ) + } + overflow_test!(u8); + overflow_test!(u16); + overflow_test!(u32); + overflow_test!(u64); + overflow_test!(u128); +} diff --git a/src/test/run-pass/next-power-of-two-overflow-ndebug.rs b/src/test/run-pass/next-power-of-two-overflow-ndebug.rs new file mode 100644 index 0000000000000..f8bcb961c6833 --- /dev/null +++ b/src/test/run-pass/next-power-of-two-overflow-ndebug.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C debug_assertions=no + +#![feature(i128_type)] + +fn main() { + for i in 129..256 { + assert_eq!((i as u8).next_power_of_two(), 0); + } + + assert_eq!(((1u16 << 15) + 1).next_power_of_two(), 0); + assert_eq!(((1u32 << 31) + 1).next_power_of_two(), 0); + assert_eq!(((1u64 << 63) + 1).next_power_of_two(), 0); + assert_eq!(((1u128 << 127) + 1).next_power_of_two(), 0); +} diff --git a/src/test/run-pass/no-landing-pads.rs b/src/test/run-pass/no-landing-pads.rs index e718046ebbcd7..73f123045d249 100644 --- a/src/test/run-pass/no-landing-pads.rs +++ b/src/test/run-pass/no-landing-pads.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z no-landing-pads +// compile-flags: -Z no-landing-pads -C codegen-units=1 // ignore-emscripten no threads support use std::thread; diff --git a/src/test/run-pass/no-stdio.rs b/src/test/run-pass/no-stdio.rs index 85c63e184fe25..7b6b711315afd 100644 --- a/src/test/run-pass/no-stdio.rs +++ b/src/test/run-pass/no-stdio.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes // ignore-android #![feature(libc)] diff --git a/src/test/run-pass/num-wrapping.rs b/src/test/run-pass/num-wrapping.rs index 143759e271561..20c7f27336e25 100644 --- a/src/test/run-pass/num-wrapping.rs +++ b/src/test/run-pass/num-wrapping.rs @@ -173,6 +173,15 @@ fn test_op_assigns() { tmp.$op(Wrapping($rhs)); assert_eq!(black_box(tmp), Wrapping($ans)); } + + // also test that a &Wrapping right-hand side is possible + { + let mut tmp = Wrapping($initial); + tmp = black_box(tmp); + tmp.$op(&Wrapping($rhs)); + assert_eq!(black_box(tmp), Wrapping($ans)); + } + // FIXME(30524): Uncomment this test /* { diff --git a/src/test/run-pass/op-assign-builtins-by-ref.rs b/src/test/run-pass/op-assign-builtins-by-ref.rs new file mode 100644 index 0000000000000..230d44ba647a5 --- /dev/null +++ b/src/test/run-pass/op-assign-builtins-by-ref.rs @@ -0,0 +1,84 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + // test compound assignment operators with ref as right-hand side, + // for each operator, with various types as operands. + + // test AddAssign + { + let mut x = 3i8; + x += &2i8; + assert_eq!(x, 5i8); + } + + // test SubAssign + { + let mut x = 7i16; + x -= &4; + assert_eq!(x, 3i16); + } + + // test MulAssign + { + let mut x = 3f32; + x *= &3f32; + assert_eq!(x, 9f32); + } + + // test DivAssign + { + let mut x = 6f64; + x /= &2f64; + assert_eq!(x, 3f64); + } + + // test RemAssign + { + let mut x = 7i64; + x %= &4i64; + assert_eq!(x, 3i64); + } + + // test BitOrAssign + { + let mut x = 0b1010u8; + x |= &0b1100u8; + assert_eq!(x, 0b1110u8); + } + + // test BitAndAssign + { + let mut x = 0b1010u16; + x &= &0b1100u16; + assert_eq!(x, 0b1000u16); + } + + // test BitXorAssign + { + let mut x = 0b1010u32; + x ^= &0b1100u32; + assert_eq!(x, 0b0110u32); + } + + // test ShlAssign + { + let mut x = 0b1010u64; + x <<= &2u32; + assert_eq!(x, 0b101000u64); + } + + // test ShrAssign + { + let mut x = 0b1010u64; + x >>= &2i16; + assert_eq!(x, 0b10u64); + } +} diff --git a/src/test/run-pass/out-of-stack.rs b/src/test/run-pass/out-of-stack.rs index 7e70c4a7ab38e..485335a2d8006 100644 --- a/src/test/run-pass/out-of-stack.rs +++ b/src/test/run-pass/out-of-stack.rs @@ -10,7 +10,7 @@ // ignore-android: FIXME (#20004) // ignore-musl -// ignore-emscripten +// ignore-emscripten no processes #![feature(asm)] #![feature(libc)] diff --git a/src/test/run-pass/packed-struct-borrow-element.rs b/src/test/run-pass/packed-struct-borrow-element.rs index 4886874062178..3041c73afba88 100644 --- a/src/test/run-pass/packed-struct-borrow-element.rs +++ b/src/test/run-pass/packed-struct-borrow-element.rs @@ -17,7 +17,7 @@ struct Foo { pub fn main() { let foo = Foo { bar: 1, baz: 2 }; - let brw = &foo.baz; + let brw = unsafe { &foo.baz }; assert_eq!(*brw, 2); } diff --git a/src/test/run-pass/packed-struct-drop-aligned.rs b/src/test/run-pass/packed-struct-drop-aligned.rs new file mode 100644 index 0000000000000..bbe31a65e86a2 --- /dev/null +++ b/src/test/run-pass/packed-struct-drop-aligned.rs @@ -0,0 +1,42 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cell::Cell; +use std::mem; + +struct Aligned<'a> { + drop_count: &'a Cell +} + +#[inline(never)] +fn check_align(ptr: *const Aligned) { + assert_eq!(ptr as usize % mem::align_of::(), + 0); +} + +impl<'a> Drop for Aligned<'a> { + fn drop(&mut self) { + check_align(self); + self.drop_count.set(self.drop_count.get() + 1); + } +} + +#[repr(packed)] +struct Packed<'a>(u8, Aligned<'a>); + +fn main() { + let drop_count = &Cell::new(0); + { + let mut p = Packed(0, Aligned { drop_count }); + p.1 = Aligned { drop_count }; + assert_eq!(drop_count.get(), 1); + } + assert_eq!(drop_count.get(), 2); +} diff --git a/src/test/run-pass/packed-struct-layout.rs b/src/test/run-pass/packed-struct-layout.rs index d1e05e5a0184c..a4a0055785f0d 100644 --- a/src/test/run-pass/packed-struct-layout.rs +++ b/src/test/run-pass/packed-struct-layout.rs @@ -7,8 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten Not sure what's happening here. - use std::mem; diff --git a/src/test/run-pass/packed-struct-optimized-enum.rs b/src/test/run-pass/packed-struct-optimized-enum.rs new file mode 100644 index 0000000000000..876b74a042f8e --- /dev/null +++ b/src/test/run-pass/packed-struct-optimized-enum.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(packed)] +struct Packed(T); + +impl Copy for Packed {} +impl Clone for Packed { + fn clone(&self) -> Self { *self } +} + +fn main() { + let one = (Some(Packed((&(), 0))), true); + let two = [one, one]; + let stride = (&two[1] as *const _ as usize) - (&two[0] as *const _ as usize); + + // This can fail if rustc and LLVM disagree on the size of a type. + // In this case, `Option>` was erronously not + // marked as packed despite needing alignment `1` and containing + // its `&()` discriminant, which has alignment larger than `1`. + assert_eq!(stride, std::mem::size_of_val(&one)); +} diff --git a/src/test/run-pass/packed-tuple-struct-layout.rs b/src/test/run-pass/packed-tuple-struct-layout.rs index ee4eb86ed0de3..18f7eff280a84 100644 --- a/src/test/run-pass/packed-tuple-struct-layout.rs +++ b/src/test/run-pass/packed-tuple-struct-layout.rs @@ -7,8 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten - use std::mem; diff --git a/src/test/run-pass/panic-handler-chain.rs b/src/test/run-pass/panic-handler-chain.rs index c5dc8ccd2eeaf..8d692f2241bc7 100644 --- a/src/test/run-pass/panic-handler-chain.rs +++ b/src/test/run-pass/panic-handler-chain.rs @@ -11,7 +11,6 @@ // ignore-emscripten no threads support #![feature(panic_handler, std_panic)] -#![feature(const_atomic_usize_new)] use std::sync::atomic::{AtomicUsize, Ordering}; use std::panic; diff --git a/src/test/run-pass/panic-handler-set-twice.rs b/src/test/run-pass/panic-handler-set-twice.rs index 8bf2683cd9feb..81da13afaa59e 100644 --- a/src/test/run-pass/panic-handler-set-twice.rs +++ b/src/test/run-pass/panic-handler-set-twice.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(panic_handler, std_panic)] -#![feature(const_atomic_usize_new)] // ignore-emscripten no threads support diff --git a/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs index ebbb00a4a9f23..d1fdc8afa653e 100644 --- a/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs +++ b/src/test/run-pass/panic-runtime/abort-link-to-unwinding-crates.rs @@ -11,7 +11,7 @@ // compile-flags:-C panic=abort // aux-build:exit-success-if-unwind.rs // no-prefer-dynamic -// ignore-emscripten Function not implemented +// ignore-emscripten no processes extern crate exit_success_if_unwind; diff --git a/src/test/run-pass/panic-runtime/abort.rs b/src/test/run-pass/panic-runtime/abort.rs index 3ba3bd61c2e87..bb541b29d7c76 100644 --- a/src/test/run-pass/panic-runtime/abort.rs +++ b/src/test/run-pass/panic-runtime/abort.rs @@ -10,7 +10,7 @@ // compile-flags:-C panic=abort // no-prefer-dynamic -// ignore-emscripten Function not implemented. +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/panic-runtime/lto-abort.rs b/src/test/run-pass/panic-runtime/lto-abort.rs index e4cd4e809a4c6..59e9474aab2ec 100644 --- a/src/test/run-pass/panic-runtime/lto-abort.rs +++ b/src/test/run-pass/panic-runtime/lto-abort.rs @@ -10,7 +10,7 @@ // compile-flags:-C lto -C panic=abort // no-prefer-dynamic -// ignore-emscripten Function not implemented. +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/panic-runtime/lto-unwind.rs b/src/test/run-pass/panic-runtime/lto-unwind.rs index 768b88fd09e0b..6d28b8d12f62b 100644 --- a/src/test/run-pass/panic-runtime/lto-unwind.rs +++ b/src/test/run-pass/panic-runtime/lto-unwind.rs @@ -10,7 +10,7 @@ // compile-flags:-C lto -C panic=unwind // no-prefer-dynamic -// ignore-emscripten Function not implemented. +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/paths-containing-nul.rs b/src/test/run-pass/paths-containing-nul.rs index 2da3e59e54c58..9f39146e238d6 100644 --- a/src/test/run-pass/paths-containing-nul.rs +++ b/src/test/run-pass/paths-containing-nul.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no files or I/O + use std::fs; use std::io; diff --git a/src/test/run-pass/process-envs.rs b/src/test/run-pass/process-envs.rs index b3785d898baa4..1622517198a2c 100644 --- a/src/test/run-pass/process-envs.rs +++ b/src/test/run-pass/process-envs.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/process-exit.rs b/src/test/run-pass/process-exit.rs index a5d408448a033..5abc06b75e14e 100644 --- a/src/test/run-pass/process-exit.rs +++ b/src/test/run-pass/process-exit.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::env; use std::process::{self, Command, Stdio}; diff --git a/src/test/run-pass/process-remove-from-env.rs b/src/test/run-pass/process-remove-from-env.rs index b7f296a65c216..7a9b431d5709e 100644 --- a/src/test/run-pass/process-remove-from-env.rs +++ b/src/test/run-pass/process-remove-from-env.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::process::Command; use std::env; diff --git a/src/test/run-pass/process-spawn-with-unicode-params.rs b/src/test/run-pass/process-spawn-with-unicode-params.rs index 550c6d6ab6702..7b85420726326 100644 --- a/src/test/run-pass/process-spawn-with-unicode-params.rs +++ b/src/test/run-pass/process-spawn-with-unicode-params.rs @@ -16,7 +16,7 @@ // non-ASCII characters. The child process ensures all the strings are // intact. -// ignore-emscripten +// ignore-emscripten no processes use std::io::prelude::*; use std::io; diff --git a/src/test/run-pass/process-status-inherits-stdin.rs b/src/test/run-pass/process-status-inherits-stdin.rs index ff389bec899ef..5ea48a4ff33bc 100644 --- a/src/test/run-pass/process-status-inherits-stdin.rs +++ b/src/test/run-pass/process-status-inherits-stdin.rs @@ -7,7 +7,8 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten Function not implemented. + +// ignore-emscripten no processes use std::env; use std::io; diff --git a/src/test/run-pass/pub-extern-privacy.rs b/src/test/run-pass/pub-extern-privacy.rs index b9a3f788f9794..1ef804fe8feea 100644 --- a/src/test/run-pass/pub-extern-privacy.rs +++ b/src/test/run-pass/pub-extern-privacy.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + // pretty-expanded FIXME #23616 use std::mem::transmute; diff --git a/src/test/run-pass/reachable-unnameable-items.rs b/src/test/run-pass/reachable-unnameable-items.rs index 75a2e36ffb7a4..d087be6d10cfc 100644 --- a/src/test/run-pass/reachable-unnameable-items.rs +++ b/src/test/run-pass/reachable-unnameable-items.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // aux-build:reachable-unnameable-items.rs extern crate reachable_unnameable_items; diff --git a/src/test/run-pass/rec-align-u64.rs b/src/test/run-pass/rec-align-u64.rs index 4863979b3f6c2..d051e05b5f9e7 100644 --- a/src/test/run-pass/rec-align-u64.rs +++ b/src/test/run-pass/rec-align-u64.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare seems unimportant to test + // Issue #2303 #![feature(intrinsics)] diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index b67612c94b009..8f278a315d1af 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -10,10 +10,9 @@ // pretty-expanded FIXME #23616 -#![feature(libc)] +#![feature(allocator_api)] -extern crate libc; -use std::mem; +use std::heap::{Alloc, Heap, Layout}; struct arena(()); @@ -32,8 +31,9 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { - mem::transmute(libc::malloc(mem::size_of::>() - as libc::size_t)) + let ptr = Heap.alloc(Layout::new::()) + .unwrap_or_else(|e| Heap.oom(e)); + &*(ptr as *const _) } } @@ -45,7 +45,7 @@ fn g(fcx : &Fcx) { let bcx = Bcx { fcx: fcx }; let bcx2 = h(&bcx); unsafe { - libc::free(mem::transmute(bcx2)); + Heap.dealloc(bcx2 as *const _ as *mut _, Layout::new::()); } } diff --git a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs index ae4adbfb1f497..3162ef54f39bc 100644 --- a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs +++ b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs @@ -42,7 +42,7 @@ impl<'a,'tcx> Foo<'a,'tcx> { // inferring `'_2` to be `'static` in this case, because // it is created outside the closure but then related to // regions bound by the closure itself. See the - // `region_inference.rs` file (and the `givens` field, in + // `region_constraints.rs` file (and the `givens` field, in // particular) for more details. this.foo() })) diff --git a/src/test/run-pass/rfc-1014.rs b/src/test/run-pass/rfc-1014.rs index df969070a2ad4..950c2e0c4dfab 100644 --- a/src/test/run-pass/rfc-1014.rs +++ b/src/test/run-pass/rfc-1014.rs @@ -7,6 +7,9 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + +// ignore-wasm32-bare no libc + #![feature(libc)] extern crate libc; diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/box.rs b/src/test/run-pass/rfc-2005-default-binding-mode/box.rs new file mode 100644 index 0000000000000..85453f32208cb --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/box.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax, box_patterns)] +#![feature(match_default_bindings)] + +struct Foo{} + +pub fn main() { + let b = box Foo{}; + let box f = &b; + let _: &Foo = f; + + match &&&b { + box f => { + let _: &Foo = f; + }, + _ => panic!(), + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs b/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs new file mode 100644 index 0000000000000..1b8fdbaa4d75c --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs @@ -0,0 +1,51 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +const CONST_REF: &[u8; 3] = b"foo"; + +trait Foo { + const CONST_REF_DEFAULT: &'static [u8; 3] = b"bar"; + const CONST_REF: &'static [u8; 3]; +} + +impl Foo for i32 { + const CONST_REF: &'static [u8; 3] = b"jjj"; +} + +impl Foo for i64 { + const CONST_REF_DEFAULT: &'static [u8; 3] = b"ggg"; + const CONST_REF: &'static [u8; 3] = b"fff"; +} + +// Check that (associated and free) const references are not mistaken for a +// non-reference pattern (in which case they would be auto-dereferenced, making +// the types mismatched). + +fn const_ref() -> bool { + let f = b"foo"; + match f { + CONST_REF => true, + _ => false, + } +} + +fn associated_const_ref() -> bool { + match (b"bar", b"jjj", b"ggg", b"fff") { + (i32::CONST_REF_DEFAULT, i32::CONST_REF, i64::CONST_REF_DEFAULT, i64::CONST_REF) => true, + _ => false, + } +} + +pub fn main() { + assert!(const_ref()); + assert!(associated_const_ref()); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs b/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs new file mode 100644 index 0000000000000..a7b3db021b021 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs @@ -0,0 +1,56 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + println!("{}", *x); + + let Wrap(x) = &mut Wrap(3); + println!("{}", *x); + + if let Some(x) = &Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + *x += 1; + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + *x += 1; + break; + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/for.rs b/src/test/run-pass/rfc-2005-default-binding-mode/for.rs new file mode 100644 index 0000000000000..4feab94a7edf5 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/for.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +pub fn main() { + let mut tups = vec![(0u8, 1u8)]; + + for (n, m) in &tups { + let _: &u8 = n; + let _: &u8 = m; + } + + for (n, m) in &mut tups { + *n += 1; + *m += 2; + } + + assert_eq!(tups, vec![(1u8, 3u8)]); + + for (n, m) in tups { + println!("{} {}", m, n); + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/general.rs b/src/test/run-pass/rfc-2005-default-binding-mode/general.rs new file mode 100644 index 0000000000000..779a38bdb1672 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/general.rs @@ -0,0 +1,259 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +fn some_or_wildcard(r: &Option, b: &i32) { + let _: &i32 = match r { + Some(a) => a, + _ => b, + }; +} + +fn none_or_wildcard(r: &Option, b: &i32) { + let _: &i32 = match r { + None => b, + _ => b, + }; +} + +fn some_or_ref_none(r: &Option, b: &i32) { + let _: &i32 = match r { + Some(a) => a, + &None => b, + }; +} + +fn ref_some_or_none(r: &Option, b: &i32) { + let _: &i32 = match r { + &Some(ref a) => a, + None => b, + }; +} + +fn some_or_self(r: &Option) { + let _: &Option = match r { + Some(n) => { + let _: &i32 = n; + r + }, + x => x, + }; +} + +fn multiple_deref(r: &&&&&Option) { + let _: i32 = match r { + Some(a) => *a, + None => 5, + }; +} + +fn match_with_or() { + // FIXME(tschottdorf): #44912. + // + // let x = &Some((3, 3)); + // let _: &i32 = match x { + // Some((x, 3)) | &Some((ref x, 5)) => x, + // _ => &5i32, + // }; +} + +fn nested_mixed() { + match (&Some(5), &Some(6)) { + (Some(a), &Some(mut b)) => { + // Here, the `a` will be `&i32`, because in the first half of the tuple + // we hit a non-reference pattern and shift into `ref` mode. + // + // In the second half of the tuple there's no non-reference pattern, + // so `b` will be `i32` (bound with `move` mode). Moreover, `b` is + // mutable. + let _: &i32 = a; + b = 7; + let _: i32 = b; + }, + _ => {}, + }; +} + +fn nested_mixed_multiple_deref_1() { + let x = (1, &Some(5)); + let y = &Some(x); + match y { + Some((a, Some(b))) => { + let _: &i32 = a; + let _: &i32 = b; + }, + _ => {}, + }; +} + +fn nested_mixed_multiple_deref_2() { + let x = &Some(5); + let y = &x; + match y { + Some(z) => { + let _: &i32 = z; + }, + _ => {}, + } +} + +fn new_mutable_reference() { + let mut x = &mut Some(5); + match &mut x { + Some(y) => { + *y = 5; + }, + None => { }, + } + + match &mut x { + Some(y) => { + println!("{}", *y); + }, + None => {}, + } +} + +fn let_implicit_ref_binding() { + struct Foo(i32); + + // Note that these rules apply to any pattern matching + // whether it be in a `match` or a `let`. + // For example, `x` here is a `ref` binding: + let Foo(x) = &Foo(3); + let _: &i32 = x; +} + +fn explicit_mut_binding() { + match &Some(5i32) { + Some(mut n) => { + n += 1; + let _ = n; + } + None => {}, + }; + + match &mut Some(5i32) { + Some(n) => { + *n += 1; + let _ = n; + } + None => {}, + }; + + match &mut &mut Some(5i32) { + Some(n) => { + let _: &mut i32 = n; + } + None => {}, + }; +} + +fn tuple_mut_and_mut_mut() { + match (Some(5i32), &Some(5i32)) { + (Some(n), Some(m)) => { + // `n` and `m` are bound as immutable references. Make new references from them to + // assert that. + let r = n; + let _ = r; + let q = m; + let _ = q; + + // Assert the types. Note that we use `n` and `m` here which would fail had they been + // moved due to the assignments above. + let _: i32 = n; + let _: &i32 = m; + } + (_, _) => {}, + }; + + match (&Some(5i32), &&Some(5i32)) { + (Some(n), Some(m)) => { + let _: &i32 = n; + let _: &i32 = m; + } + (_, _) => {}, + }; + + match &mut &mut (Some(5i32), Some(5i32)) { + (Some(n), Some(m)) => { + // Dereferenced through &mut &mut, so a mutable binding results. + let _: &mut i32 = n; + let _: &mut i32 = m; + } + (_, _) => {}, + }; + + match (&mut Some(5i32), &mut &mut Some(5i32)) { + (Some(n), Some(m)) => { + let _: &mut i32 = n; + let _: &mut i32 = m; + } + (_, _) => {}, + }; +} + +fn min_mir_embedded_type() { + // The reduced invocation that an ICE was diagnosed with (was consuming + // adjustments in wrong order). + match (0u8, &&Some(5i32)) { + (_, Some(m)) => { + let _: &i32 = m; + } + (_, _) => {}, + }; +} + +fn no_autoderef() { + // Binding. + let x = &3; + println!("{}", *x); + + // Wildcard. + let _ = &3; + + // Constant of generic type (string) + const Y: &'static str = "foo"; + assert_eq!(0, match "foo" { + Y => 0, + _ => 1, + }); + + // Reference pattern. + let &x = &3; +} + +pub fn main() { + let r: &Option = &Some(3); + let b = &4i32; + + none_or_wildcard(r, b); + some_or_wildcard(r, b); + some_or_ref_none(r, b); + ref_some_or_none(r, b); + + some_or_self(r); + multiple_deref(&&&&r); + match_with_or(); + + nested_mixed(); + nested_mixed_multiple_deref_1(); + nested_mixed_multiple_deref_2(); + + new_mutable_reference(); + explicit_mut_binding(); + tuple_mut_and_mut_mut(); + min_mir_embedded_type(); + + let_implicit_ref_binding(); + + no_autoderef(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs b/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs new file mode 100644 index 0000000000000..0b2a8e52fbf77 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs @@ -0,0 +1,44 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +fn with_u8() { + let s = 5u8; + let r = match &s { + 4 => false, + 5 => true, + _ => false, + }; + assert!(r); +} + +// A string literal isn't mistaken for a non-ref pattern (in which case we'd +// deref `s` and mess things up). +fn with_str() { + let s: &'static str = "abc"; + match s { + "abc" => true, + _ => panic!(), + }; +} + +// Ditto with byte strings. +fn with_bytes() { + let s: &'static [u8] = b"abc"; + match s { + b"abc" => true, + _ => panic!(), + }; +} + +pub fn main() { + with_str(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/range.rs b/src/test/run-pass/rfc-2005-default-binding-mode/range.rs new file mode 100644 index 0000000000000..aafaa4cca82ca --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/range.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +pub fn main() { + let i = 5; + match &&&&i { + 1 ... 3 => panic!(), + 3 ... 8 => {}, + _ => panic!(), + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs b/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs new file mode 100644 index 0000000000000..de7df011b56fd --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +fn foo<'a, 'b>(x: &'a &'b Option) -> &'a u32 { + let x: &'a &'a Option = x; + match x { + Some(r) => { + let _: &u32 = r; + r + }, + &None => panic!(), + } +} + +pub fn main() { + let x = Some(5); + foo(&&x); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs new file mode 100644 index 0000000000000..1717d0d54c026 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs @@ -0,0 +1,36 @@ +// 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. + +#![feature(slice_patterns)] +#![feature(match_default_bindings)] + +fn slice_pat() { + let sl: &[u8] = b"foo"; + + match sl { + [first, remainder..] => { + let _: &u8 = first; + assert_eq!(first, &b'f'); + assert_eq!(remainder, b"oo"); + } + [] => panic!(), + } +} + +fn slice_pat_omission() { + match &[0, 1, 2] { + [..] => {} + }; +} + +fn main() { + slice_pat(); + slice_pat_omission(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs b/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs new file mode 100644 index 0000000000000..11a675c0c72af --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +#[derive(Debug, PartialEq)] +struct Foo { + x: u8, +} + +pub fn main() { + let mut foo = Foo { + x: 1, + }; + + match &mut foo { + Foo{x: n} => { + *n += 1; + }, + }; + + assert_eq!(foo, Foo{x: 2}); + + let Foo{x: n} = &foo; + assert_eq!(*n, 2); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs b/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs new file mode 100644 index 0000000000000..7867d6529050d --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +enum Foo { + Bar(Option, (), (), Vec), + Baz, +} + +pub fn main() { + let foo = Foo::Bar(Some(1), (), (), vec![2, 3]); + + match &foo { + Foo::Baz => panic!(), + Foo::Bar(None, ..) => panic!(), + Foo::Bar(Some(n), .., v) => { + assert_eq!((*v).len(), 2); + assert_eq!(*n, 1); + } + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs b/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs new file mode 100644 index 0000000000000..cf27265b2ed56 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +pub fn main() { + let foo = (Some(1), (), (), vec![2, 3]); + + match &foo { + (Some(n), .., v) => { + assert_eq!((*v).len(), 2); + assert_eq!(*n, 1); + } + (None, (), (), ..) => panic!(), + } +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/enums.rs b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/enums.rs new file mode 100644 index 0000000000000..12d1bf9ea9104 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/enums.rs @@ -0,0 +1,19 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub enum NonExhaustiveEnum { + Unit, + Tuple(u32), + Struct { field: u32 } +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/structs.rs b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/structs.rs new file mode 100644 index 0000000000000..a2c6f8c05e2c9 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/structs.rs @@ -0,0 +1,23 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub struct NormalStruct { + pub first_field: u16, + pub second_field: u16, +} + +#[non_exhaustive] +pub struct UnitStruct; + +#[non_exhaustive] +pub struct TupleStruct (pub u16, pub u16); diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/variants.rs b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/variants.rs new file mode 100644 index 0000000000000..d04c1073ad9b3 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/auxiliary/variants.rs @@ -0,0 +1,18 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] +#![feature(non_exhaustive)] + +pub enum NonExhaustiveVariants { + #[non_exhaustive] Unit, + #[non_exhaustive] Tuple(u32), + #[non_exhaustive] Struct { field: u32 } +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs b/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs new file mode 100644 index 0000000000000..9d41eca8fe5d2 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/enums.rs @@ -0,0 +1,33 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:enums.rs +extern crate enums; + +// ignore-pretty issue #37199 + +use enums::NonExhaustiveEnum; + +fn main() { + let enum_unit = NonExhaustiveEnum::Unit; + + match enum_unit { + NonExhaustiveEnum::Unit => 1, + NonExhaustiveEnum::Tuple(_) => 2, + // This particular arm tests that a enum marked as non-exhaustive + // will not error if its variants are matched exhaustively. + NonExhaustiveEnum::Struct { field } => field, + _ => 0 // no error with wildcard + }; + + match enum_unit { + _ => "no error with only wildcard" + }; +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/enums_same_crate.rs b/src/test/run-pass/rfc-2008-non-exhaustive/enums_same_crate.rs new file mode 100644 index 0000000000000..8f1ba364b0e2b --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/enums_same_crate.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub enum NonExhaustiveEnum { + Unit, + Tuple(u32), + Struct { field: u32 } +} + +fn main() { + let enum_unit = NonExhaustiveEnum::Unit; + + match enum_unit { + NonExhaustiveEnum::Unit => "first", + NonExhaustiveEnum::Tuple(_) => "second", + NonExhaustiveEnum::Struct { .. } => "third", + }; +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/structs.rs b/src/test/run-pass/rfc-2008-non-exhaustive/structs.rs new file mode 100644 index 0000000000000..bb65e10da27bf --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/structs.rs @@ -0,0 +1,27 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:structs.rs +extern crate structs; + +use structs::{NormalStruct, UnitStruct, TupleStruct}; + +// We only test matching here as we cannot create non-exhaustive +// structs from another crate. ie. they'll never pass in run-pass tests. + +fn match_structs(ns: NormalStruct, ts: TupleStruct, us: UnitStruct) { + let NormalStruct { first_field, second_field, .. } = ns; + + let TupleStruct { 0: first, 1: second, .. } = ts; + + let UnitStruct { .. } = us; +} + +fn main() { } diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/structs_same_crate.rs b/src/test/run-pass/rfc-2008-non-exhaustive/structs_same_crate.rs new file mode 100644 index 0000000000000..175782f10fc91 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/structs_same_crate.rs @@ -0,0 +1,40 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +#[non_exhaustive] +pub struct NormalStruct { + pub first_field: u16, + pub second_field: u16, +} + +#[non_exhaustive] +pub struct UnitStruct; + +#[non_exhaustive] +pub struct TupleStruct (pub u16, pub u16); + +fn main() { + let ns = NormalStruct { first_field: 640, second_field: 480 }; + + let NormalStruct { first_field, second_field } = ns; + + let ts = TupleStruct { 0: 340, 1: 480 }; + let ts_constructor = TupleStruct(340, 480); + + let TupleStruct { 0: first, 1: second } = ts; + let TupleStruct(first, second) = ts_constructor; + + let us = UnitStruct {}; + let us_constructor = UnitStruct; + + let UnitStruct { } = us; +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs b/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs new file mode 100644 index 0000000000000..2658c59a69985 --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/variants.rs @@ -0,0 +1,31 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:variants.rs +extern crate variants; + +use variants::NonExhaustiveVariants; + +/* + * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for + * variants. See issue #44109 and PR 45394. + */ +// ignore-test + +fn main() { + let variant_tuple = NonExhaustiveVariants::Tuple { 0: 340 }; + let variant_struct = NonExhaustiveVariants::Struct { field: 340 }; + + match variant_struct { + NonExhaustiveVariants::Unit => "", + NonExhaustiveVariants::Struct { field, .. } => "", + NonExhaustiveVariants::Tuple(fe_tpl, ..) => "" + }; +} diff --git a/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs b/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs new file mode 100644 index 0000000000000..a1c376c17985d --- /dev/null +++ b/src/test/run-pass/rfc-2008-non-exhaustive/variants_same_crate.rs @@ -0,0 +1,34 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(non_exhaustive)] + +/* + * The initial implementation of #[non_exhaustive] (RFC 2008) does not include support for + * variants. See issue #44109 and PR 45394. + */ +// ignore-test + +pub enum NonExhaustiveVariants { + #[non_exhaustive] Unit, + #[non_exhaustive] Tuple(u32), + #[non_exhaustive] Struct { field: u32 } +} + +fn main() { + let variant_tuple = NonExhaustiveVariants::Tuple(340); + let variant_struct = NonExhaustiveVariants::Struct { field: 340 }; + + match variant_tuple { + NonExhaustiveVariants::Unit => "", + NonExhaustiveVariants::Tuple(fe_tpl) => "", + NonExhaustiveVariants::Struct { field } => "" + }; +} diff --git a/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs b/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs new file mode 100644 index 0000000000000..172c34e79d233 --- /dev/null +++ b/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(crate_in_paths)] + +use crate::m::f; + +mod m { + pub fn f() -> u8 { 1 } + pub fn g() -> u8 { 2 } + + // OK, visibilities are implicitly absolute like imports + pub(in crate::m) struct S; +} + +mod n +{ + use crate::m::f; + pub fn check() { + assert_eq!(f(), 1); + assert_eq!(::crate::m::g(), 2); + } +} + +fn main() { + assert_eq!(f(), 1); + assert_eq!(::crate::m::g(), 2); + n::check(); +} diff --git a/src/test/run-pass/rfc1717/library-override.rs b/src/test/run-pass/rfc1717/library-override.rs index 26713a2554377..c51b33f9c4af6 100644 --- a/src/test/run-pass/rfc1717/library-override.rs +++ b/src/test/run-pass/rfc1717/library-override.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with // compile-flags: -lstatic=wronglibrary:rust_test_helpers #[link(name = "wronglibrary", kind = "dylib")] diff --git a/src/test/run-pass/rfc1857-drop-order.rs b/src/test/run-pass/rfc1857-drop-order.rs index 42f989538c890..b2e5ff62eb86e 100644 --- a/src/test/run-pass/rfc1857-drop-order.rs +++ b/src/test/run-pass/rfc1857-drop-order.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default + #![allow(dead_code, unreachable_code)] use std::cell::RefCell; diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index f81c3f2e99d36..6f696c1c9cea5 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten spawning processes is not supported #![feature(start)] diff --git a/src/test/run-pass/saturating-float-casts.rs b/src/test/run-pass/saturating-float-casts.rs new file mode 100644 index 0000000000000..c8fa49c62a0b6 --- /dev/null +++ b/src/test/run-pass/saturating-float-casts.rs @@ -0,0 +1,144 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. +// compile-flags: -Z saturating-float-casts + +#![feature(test, i128, i128_type, stmt_expr_attributes)] +#![deny(overflowing_literals)] +extern crate test; + +use std::{f32, f64}; +use std::{u8, i8, u16, i16, u32, i32, u64, i64}; +#[cfg(not(target_os="emscripten"))] +use std::{u128, i128}; +use test::black_box; + +macro_rules! test { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( + // black_box disables constant evaluation to test run-time conversions: + assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, + "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + ); + + ($fval:expr, f* -> $ity:ident, $ival:expr) => ( + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); + ) +} + +// This macro tests const eval in addition to run-time evaluation. +// If and when saturating casts are adopted, this macro should be merged with test!() to ensure +// that run-time and const eval agree on inputs that currently trigger a const eval error. +macro_rules! test_c { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ + test!($val, $src_ty -> $dest_ty, $expected); + { + const X: $src_ty = $val; + const Y: $dest_ty = X as $dest_ty; + assert_eq!(Y, $expected, + "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + } + }); + + ($fval:expr, f* -> $ity:ident, $ival:expr) => ( + test_c!($fval, f32 -> $ity, $ival); + test_c!($fval, f64 -> $ity, $ival); + ) +} + +macro_rules! common_fptoi_tests { + ($fty:ident -> $($ity:ident)+) => ({ $( + test!($fty::NAN, $fty -> $ity, 0); + test!($fty::INFINITY, $fty -> $ity, $ity::MAX); + test!($fty::NEG_INFINITY, $fty -> $ity, $ity::MIN); + // These two tests are not solely float->int tests, in particular the latter relies on + // `u128::MAX as f32` not being UB. But that's okay, since this file tests int->float + // as well, the test is just slightly misplaced. + test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); + test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); + test_c!(0., $fty -> $ity, 0); + test_c!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.9, $fty -> $ity, 0); + test_c!(1., $fty -> $ity, 1); + test_c!(42., $fty -> $ity, 42); + )+ }); + + (f* -> $($ity:ident)+) => ({ + common_fptoi_tests!(f32 -> $($ity)+); + common_fptoi_tests!(f64 -> $($ity)+); + }) +} + +macro_rules! fptoui_tests { + ($fty: ident -> $($ity: ident)+) => ({ $( + test!(-0., $fty -> $ity, 0); + test!(-$fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(-0.99999994, $fty -> $ity, 0); + test!(-1., $fty -> $ity, 0); + test!(-100., $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e50, $fty -> $ity, 0); + test!(#[allow(overflowing_literals)] -1e130, $fty -> $ity, 0); + )+ }); + + (f* -> $($ity:ident)+) => ({ + fptoui_tests!(f32 -> $($ity)+); + fptoui_tests!(f64 -> $($ity)+); + }) +} + +pub fn main() { + common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); + fptoui_tests!(f* -> u8 u16 u32 u64); + // FIXME emscripten does not support i128 + #[cfg(not(target_os="emscripten"))] { + common_fptoi_tests!(f* -> i128 u128); + fptoui_tests!(f* -> u128); + } + + // The following tests cover edge cases for some integer types. + + // # u8 + test_c!(254., f* -> u8, 254); + test!(256., f* -> u8, 255); + + // # i8 + test_c!(-127., f* -> i8, -127); + test!(-129., f* -> i8, -128); + test_c!(126., f* -> i8, 126); + test!(128., f* -> i8, 127); + + // # i32 + // -2147483648. is i32::MIN (exactly) + test_c!(-2147483648., f* -> i32, i32::MIN); + // 2147483648. is i32::MAX rounded up + test!(2147483648., f32 -> i32, 2147483647); + // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to + // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: + test_c!(2147483520., f32 -> i32, 2147483520); + // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 + test!(-2147483904., f* -> i32, i32::MIN); + test_c!(-2147483520., f* -> i32, -2147483520); + + // # u32 + // round(MAX) and nextUp(round(MAX)) + test_c!(4294967040., f* -> u32, 4294967040); + test!(4294967296., f* -> u32, 4294967295); + + // # u128 + #[cfg(not(target_os="emscripten"))] + { + // float->int: + test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + // nextDown(f32::MAX) = 2^128 - 2 * 2^104 + const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; + test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + } +} diff --git a/src/test/run-pass/signal-alternate-stack-cleanup.rs b/src/test/run-pass/signal-alternate-stack-cleanup.rs index 26fa36f0c13d4..73ccd28a3e963 100644 --- a/src/test/run-pass/signal-alternate-stack-cleanup.rs +++ b/src/test/run-pass/signal-alternate-stack-cleanup.rs @@ -13,6 +13,7 @@ // triggers this situation by sending signal from atexit handler. // // ignore-windows +// ignore-wasm32-bare no libc #![feature(libc)] extern crate libc; diff --git a/src/test/run-pass/signal-exit-status.rs b/src/test/run-pass/signal-exit-status.rs index 8a2bbc83c424e..0f6832acec814 100644 --- a/src/test/run-pass/signal-exit-status.rs +++ b/src/test/run-pass/signal-exit-status.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-windows -// ignore-emscripten +// ignore-emscripten no processes use std::env; use std::process::Command; diff --git a/src/test/run-pass/sigpipe-should-be-ignored.rs b/src/test/run-pass/sigpipe-should-be-ignored.rs index 5aa4faa136565..465feb4b7790e 100644 --- a/src/test/run-pass/sigpipe-should-be-ignored.rs +++ b/src/test/run-pass/sigpipe-should-be-ignored.rs @@ -11,7 +11,7 @@ // Be sure that when a SIGPIPE would have been received that the entire process // doesn't die in a ball of fire, but rather it's gracefully handled. -// ignore-emscripten +// ignore-emscripten no processes use std::env; use std::io::prelude::*; diff --git a/src/test/run-pass/simd-intrinsic-generic-arithmetic.rs b/src/test/run-pass/simd-intrinsic-generic-arithmetic.rs index 5d4ecbb5f8172..1894cd0084bcb 100644 --- a/src/test/run-pass/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/run-pass/simd-intrinsic-generic-arithmetic.rs @@ -35,6 +35,7 @@ extern "platform-intrinsic" { fn simd_sub(x: T, y: T) -> T; fn simd_mul(x: T, y: T) -> T; fn simd_div(x: T, y: T) -> T; + fn simd_rem(x: T, y: T) -> T; fn simd_shl(x: T, y: T) -> T; fn simd_shr(x: T, y: T) -> T; fn simd_and(x: T, y: T) -> T; @@ -72,9 +73,22 @@ fn main() { all_eq!(simd_sub(z2, z1), f32x4(1.0, 1.0, 1.0, 1.0)); all_eq!(simd_sub(z1, z2), f32x4(-1.0, -1.0, -1.0, -1.0)); + all_eq!(simd_div(x1, x1), i32x4(1, 1, 1, 1)); + all_eq!(simd_div(i32x4(2, 4, 6, 8), i32x4(2, 2, 2, 2)), x1); + all_eq!(simd_div(y1, y1), u32x4(1, 1, 1, 1)); + all_eq!(simd_div(u32x4(2, 4, 6, 8), u32x4(2, 2, 2, 2)), y1); + all_eq!(simd_div(z1, z1), f32x4(1.0, 1.0, 1.0, 1.0)); all_eq!(simd_div(z1, z2), f32x4(1.0/2.0, 2.0/3.0, 3.0/4.0, 4.0/5.0)); all_eq!(simd_div(z2, z1), f32x4(2.0/1.0, 3.0/2.0, 4.0/3.0, 5.0/4.0)); + all_eq!(simd_rem(x1, x1), i32x4(0, 0, 0, 0)); + all_eq!(simd_rem(x2, x1), i32x4(0, 1, 1, 1)); + all_eq!(simd_rem(y1, y1), u32x4(0, 0, 0, 0)); + all_eq!(simd_rem(y2, y1), u32x4(0, 1, 1, 1)); + all_eq!(simd_rem(z1, z1), f32x4(0.0, 0.0, 0.0, 0.0)); + all_eq!(simd_rem(z1, z2), z1); + all_eq!(simd_rem(z2, z1), f32x4(0.0, 1.0, 1.0, 1.0)); + all_eq!(simd_shl(x1, x2), i32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5)); all_eq!(simd_shl(x2, x1), i32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4)); all_eq!(simd_shl(y1, y2), u32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5)); diff --git a/src/test/run-pass/simd-intrinsic-generic-cast.rs b/src/test/run-pass/simd-intrinsic-generic-cast.rs index ede2325b51c2f..ed2786edf3a3f 100644 --- a/src/test/run-pass/simd-intrinsic-generic-cast.rs +++ b/src/test/run-pass/simd-intrinsic-generic-cast.rs @@ -7,7 +7,8 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten linking with emcc failed + +// ignore-emscripten FIXME(#45351) hits an LLVM assert #![feature(repr_simd, platform_intrinsics, concat_idents, test)] #![allow(non_camel_case_types)] diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs deleted file mode 100644 index bcbd3fd3786ab..0000000000000 --- a/src/test/run-pass/smallest-hello-world.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Smallest "hello world" with a libc runtime - -// ignore-windows -// ignore-android - -#![feature(intrinsics, lang_items, start, no_core, alloc_system)] -#![feature(global_allocator, allocator_api)] -#![no_std] - -extern crate alloc_system; - -use alloc_system::System; - -#[global_allocator] -static A: System = System; - -extern { - fn puts(s: *const u8); -} - -#[no_mangle] -#[lang = "eh_personality"] pub extern fn rust_eh_personality() {} -#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } - -#[start] -fn main(_: isize, _: *const *const u8) -> isize { - unsafe { - puts("Hello!\0".as_ptr() as *const u8); - } - return 0 -} diff --git a/src/test/run-pass/specialization/auxiliary/specialization_cross_crate_defaults.rs b/src/test/run-pass/specialization/auxiliary/cross_crates_defaults.rs similarity index 100% rename from src/test/run-pass/specialization/auxiliary/specialization_cross_crate_defaults.rs rename to src/test/run-pass/specialization/auxiliary/cross_crates_defaults.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/cross-crate-defaults.rs similarity index 87% rename from src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-defaults.rs rename to src/test/run-pass/specialization/cross-crate-defaults.rs index 62c7e3e2e4431..132520dcb736e 100644 --- a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-defaults.rs +++ b/src/test/run-pass/specialization/cross-crate-defaults.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:specialization_cross_crate_defaults.rs +// aux-build:cross_crates_defaults.rs #![feature(specialization)] -extern crate specialization_cross_crate_defaults; +extern crate cross_crates_defaults; -use specialization_cross_crate_defaults::*; +use cross_crates_defaults::*; struct LocalDefault; struct LocalOverride; diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization/defaultimpl/allowed-cross-crate.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-allowed-cross-crate.rs rename to src/test/run-pass/specialization/defaultimpl/allowed-cross-crate.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-assoc-fns.rs b/src/test/run-pass/specialization/defaultimpl/assoc-fns.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-assoc-fns.rs rename to src/test/run-pass/specialization/defaultimpl/assoc-fns.rs diff --git a/src/test/run-pass/specialization/defaultimpl/auxiliary/specialization_cross_crate.rs b/src/test/run-pass/specialization/defaultimpl/auxiliary/cross_crate.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/auxiliary/specialization_cross_crate.rs rename to src/test/run-pass/specialization/defaultimpl/auxiliary/cross_crate.rs diff --git a/src/test/run-pass/specialization/defaultimpl/auxiliary/specialization_cross_crate_defaults.rs b/src/test/run-pass/specialization/defaultimpl/auxiliary/cross_crate_defaults.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/auxiliary/specialization_cross_crate_defaults.rs rename to src/test/run-pass/specialization/defaultimpl/auxiliary/cross_crate_defaults.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-basics-unsafe.rs b/src/test/run-pass/specialization/defaultimpl/basics-unsafe.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-basics-unsafe.rs rename to src/test/run-pass/specialization/defaultimpl/basics-unsafe.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-basics.rs b/src/test/run-pass/specialization/defaultimpl/basics.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-basics.rs rename to src/test/run-pass/specialization/defaultimpl/basics.rs diff --git a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/defaultimpl/cross-crate-defaults.rs similarity index 87% rename from src/test/run-pass/specialization/specialization-cross-crate-defaults.rs rename to src/test/run-pass/specialization/defaultimpl/cross-crate-defaults.rs index 62c7e3e2e4431..19e1af15bdd56 100644 --- a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs +++ b/src/test/run-pass/specialization/defaultimpl/cross-crate-defaults.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:specialization_cross_crate_defaults.rs +// aux-build:cross_crate_defaults.rs #![feature(specialization)] -extern crate specialization_cross_crate_defaults; +extern crate cross_crate_defaults; -use specialization_cross_crate_defaults::*; +use cross_crate_defaults::*; struct LocalDefault; struct LocalOverride; diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-no-gate.rs b/src/test/run-pass/specialization/defaultimpl/cross-crate-no-gate.rs similarity index 89% rename from src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-no-gate.rs rename to src/test/run-pass/specialization/defaultimpl/cross-crate-no-gate.rs index b9548539e1649..67cc694ae12c7 100644 --- a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate-no-gate.rs +++ b/src/test/run-pass/specialization/defaultimpl/cross-crate-no-gate.rs @@ -10,11 +10,11 @@ // Test that specialization works even if only the upstream crate enables it -// aux-build:specialization_cross_crate.rs +// aux-build:cross_crate.rs -extern crate specialization_cross_crate; +extern crate cross_crate; -use specialization_cross_crate::*; +use cross_crate::*; fn main() { assert!(0u8.foo() == "generic Clone"); diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate.rs b/src/test/run-pass/specialization/defaultimpl/cross-crate.rs similarity index 93% rename from src/test/run-pass/specialization/defaultimpl/specialization-cross-crate.rs rename to src/test/run-pass/specialization/defaultimpl/cross-crate.rs index 7517824b62bba..f1ad105db8f7c 100644 --- a/src/test/run-pass/specialization/defaultimpl/specialization-cross-crate.rs +++ b/src/test/run-pass/specialization/defaultimpl/cross-crate.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:specialization_cross_crate.rs +// aux-build:cross_crate.rs #![feature(specialization)] -extern crate specialization_cross_crate; +extern crate cross_crate; -use specialization_cross_crate::*; +use cross_crate::*; struct NotClone; diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-default-methods.rs b/src/test/run-pass/specialization/defaultimpl/default-methods.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-default-methods.rs rename to src/test/run-pass/specialization/defaultimpl/default-methods.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-out-of-order.rs b/src/test/run-pass/specialization/defaultimpl/out-of-order.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-out-of-order.rs rename to src/test/run-pass/specialization/defaultimpl/out-of-order.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-overlap-projection.rs b/src/test/run-pass/specialization/defaultimpl/overlap-projection.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-overlap-projection.rs rename to src/test/run-pass/specialization/defaultimpl/overlap-projection.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-projection-alias.rs b/src/test/run-pass/specialization/defaultimpl/projection-alias.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-projection-alias.rs rename to src/test/run-pass/specialization/defaultimpl/projection-alias.rs diff --git a/src/test/run-pass/specialization/defaultimpl/specialization-projection.rs b/src/test/run-pass/specialization/defaultimpl/projection.rs similarity index 100% rename from src/test/run-pass/specialization/defaultimpl/specialization-projection.rs rename to src/test/run-pass/specialization/defaultimpl/projection.rs diff --git a/src/test/run-pass/stack-probes-lto.rs b/src/test/run-pass/stack-probes-lto.rs index f49320e4da4a3..78a1019578e35 100644 --- a/src/test/run-pass/stack-probes-lto.rs +++ b/src/test/run-pass/stack-probes-lto.rs @@ -11,7 +11,7 @@ // ignore-arm // ignore-aarch64 // ignore-wasm -// ignore-emscripten +// ignore-emscripten no processes // ignore-musl FIXME #31506 // ignore-pretty // no-system-llvm diff --git a/src/test/run-pass/stack-probes.rs b/src/test/run-pass/stack-probes.rs index 1d66cb6020760..bb9471e1b48bc 100644 --- a/src/test/run-pass/stack-probes.rs +++ b/src/test/run-pass/stack-probes.rs @@ -11,7 +11,7 @@ // ignore-arm // ignore-aarch64 // ignore-wasm -// ignore-emscripten +// ignore-emscripten no processes // ignore-musl FIXME #31506 // no-system-llvm diff --git a/src/test/run-pass/static-method-xcrate.rs b/src/test/run-pass/static-method-xcrate.rs index 57609cec9f2b0..ab6adcbbffe7a 100644 --- a/src/test/run-pass/static-method-xcrate.rs +++ b/src/test/run-pass/static-method-xcrate.rs @@ -10,7 +10,6 @@ // aux-build:static-methods-crate.rs - extern crate static_methods_crate; use static_methods_crate::read; diff --git a/src/test/run-pass/static-mut-foreign.rs b/src/test/run-pass/static-mut-foreign.rs index 24d58487f061e..2b7fa0166a8ed 100644 --- a/src/test/run-pass/static-mut-foreign.rs +++ b/src/test/run-pass/static-mut-foreign.rs @@ -12,6 +12,7 @@ // statics cannot. This ensures that there's some form of error if this is // attempted. +// ignore-wasm32-bare no libc to test ffi with #![feature(libc)] diff --git a/src/test/run-pass/stdio-is-blocking.rs b/src/test/run-pass/stdio-is-blocking.rs index 448bb7de7727f..cce1077202c3f 100644 --- a/src/test/run-pass/stdio-is-blocking.rs +++ b/src/test/run-pass/stdio-is-blocking.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes use std::env; use std::io::prelude::*; diff --git a/src/test/run-pass/struct-order-of-eval-3.rs b/src/test/run-pass/struct-order-of-eval-3.rs index cf93133d20593..3059c8e9e8963 100644 --- a/src/test/run-pass/struct-order-of-eval-3.rs +++ b/src/test/run-pass/struct-order-of-eval-3.rs @@ -11,8 +11,6 @@ // Checks that functional-record-update order-of-eval is as expected // even when no Drop-implementations are involved. -#![feature(const_atomic_usize_new)] - use std::sync::atomic::{Ordering, AtomicUsize}; struct W { wrapped: u32 } diff --git a/src/test/run-pass/struct-order-of-eval-4.rs b/src/test/run-pass/struct-order-of-eval-4.rs index d442923478f1f..2ae9ebc34e1b3 100644 --- a/src/test/run-pass/struct-order-of-eval-4.rs +++ b/src/test/run-pass/struct-order-of-eval-4.rs @@ -11,8 +11,6 @@ // Checks that struct-literal expression order-of-eval is as expected // even when no Drop-implementations are involved. -#![feature(const_atomic_usize_new)] - use std::sync::atomic::{Ordering, AtomicUsize}; struct W { wrapped: u32 } diff --git a/src/test/run-pass/struct-return.rs b/src/test/run-pass/struct-return.rs index ed618cea98ac0..61a2bcb3a9b75 100644 --- a/src/test/run-pass/struct-return.rs +++ b/src/test/run-pass/struct-return.rs @@ -7,7 +7,8 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// + +// ignore-wasm32-bare no libc to test ffi with #[repr(C)] #[derive(Copy, Clone)] diff --git a/src/test/run-pass/supported-cast.rs b/src/test/run-pass/supported-cast.rs index a47ae52f5902c..7f92707586b28 100644 --- a/src/test/run-pass/supported-cast.rs +++ b/src/test/run-pass/supported-cast.rs @@ -8,12 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(libc)] - -extern crate libc; - pub fn main() { - let f = 1_usize as *const libc::FILE; + let f = 1_usize as *const String; println!("{:?}", f as isize); println!("{:?}", f as usize); println!("{:?}", f as i8); @@ -27,7 +23,7 @@ pub fn main() { println!("{:?}", 1 as isize); println!("{:?}", 1 as usize); - println!("{:?}", 1 as *const libc::FILE); + println!("{:?}", 1 as *const String); println!("{:?}", 1 as i8); println!("{:?}", 1 as i16); println!("{:?}", 1 as i32); @@ -41,7 +37,7 @@ pub fn main() { println!("{:?}", 1_usize as isize); println!("{:?}", 1_usize as usize); - println!("{:?}", 1_usize as *const libc::FILE); + println!("{:?}", 1_usize as *const String); println!("{:?}", 1_usize as i8); println!("{:?}", 1_usize as i16); println!("{:?}", 1_usize as i32); @@ -55,7 +51,7 @@ pub fn main() { println!("{:?}", 1i8 as isize); println!("{:?}", 1i8 as usize); - println!("{:?}", 1i8 as *const libc::FILE); + println!("{:?}", 1i8 as *const String); println!("{:?}", 1i8 as i8); println!("{:?}", 1i8 as i16); println!("{:?}", 1i8 as i32); @@ -69,7 +65,7 @@ pub fn main() { println!("{:?}", 1u8 as isize); println!("{:?}", 1u8 as usize); - println!("{:?}", 1u8 as *const libc::FILE); + println!("{:?}", 1u8 as *const String); println!("{:?}", 1u8 as i8); println!("{:?}", 1u8 as i16); println!("{:?}", 1u8 as i32); @@ -83,7 +79,7 @@ pub fn main() { println!("{:?}", 1i16 as isize); println!("{:?}", 1i16 as usize); - println!("{:?}", 1i16 as *const libc::FILE); + println!("{:?}", 1i16 as *const String); println!("{:?}", 1i16 as i8); println!("{:?}", 1i16 as i16); println!("{:?}", 1i16 as i32); @@ -97,7 +93,7 @@ pub fn main() { println!("{:?}", 1u16 as isize); println!("{:?}", 1u16 as usize); - println!("{:?}", 1u16 as *const libc::FILE); + println!("{:?}", 1u16 as *const String); println!("{:?}", 1u16 as i8); println!("{:?}", 1u16 as i16); println!("{:?}", 1u16 as i32); @@ -111,7 +107,7 @@ pub fn main() { println!("{:?}", 1i32 as isize); println!("{:?}", 1i32 as usize); - println!("{:?}", 1i32 as *const libc::FILE); + println!("{:?}", 1i32 as *const String); println!("{:?}", 1i32 as i8); println!("{:?}", 1i32 as i16); println!("{:?}", 1i32 as i32); @@ -125,7 +121,7 @@ pub fn main() { println!("{:?}", 1u32 as isize); println!("{:?}", 1u32 as usize); - println!("{:?}", 1u32 as *const libc::FILE); + println!("{:?}", 1u32 as *const String); println!("{:?}", 1u32 as i8); println!("{:?}", 1u32 as i16); println!("{:?}", 1u32 as i32); @@ -139,7 +135,7 @@ pub fn main() { println!("{:?}", 1i64 as isize); println!("{:?}", 1i64 as usize); - println!("{:?}", 1i64 as *const libc::FILE); + println!("{:?}", 1i64 as *const String); println!("{:?}", 1i64 as i8); println!("{:?}", 1i64 as i16); println!("{:?}", 1i64 as i32); @@ -153,7 +149,7 @@ pub fn main() { println!("{:?}", 1u64 as isize); println!("{:?}", 1u64 as usize); - println!("{:?}", 1u64 as *const libc::FILE); + println!("{:?}", 1u64 as *const String); println!("{:?}", 1u64 as i8); println!("{:?}", 1u64 as i16); println!("{:?}", 1u64 as i32); @@ -167,7 +163,7 @@ pub fn main() { println!("{:?}", 1u64 as isize); println!("{:?}", 1u64 as usize); - println!("{:?}", 1u64 as *const libc::FILE); + println!("{:?}", 1u64 as *const String); println!("{:?}", 1u64 as i8); println!("{:?}", 1u64 as i16); println!("{:?}", 1u64 as i32); diff --git a/src/test/run-pass/sync-send-in-std.rs b/src/test/run-pass/sync-send-in-std.rs index 85ab59a298323..4dadfdf9c756e 100644 --- a/src/test/run-pass/sync-send-in-std.rs +++ b/src/test/run-pass/sync-send-in-std.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare networking not available + #![feature(lookup_host)] use std::net::lookup_host; diff --git a/src/test/run-pass/test-allow-fail-attr.rs b/src/test/run-pass/test-allow-fail-attr.rs index aa9cf76617f69..884633df66b1b 100644 --- a/src/test/run-pass/test-allow-fail-attr.rs +++ b/src/test/run-pass/test-allow-fail-attr.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: --test #![feature(allow_fail)] diff --git a/src/test/run-pass/test-should-fail-good-message.rs b/src/test/run-pass/test-should-fail-good-message.rs index e665fa4fc7b58..360d4952d16aa 100644 --- a/src/test/run-pass/test-should-fail-good-message.rs +++ b/src/test/run-pass/test-should-fail-good-message.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare compiled with panic=abort by default // compile-flags: --test #[test] #[should_panic(expected = "foo")] diff --git a/src/test/run-pass/thin-lto-global-allocator.rs b/src/test/run-pass/thin-lto-global-allocator.rs new file mode 100644 index 0000000000000..1c15da5469e52 --- /dev/null +++ b/src/test/run-pass/thin-lto-global-allocator.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=2 +// min-llvm-version 4.0 + +#![feature(allocator_api, global_allocator)] + +#[global_allocator] +static A: std::heap::System = std::heap::System; + +fn main() {} diff --git a/src/test/run-pass/thinlto/auxiliary/dylib.rs b/src/test/run-pass/thinlto/auxiliary/dylib.rs new file mode 100644 index 0000000000000..cdb3f49cae88b --- /dev/null +++ b/src/test/run-pass/thinlto/auxiliary/dylib.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=8 + +#[inline] +pub fn foo(b: u8) { + b.to_string(); +} diff --git a/src/test/run-pass/thinlto/auxiliary/msvc-imp-present.rs b/src/test/run-pass/thinlto/auxiliary/msvc-imp-present.rs new file mode 100644 index 0000000000000..eff7802a24519 --- /dev/null +++ b/src/test/run-pass/thinlto/auxiliary/msvc-imp-present.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic +// compile-flags: -Z thinlto -C codegen-units=8 -C prefer-dynamic + +#![crate_type = "rlib"] +#![crate_type = "dylib"] + +pub static A: u32 = 43; + +pub mod a { + pub static A: u32 = 43; +} diff --git a/src/test/run-pass/thinlto/auxiliary/thin-lto-inlines-aux.rs b/src/test/run-pass/thinlto/auxiliary/thin-lto-inlines-aux.rs new file mode 100644 index 0000000000000..ccbb0e7a71863 --- /dev/null +++ b/src/test/run-pass/thinlto/auxiliary/thin-lto-inlines-aux.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic + +#![crate_type = "rlib"] + +pub fn bar() -> u32 { + 3 +} diff --git a/src/test/run-pass/thinlto/dylib-works.rs b/src/test/run-pass/thinlto/dylib-works.rs new file mode 100644 index 0000000000000..3f54519d0d8ce --- /dev/null +++ b/src/test/run-pass/thinlto/dylib-works.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:dylib.rs +// min-llvm-version 4.0 + +extern crate dylib; + +fn main() { + dylib::foo(1); +} diff --git a/src/test/run-pass/thinlto/msvc-imp-present.rs b/src/test/run-pass/thinlto/msvc-imp-present.rs new file mode 100644 index 0000000000000..8329c7032f1b6 --- /dev/null +++ b/src/test/run-pass/thinlto/msvc-imp-present.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:msvc-imp-present.rs +// compile-flags: -Z thinlto -C codegen-units=8 +// min-llvm-version: 4.0 +// no-prefer-dynamic + +// On MSVC we have a "hack" where we emit symbols that look like `_imp_$name` +// for all exported statics. This is done because we apply `dllimport` to all +// imported constants and this allows everything to actually link correctly. +// +// The ThinLTO passes aggressively remove symbols if they can, and this test +// asserts that the ThinLTO passes don't remove these compiler-generated +// `_imp_*` symbols. The external library that we link in here is compiled with +// ThinLTO and multiple codegen units and has a few exported constants. Note +// that we also namely compile the library as both a dylib and an rlib, but we +// link the rlib to ensure that we assert those generated symbols exist. + +extern crate msvc_imp_present as bar; + +fn main() { + println!("{}", bar::A); +} diff --git a/src/test/run-pass/thinlto/thin-lto-inlines.rs b/src/test/run-pass/thinlto/thin-lto-inlines.rs new file mode 100644 index 0000000000000..7a71dd2bc5125 --- /dev/null +++ b/src/test/run-pass/thinlto/thin-lto-inlines.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=8 -O +// min-llvm-version 4.0 +// ignore-emscripten can't inspect instructions on emscripten + +// We want to assert here that ThinLTO will inline across codegen units. There's +// not really a great way to do that in general so we sort of hack around it by +// praying two functions go into separate codegen units and then assuming that +// if inlining *doesn't* happen the first byte of the functions will differ. + +pub fn foo() -> u32 { + bar::bar() +} + +mod bar { + pub fn bar() -> u32 { + 3 + } +} + +fn main() { + println!("{} {}", foo(), bar::bar()); + + unsafe { + let foo = foo as usize as *const u8; + let bar = bar::bar as usize as *const u8; + + assert_eq!(*foo, *bar); + } +} diff --git a/src/test/run-pass/thinlto/thin-lto-inlines2.rs b/src/test/run-pass/thinlto/thin-lto-inlines2.rs new file mode 100644 index 0000000000000..0e8ad08a5f680 --- /dev/null +++ b/src/test/run-pass/thinlto/thin-lto-inlines2.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=8 -O -C lto +// aux-build:thin-lto-inlines-aux.rs +// min-llvm-version 4.0 +// no-prefer-dynamic +// ignore-emscripten can't inspect instructions on emscripten + +// We want to assert here that ThinLTO will inline across codegen units. There's +// not really a great way to do that in general so we sort of hack around it by +// praying two functions go into separate codegen units and then assuming that +// if inlining *doesn't* happen the first byte of the functions will differ. + +extern crate thin_lto_inlines_aux as bar; + +pub fn foo() -> u32 { + bar::bar() +} + +fn main() { + println!("{} {}", foo(), bar::bar()); + + unsafe { + let foo = foo as usize as *const u8; + let bar = bar::bar as usize as *const u8; + + assert_eq!(*foo, *bar); + } +} + diff --git a/src/test/run-pass/try-wait.rs b/src/test/run-pass/try-wait.rs index be87b7b3c87e4..0ee2cb9238c91 100644 --- a/src/test/run-pass/try-wait.rs +++ b/src/test/run-pass/try-wait.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes #![feature(process_try_wait)] diff --git a/src/test/run-pass/u128-as-f32.rs b/src/test/run-pass/u128-as-f32.rs new file mode 100644 index 0000000000000..117e520155fd1 --- /dev/null +++ b/src/test/run-pass/u128-as-f32.rs @@ -0,0 +1,58 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-emscripten u128 not supported + +#![feature(test, i128, i128_type)] +#![deny(overflowing_literals)] +extern crate test; + +use std::f32; +use std::u128; +use test::black_box; + +macro_rules! test { + ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ + { + const X: $src_ty = $val; + const Y: $dest_ty = X as $dest_ty; + assert_eq!(Y, $expected, + "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + } + // black_box disables constant evaluation to test run-time conversions: + assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, + "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); + }); +} + +pub fn main() { + // nextDown(f32::MAX) = 2^128 - 2 * 2^104 + const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; + + // f32::MAX - 0.5 ULP and smaller should be rounded down + test!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + test!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); + test!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); + // numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX + test!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); + test!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); + test!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); + test!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); + test!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); + // f32::MAX + 0.5 ULP and greater should be rounded to infinity + test!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); + test!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); + test!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); + + // u128->f64 should not be affected by the u128->f32 checks + test!(0xffffff80000000000000000000000000, u128 -> f64, + 340282356779733661637539395458142568448.0); + test!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); +} diff --git a/src/test/run-pass/u128.rs b/src/test/run-pass/u128.rs index b16f6c7b6af55..bf506a7125001 100644 --- a/src/test/run-pass/u128.rs +++ b/src/test/run-pass/u128.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten u128 not supported #![feature(i128_type, test)] diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-arg-types-from-expected-bound.rs similarity index 100% rename from src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs rename to src/test/run-pass/unboxed-closures-infer-arg-types-from-expected-bound.rs diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs b/src/test/run-pass/unboxed-closures-infer-arg-types-from-expected-object-type.rs similarity index 100% rename from src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs rename to src/test/run-pass/unboxed-closures-infer-arg-types-from-expected-object-type.rs diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs similarity index 100% rename from src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs rename to src/test/run-pass/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs diff --git a/src/test/run-pass/unboxed-closures-move-from-projection-issue-30046.rs b/src/test/run-pass/unboxed-closures-move-from-projection-issue-30046.rs new file mode 100644 index 0000000000000..d902ebc9dc9d2 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-move-from-projection-issue-30046.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +fn foo(f: F) + where F: FnOnce() +{ +} + +fn main() { + // Test that this closure is inferred to `FnOnce` + // because it moves from `y.as.0`: + let x = Some(vec![1, 2, 3]); + foo(|| { + match x { + Some(y) => { } + None => { } + } + }); + + // Test that this closure is inferred to `FnOnce` + // because it moves from `y.0`: + let y = (vec![1, 2, 3], 0); + foo(|| { + let x = y.0; + }); +} diff --git a/src/test/run-pass/union/union-c-interop.rs b/src/test/run-pass/union/union-c-interop.rs index b3df7d658b15f..dd16bf2e4a38e 100644 --- a/src/test/run-pass/union/union-c-interop.rs +++ b/src/test/run-pass/union/union-c-interop.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #[derive(Clone, Copy)] #[repr(C)] struct LARGE_INTEGER_U { diff --git a/src/test/run-pass/use-nested-groups.rs b/src/test/run-pass/use-nested-groups.rs new file mode 100644 index 0000000000000..74a82afd462b8 --- /dev/null +++ b/src/test/run-pass/use-nested-groups.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(use_nested_groups)] + +mod a { + pub enum B {} + + pub mod d { + pub enum E {} + pub enum F {} + + pub mod g { + pub enum H {} + pub enum I {} + } + } +} + +use a::{B, d::{self, *, g::H}}; + +fn main() { + let _: B; + let _: E; + let _: F; + let _: H; + let _: d::g::I; +} diff --git a/src/test/run-pass/variadic-ffi.rs b/src/test/run-pass/variadic-ffi.rs index ec6261febc54d..2198ead106bb7 100644 --- a/src/test/run-pass/variadic-ffi.rs +++ b/src/test/run-pass/variadic-ffi.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + #[link(name = "rust_test_helpers", kind = "static")] extern { fn rust_interesting_average(_: u64, ...) -> f64; diff --git a/src/test/run-pass/vec-macro-no-std.rs b/src/test/run-pass/vec-macro-no-std.rs index f21027afac318..56ff9cb24774b 100644 --- a/src/test/run-pass/vec-macro-no-std.rs +++ b/src/test/run-pass/vec-macro-no-std.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten missing rust_begin_unwind +// ignore-emscripten no no_std executables #![feature(lang_items, start, libc, alloc)] #![no_std] diff --git a/src/test/run-pass/wait-forked-but-failed-child.rs b/src/test/run-pass/wait-forked-but-failed-child.rs index 1d1c83cf12a15..744f2989bcfca 100644 --- a/src/test/run-pass/wait-forked-but-failed-child.rs +++ b/src/test/run-pass/wait-forked-but-failed-child.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-emscripten +// ignore-emscripten no processes #![feature(libc)] diff --git a/src/test/run-pass/weird-exprs.rs b/src/test/run-pass/weird-exprs.rs index 64fd9e0a7721b..ecb62b1888dd2 100644 --- a/src/test/run-pass/weird-exprs.rs +++ b/src/test/run-pass/weird-exprs.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z borrowck=compare use std::cell::Cell; use std::mem::swap; diff --git a/src/test/run-pass/x86stdcall.rs b/src/test/run-pass/x86stdcall.rs index 106bf8ce7dfa4..e2e64ddfa3158 100644 --- a/src/test/run-pass/x86stdcall.rs +++ b/src/test/run-pass/x86stdcall.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-wasm32-bare no libc to test ffi with + // GetLastError doesn't seem to work with stack switching #[cfg(windows)] diff --git a/src/test/rustdoc/auxiliary/external-cross-doc.md b/src/test/rustdoc/auxiliary/external-cross-doc.md new file mode 100644 index 0000000000000..8b4e6edc69997 --- /dev/null +++ b/src/test/rustdoc/auxiliary/external-cross-doc.md @@ -0,0 +1,4 @@ +# Cross-crate imported docs + +This file is to make sure `#[doc(include="file.md")]` works when you re-export an item with included +docs. diff --git a/src/test/rustdoc/auxiliary/external-cross.rs b/src/test/rustdoc/auxiliary/external-cross.rs new file mode 100644 index 0000000000000..cb14fec7abe7a --- /dev/null +++ b/src/test/rustdoc/auxiliary/external-cross.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(external_doc)] + +#[doc(include="external-cross-doc.md")] +pub struct NeedMoreDocs; diff --git a/src/test/rustdoc/auxiliary/external-doc.md b/src/test/rustdoc/auxiliary/external-doc.md new file mode 100644 index 0000000000000..38478c1635a17 --- /dev/null +++ b/src/test/rustdoc/auxiliary/external-doc.md @@ -0,0 +1,3 @@ +# External Docs + +This file is here to test the `#[doc(include="file")]` attribute. diff --git a/src/test/rustdoc/auxiliary/issue-19190-3.rs b/src/test/rustdoc/auxiliary/issue-19190-3.rs index 2c9271202a650..4462164343128 100644 --- a/src/test/rustdoc/auxiliary/issue-19190-3.rs +++ b/src/test/rustdoc/auxiliary/issue-19190-3.rs @@ -15,8 +15,8 @@ use std::ops::Deref; pub struct Foo; impl Deref for Foo { - type Target = i32; - fn deref(&self) -> &i32 { loop {} } + type Target = String; + fn deref(&self) -> &String { loop {} } } pub struct Bar; diff --git a/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs b/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs index 52bd386ba595b..4fd55bd482cd9 100644 --- a/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs +++ b/src/test/rustdoc/auxiliary/rustdoc-default-impl.rs @@ -16,6 +16,7 @@ pub mod bar { pub trait Bar {} + #[allow(auto_impl)] impl Bar for .. {} pub trait Foo { diff --git a/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs b/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs index 6e8f80c8f5f9f..d886778278dfd 100644 --- a/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs +++ b/src/test/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs @@ -12,4 +12,5 @@ pub trait AnOibit {} +#[allow(auto_impl)] impl AnOibit for .. {} diff --git a/src/test/rustdoc/codeblock-title.rs b/src/test/rustdoc/codeblock-title.rs index accefd6b65f28..d6cd5a24d915f 100644 --- a/src/test/rustdoc/codeblock-title.rs +++ b/src/test/rustdoc/codeblock-title.rs @@ -12,8 +12,8 @@ // ignore-tidy-linelength -// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This code doesn't compile so be extra careful!" -// @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "Be careful when using this code, it's not being tested!" +// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This example deliberately fails to compile" +// @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "This example is not tested" /// foo /// diff --git a/src/test/rustdoc/crate-version.rs b/src/test/rustdoc/crate-version.rs new file mode 100644 index 0000000000000..07ab5ceedfa02 --- /dev/null +++ b/src/test/rustdoc/crate-version.rs @@ -0,0 +1,13 @@ +// 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. + +// compile-flags: --crate-version=1.3.37 -Z unstable-options + +// @has 'crate_version/index.html' '//div[@class="block version"]/p' 'Version 1.3.37' diff --git a/src/test/rustdoc/doc-spotlight.rs b/src/test/rustdoc/doc-spotlight.rs new file mode 100644 index 0000000000000..a570aa2d3984f --- /dev/null +++ b/src/test/rustdoc/doc-spotlight.rs @@ -0,0 +1,46 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(doc_spotlight)] + +pub struct Wrapper { + inner: T, +} + +impl SomeTrait for Wrapper {} + +#[doc(spotlight)] +pub trait SomeTrait { + // @has doc_spotlight/trait.SomeTrait.html + // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' + fn wrap_me(self) -> Wrapper where Self: Sized { + Wrapper { + inner: self, + } + } +} + +pub struct SomeStruct; +impl SomeTrait for SomeStruct {} + +impl SomeStruct { + // @has doc_spotlight/struct.SomeStruct.html + // @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' + // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' + pub fn new() -> SomeStruct { + SomeStruct + } +} + +// @has doc_spotlight/fn.bare_fn.html +// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' +pub fn bare_fn() -> SomeStruct { + SomeStruct +} diff --git a/src/test/rustdoc/double-quote-escape.rs b/src/test/rustdoc/double-quote-escape.rs new file mode 100644 index 0000000000000..46e2ca8c760b5 --- /dev/null +++ b/src/test/rustdoc/double-quote-escape.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// ignore-tidy-linelength + +pub trait Foo { + fn foo() {} +} + +pub struct Bar; + +// @has foo/struct.Bar.html +// @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E"]' 'Foo' +impl Foo for Bar {} diff --git a/src/test/rustdoc/empty-mod-private.rs b/src/test/rustdoc/empty-mod-private.rs index 6b86af62a663a..6c6af19be88f9 100644 --- a/src/test/rustdoc/empty-mod-private.rs +++ b/src/test/rustdoc/empty-mod-private.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports +// compile-flags: --document-private-items // @has 'empty_mod_private/index.html' '//a[@href="foo/index.html"]' 'foo' // @has 'empty_mod_private/sidebar-items.js' 'foo' diff --git a/src/test/rustdoc/external-cross.rs b/src/test/rustdoc/external-cross.rs new file mode 100644 index 0000000000000..f4a59cee32dd9 --- /dev/null +++ b/src/test/rustdoc/external-cross.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:external-cross.rs +// ignore-cross-compile + +#![crate_name="host"] + +extern crate external_cross; + +// @has host/struct.NeedMoreDocs.html +// @has - '//h1' 'Cross-crate imported docs' +pub use external_cross::NeedMoreDocs; diff --git a/src/test/rustdoc/external-doc.rs b/src/test/rustdoc/external-doc.rs new file mode 100644 index 0000000000000..07e784a6ccfaf --- /dev/null +++ b/src/test/rustdoc/external-doc.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(external_doc)] + +// @has external_doc/struct.CanHasDocs.html +// @has - '//h1' 'External Docs' +// @has - '//h2' 'Inline Docs' +#[doc(include = "auxiliary/external-doc.md")] +/// ## Inline Docs +pub struct CanHasDocs; diff --git a/src/test/rustdoc/fn-pointer-arg-name.rs b/src/test/rustdoc/fn-pointer-arg-name.rs new file mode 100644 index 0000000000000..af87f1b466974 --- /dev/null +++ b/src/test/rustdoc/fn-pointer-arg-name.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// @has foo/fn.f.html +// @has - '//*[@class="rust fn"]' 'pub fn f(callback: fn(len: usize, foo: u32))' +pub fn f(callback: fn(len: usize, foo: u32)) {} diff --git a/src/test/rustdoc/foreigntype-reexport.rs b/src/test/rustdoc/foreigntype-reexport.rs new file mode 100644 index 0000000000000..714222de529ee --- /dev/null +++ b/src/test/rustdoc/foreigntype-reexport.rs @@ -0,0 +1,66 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(extern_types)] + +mod sub { + extern { + /// Another extern type. + pub type C2; + pub fn f2(); + pub static K: usize; + } +} + +pub mod sub2 { + extern { + // @has foreigntype_reexport/sub2/foreigntype.C.html + pub type C; + // @has foreigntype_reexport/sub2/fn.f.html + pub fn f(); + // @has foreigntype_reexport/sub2/static.K3.html + pub static K3: usize; + } +} + +mod sub3 { + extern { + pub type C4; + pub fn f4(); + pub static K4: usize; + type X4; + } +} + +// @has foreigntype_reexport/foreigntype.C2.html +// @has foreigntype_reexport/fn.f2.html +// @has foreigntype_reexport/static.K2.html +// @has foreigntype_reexport/index.html '//a[@class="foreigntype"]' 'C2' +// @has foreigntype_reexport/index.html '//a[@class="fn"]' 'f2' +// @has foreigntype_reexport/index.html '//a[@class="static"]' 'K2' +pub use self::sub::{C2, f2, K as K2}; + +// @has foreigntype_reexport/index.html '//a[@class="foreigntype"]' 'C' +// @has foreigntype_reexport/index.html '//a[@class="fn"]' 'f' +// @has foreigntype_reexport/index.html '//a[@class="static"]' 'K3' +// @has foreigntype_reexport/index.html '//code' 'pub use self::sub2::C as C3;' +// @has foreigntype_reexport/index.html '//code' 'pub use self::sub2::f as f3;' +// @has foreigntype_reexport/index.html '//code' 'pub use self::sub2::K3;' +pub use self::sub2::{C as C3, f as f3, K3}; + +// @has foreigntype_reexport/foreigntype.C4.html +// @has foreigntype_reexport/fn.f4.html +// @has foreigntype_reexport/static.K4.html +// @!has foreigntype_reexport/foreigntype.X4.html +// @has foreigntype_reexport/index.html '//a[@class="foreigntype"]' 'C4' +// @has foreigntype_reexport/index.html '//a[@class="fn"]' 'f4' +// @has foreigntype_reexport/index.html '//a[@class="static"]' 'K4' +// @!has foreigntype_reexport/index.html '//a[@class="foreigntype"]' 'X4' +pub use self::sub3::*; diff --git a/src/test/rustdoc/foreigntype.rs b/src/test/rustdoc/foreigntype.rs new file mode 100644 index 0000000000000..06447ffaa753d --- /dev/null +++ b/src/test/rustdoc/foreigntype.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(extern_types)] + +extern { + // @has foreigntype/foreigntype.ExtType.html + pub type ExtType; +} + +impl ExtType { + // @has - '//a[@class="fnname"]' 'do_something' + pub fn do_something(&self) {} +} + +pub trait Trait {} + +// @has foreigntype/trait.Trait.html '//a[@class="foreigntype"]' 'ExtType' +impl Trait for ExtType {} + +// @has foreigntype/index.html '//a[@class="foreigntype"]' 'ExtType' diff --git a/src/test/rustdoc/impl-parts.rs b/src/test/rustdoc/impl-parts.rs index 48ef4b6be66de..f74f66ce72905 100644 --- a/src/test/rustdoc/impl-parts.rs +++ b/src/test/rustdoc/impl-parts.rs @@ -12,6 +12,7 @@ pub trait AnOibit {} +#[allow(auto_impl)] impl AnOibit for .. {} pub struct Foo { field: T } diff --git a/src/test/rustdoc/inline_cross/assoc-items.rs b/src/test/rustdoc/inline_cross/assoc-items.rs new file mode 100644 index 0000000000000..95d936883ffa9 --- /dev/null +++ b/src/test/rustdoc/inline_cross/assoc-items.rs @@ -0,0 +1,57 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:assoc-items.rs +// build-aux-docs +// ignore-cross-compile + +#![crate_name = "foo"] + +extern crate assoc_items; + +// @has foo/struct.MyStruct.html +// @!has - 'PrivateConst' +// @has - '//*[@id="associatedconstant.PublicConst"]' 'pub const PublicConst: u8' +// @has - '//*[@class="docblock"]' 'PublicConst: u8 = 123' +// @has - '//*[@class="docblock"]' 'docs for PublicConst' +// @!has - 'private_method' +// @has - '//*[@id="method.public_method"]' 'pub fn public_method()' +// @has - '//*[@class="docblock"]' 'docs for public_method' +// @has - '//*[@id="associatedconstant.ConstNoDefault"]' 'const ConstNoDefault: i16' +// @has - '//*[@class="docblock"]' 'ConstNoDefault: i16 = -123' +// @has - '//*[@class="docblock"]' 'dox for ConstNoDefault' +// @has - '//*[@id="associatedconstant.ConstWithDefault"]' 'const ConstWithDefault: u16' +// @has - '//*[@class="docblock"]' 'ConstWithDefault: u16 = 12345' +// @has - '//*[@class="docblock"]' 'docs for ConstWithDefault' +// @has - '//*[@id="associatedtype.TypeNoDefault"]' 'type TypeNoDefault = i32' +// @has - '//*[@class="docblock"]' 'dox for TypeNoDefault' +// @has - '//*[@id="associatedtype.TypeWithDefault"]' 'type TypeWithDefault = u32' +// @has - '//*[@class="docblock"]' 'docs for TypeWithDefault' +// @has - '//*[@id="method.method_no_default"]' 'fn method_no_default()' +// @has - '//*[@class="docblock"]' 'dox for method_no_default' +// @has - '//*[@id="method.method_with_default"]' 'fn method_with_default()' +// @has - '//*[@class="docblock"]' 'docs for method_with_default' +pub use assoc_items::MyStruct; + +// @has foo/trait.MyTrait.html +// @has - '//*[@id="associatedconstant.ConstNoDefault"]' 'const ConstNoDefault: i16' +// @has - '//*[@class="docblock"]' 'docs for ConstNoDefault' +// @has - '//*[@id="associatedconstant.ConstWithDefault"]' 'const ConstWithDefault: u16' +// @has - '//*[@class="docblock"]' 'ConstWithDefault: u16 = 12345' +// @has - '//*[@class="docblock"]' 'docs for ConstWithDefault' +// @has - '//*[@id="associatedtype.TypeNoDefault"]' 'type TypeNoDefault' +// @has - '//*[@class="docblock"]' 'docs for TypeNoDefault' +// @has - '//*[@id="associatedtype.TypeWithDefault"]' 'type TypeWithDefault = u32' +// @has - '//*[@class="docblock"]' 'docs for TypeWithDefault' +// @has - '//*[@id="tymethod.method_no_default"]' 'fn method_no_default()' +// @has - '//*[@class="docblock"]' 'docs for method_no_default' +// @has - '//*[@id="method.method_with_default"]' 'fn method_with_default()' +// @has - '//*[@class="docblock"]' 'docs for method_with_default' +pub use assoc_items::MyTrait; diff --git a/src/test/rustdoc/inline_cross/auxiliary/assoc-items.rs b/src/test/rustdoc/inline_cross/auxiliary/assoc-items.rs new file mode 100644 index 0000000000000..e955526900e99 --- /dev/null +++ b/src/test/rustdoc/inline_cross/auxiliary/assoc-items.rs @@ -0,0 +1,48 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_type_defaults)] + +pub struct MyStruct; + +impl MyStruct { + /// docs for PrivateConst + const PrivateConst: i8 = -123; + /// docs for PublicConst + pub const PublicConst: u8 = 123; + /// docs for private_method + fn private_method() {} + /// docs for public_method + pub fn public_method() {} +} + +pub trait MyTrait { + /// docs for ConstNoDefault + const ConstNoDefault: i16; + /// docs for ConstWithDefault + const ConstWithDefault: u16 = 12345; + /// docs for TypeNoDefault + type TypeNoDefault; + /// docs for TypeWithDefault + type TypeWithDefault = u32; + /// docs for method_no_default + fn method_no_default(); + /// docs for method_with_default + fn method_with_default() {} +} + +impl MyTrait for MyStruct { + /// dox for ConstNoDefault + const ConstNoDefault: i16 = -12345; + /// dox for TypeNoDefault + type TypeNoDefault = i32; + /// dox for method_no_default + fn method_no_default() {} +} diff --git a/src/test/rustdoc/inline_local/glob-private.rs b/src/test/rustdoc/inline_local/glob-private.rs index b5e256dfdce94..ecc754f012ed1 100644 --- a/src/test/rustdoc/inline_local/glob-private.rs +++ b/src/test/rustdoc/inline_local/glob-private.rs @@ -34,16 +34,19 @@ pub use mod1::*; // @has foo/struct.Mod2Public.html // @!has foo/struct.Mod2Private.html +// @has-dir foo/mod1 // @!has foo/mod1/index.html // @has foo/mod1/struct.Mod1Public.html // @!has foo/mod1/struct.Mod1Private.html // @!has foo/mod1/struct.Mod2Public.html // @!has foo/mod1/struct.Mod2Private.html +// @has-dir foo/mod1/mod2 // @!has foo/mod1/mod2/index.html // @has foo/mod1/mod2/struct.Mod2Public.html // @!has foo/mod1/mod2/struct.Mod2Private.html +// @!has-dir foo/mod2 // @!has foo/mod2/index.html // @!has foo/mod2/struct.Mod2Public.html // @!has foo/mod2/struct.Mod2Private.html diff --git a/src/test/rustdoc/issue-15347.rs b/src/test/rustdoc/issue-15347.rs index 266a30891941d..c50df6edd484a 100644 --- a/src/test/rustdoc/issue-15347.rs +++ b/src/test/rustdoc/issue-15347.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:--no-defaults --passes collapse-docs --passes unindent-comments +// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments // @has issue_15347/fn.foo.html #[doc(hidden)] diff --git a/src/test/rustdoc/issue-19190-2.rs b/src/test/rustdoc/issue-19190-2.rs index 8835e18f1c5ce..5688c5cba0bbe 100644 --- a/src/test/rustdoc/issue-19190-2.rs +++ b/src/test/rustdoc/issue-19190-2.rs @@ -13,10 +13,10 @@ use std::ops::Deref; pub struct Bar; impl Deref for Bar { - type Target = i32; - fn deref(&self) -> &i32 { loop {} } + type Target = String; + fn deref(&self) -> &String { loop {} } } // @has issue_19190_2/struct.Bar.html -// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32' -// @!has - '//*[@id="method.min_value"]' 'fn min_value() -> i32' +// @!has - '//*[@id="method.new"]' 'fn new() -> String' +// @has - '//*[@id="method.as_str"]' 'fn as_str(&self) -> &str' diff --git a/src/test/rustdoc/issue-19190-3.rs b/src/test/rustdoc/issue-19190-3.rs index 64c396b29f27e..be2e15dffc0e7 100644 --- a/src/test/rustdoc/issue-19190-3.rs +++ b/src/test/rustdoc/issue-19190-3.rs @@ -17,8 +17,8 @@ use std::ops::Deref; use issue_19190_3::Baz; // @has issue_19190_3/struct.Foo.html -// @has - '//*[@id="method.count_ones"]' 'fn count_ones(self) -> u32' -// @!has - '//*[@id="method.min_value"]' 'fn min_value() -> i32' +// @has - '//*[@id="method.as_str"]' 'fn as_str(&self) -> &str' +// @!has - '//*[@id="method.new"]' 'fn new() -> String' pub use issue_19190_3::Foo; // @has issue_19190_3/struct.Bar.html diff --git a/src/test/rustdoc/issue-42760.rs b/src/test/rustdoc/issue-42760.rs new file mode 100644 index 0000000000000..f5f5c4f97fd02 --- /dev/null +++ b/src/test/rustdoc/issue-42760.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// @has issue_42760/struct.NonGen.html +// @has - '//h1' 'Example' + +/// Item docs. +/// +#[doc="Hello there!"] +/// +/// # Example +/// +/// ```rust +/// // some code here +/// ``` +pub struct NonGen; diff --git a/src/test/rustdoc/issue-43869.rs b/src/test/rustdoc/issue-43869.rs index 2d18e4be53286..554c71500cc8b 100644 --- a/src/test/rustdoc/issue-43869.rs +++ b/src/test/rustdoc/issue-43869.rs @@ -26,7 +26,58 @@ pub fn j() -> impl Iterator + Clone { Some(1u8).into_iter() } +pub fn k() -> [impl Clone; 2] { + [123u32, 456u32] +} + +pub fn l() -> (impl Clone, impl Default) { + (789u32, -123i32) +} + +pub fn m() -> &'static impl Clone { + &1u8 +} + +pub fn n() -> *const impl Clone { + &1u8 +} + +pub fn o() -> &'static [impl Clone] { + b":)" +} + +// issue #44731 +pub fn test_44731_0() -> Box> { + Box::new(g()) +} + +pub fn test_44731_1() -> Result, ()> { + Ok(Box::new(j())) +} + +// NOTE these involve Fn sugar, where impl Trait is disallowed for now, see issue #45994 +// +//pub fn test_44731_2() -> Box { +// Box::new(|_: u32| {}) +//} +// +//pub fn test_44731_3() -> Box impl Clone> { +// Box::new(|| 0u32) +//} + +pub fn test_44731_4() -> Box> { + Box::new(g()) +} + // @has issue_43869/fn.g.html // @has issue_43869/fn.h.html // @has issue_43869/fn.i.html // @has issue_43869/fn.j.html +// @has issue_43869/fn.k.html +// @has issue_43869/fn.l.html +// @has issue_43869/fn.m.html +// @has issue_43869/fn.n.html +// @has issue_43869/fn.o.html +// @has issue_43869/fn.test_44731_0.html +// @has issue_43869/fn.test_44731_1.html +// @has issue_43869/fn.test_44731_4.html diff --git a/src/test/rustdoc/issue-43893.rs b/src/test/rustdoc/issue-43893.rs new file mode 100644 index 0000000000000..96bd9d7dc3cc5 --- /dev/null +++ b/src/test/rustdoc/issue-43893.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-cross-compile + +#![crate_name = "foo"] + +pub trait SomeTrait {} +pub struct SomeStruct; + +// @has foo/trait.SomeTrait.html '//a/@href' '../src/foo/issue-43893.rs.html#19' +impl SomeTrait for usize {} + +// @has foo/trait.SomeTrait.html '//a/@href' '../src/foo/issue-43893.rs.html#22-24' +impl SomeTrait for SomeStruct { + // deliberately multi-line impl +} diff --git a/src/test/rustdoc/issue-45584.rs b/src/test/rustdoc/issue-45584.rs new file mode 100644 index 0000000000000..6d6ae3dc94a21 --- /dev/null +++ b/src/test/rustdoc/issue-45584.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +pub trait Bar {} + +// @has 'foo/struct.Foo1.html' +pub struct Foo1; +// @count - '//*[@class="impl"]' 1 +// @has - '//*[@class="impl"]' "impl Bar for Foo1" +impl Bar for Foo1 {} + +// @has 'foo/struct.Foo2.html' +pub struct Foo2; +// @count - '//*[@class="impl"]' 1 +// @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8" +impl Bar<&'static Foo2, Foo2> for u8 {} diff --git a/src/test/rustdoc/issue-46271.rs b/src/test/rustdoc/issue-46271.rs new file mode 100644 index 0000000000000..cc3be08c5688d --- /dev/null +++ b/src/test/rustdoc/issue-46271.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// hopefully this doesn't cause an ICE + +pub fn foo() { + extern crate std; +} diff --git a/src/libstd/os/nacl/mod.rs b/src/test/rustdoc/issue-46377.rs similarity index 76% rename from src/libstd/os/nacl/mod.rs rename to src/test/rustdoc/issue-46377.rs index 7dfa2eabe3e17..db8c7660df1d8 100644 --- a/src/libstd/os/nacl/mod.rs +++ b/src/test/rustdoc/issue-46377.rs @@ -8,9 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Nacl-specific definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] - -pub mod raw; -pub mod fs; +// @has 'issue_46377/index.html' '//*[@class="docblock-short"]' 'Check out this struct!' +/// # Check out this struct! +pub struct SomeStruct; diff --git a/src/test/rustdoc/issue-46380-2.rs b/src/test/rustdoc/issue-46380-2.rs new file mode 100644 index 0000000000000..22408d3522a4b --- /dev/null +++ b/src/test/rustdoc/issue-46380-2.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait PublicTrait {} + +// @has issue_46380_2/struct.PublicStruct.html +pub struct PublicStruct; + +// @!has - '//*[@class="impl"]' 'impl PublicTrait for PublicStruct' +impl PublicTrait for PublicStruct {} + +struct PrivateStruct; diff --git a/src/test/run-pass/issue-21410.rs b/src/test/rustdoc/issue-46380.rs similarity index 80% rename from src/test/run-pass/issue-21410.rs rename to src/test/rustdoc/issue-46380.rs index bc525ba54c354..85f29ec4b02d1 100644 --- a/src/test/run-pass/issue-21410.rs +++ b/src/test/rustdoc/issue-46380.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn g(_: F) where F: FnOnce(Option) {} +// compile-flags: --document-private-items -fn main() { - g(|_| { }); -} +// @has issue_46380/struct.Hidden.html +#[doc(hidden)] +pub struct Hidden; diff --git a/src/test/rustdoc/method-list.rs b/src/test/rustdoc/method-list.rs new file mode 100644 index 0000000000000..b7112885e888b --- /dev/null +++ b/src/test/rustdoc/method-list.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +#![crate_name = "foo"] + +// @has foo/struct.Foo.html +// @has - '//*[@class="sidebar-links"]/a' 'super_long_name' +// @has - '//*[@class="sidebar-links"]/a' 'Disp' +pub struct Foo(usize); + +impl Foo { + pub fn super_long_name() {} +} + +pub trait Disp { + fn disp_trait_method(); +} + +impl Disp for Foo { + fn disp_trait_method() {} +} diff --git a/src/test/rustdoc/negative-impl-sidebar.rs b/src/test/rustdoc/negative-impl-sidebar.rs new file mode 100644 index 0000000000000..dc27b26241d59 --- /dev/null +++ b/src/test/rustdoc/negative-impl-sidebar.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(optin_builtin_traits)] +#![crate_name = "foo"] + +pub struct Foo; + +// @has foo/struct.Foo.html +// @has - '//*[@class="sidebar-title"][@href="#implementations"]' 'Trait Implementations' +// @has - '//*[@class="sidebar-links"]/a' '!Sync' +impl !Sync for Foo {} diff --git a/src/test/rustdoc/playground-arg.rs b/src/test/rustdoc/playground-arg.rs index f0d55ef6e9fcd..478477dea61ed 100644 --- a/src/test/rustdoc/playground-arg.rs +++ b/src/test/rustdoc/playground-arg.rs @@ -21,4 +21,4 @@ pub fn dummy() {} // ensure that `extern crate foo;` was inserted into code snips automatically: -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=extern%20crate%20foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D"]' "Run" diff --git a/src/test/rustdoc/playground.rs b/src/test/rustdoc/playground.rs index 9eb8dec51a7f2..8e193efaf8504 100644 --- a/src/test/rustdoc/playground.rs +++ b/src/test/rustdoc/playground.rs @@ -34,6 +34,6 @@ //! } //! ``` -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=fn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A"]' "Run" -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=fn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D"]' "Run" -// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A&version=nightly"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A&version=nightly"]' "Run" diff --git a/src/test/rustdoc/pub-method.rs b/src/test/rustdoc/pub-method.rs index 5998734e4a20c..24d566e082eea 100644 --- a/src/test/rustdoc/pub-method.rs +++ b/src/test/rustdoc/pub-method.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports +// compile-flags: --document-private-items #![crate_name = "foo"] diff --git a/src/test/rustdoc/sidebar-items.rs b/src/test/rustdoc/sidebar-items.rs new file mode 100644 index 0000000000000..9be40441e9d4f --- /dev/null +++ b/src/test/rustdoc/sidebar-items.rs @@ -0,0 +1,59 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// @has foo/trait.Foo.html +// @has - '//*[@class="sidebar-title"][@href="#required-methods"]' 'Required Methods' +// @has - '//*[@class="sidebar-links"]/a' 'bar' +// @has - '//*[@class="sidebar-title"][@href="#provided-methods"]' 'Provided Methods' +// @has - '//*[@class="sidebar-links"]/a' 'foo' +// @has - '//*[@class="sidebar-title"][@href="#associated-const"]' 'Associated Constants' +// @has - '//*[@class="sidebar-links"]/a' 'BAR' +// @has - '//*[@class="sidebar-title"][@href="#associated-types"]' 'Associated Types' +// @has - '//*[@class="sidebar-links"]/a' 'Output' +pub trait Foo { + const BAR: u32 = 0; + type Output: ?Sized; + + fn foo() {} + fn bar() -> Self::Output; +} + +// @has foo/struct.Bar.html +// @has - '//*[@class="sidebar-title"][@href="#fields"]' 'Fields' +// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f"]' 'f' +// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.u"]' 'u' +// @!has - '//*[@class="sidebar-links"]/a' 'w' +pub struct Bar { + pub f: u32, + pub u: u32, + w: u32, +} + +// @has foo/enum.En.html +// @has - '//*[@class="sidebar-title"][@href="#variants"]' 'Variants' +// @has - '//*[@class="sidebar-links"]/a' 'foo' +// @has - '//*[@class="sidebar-links"]/a' 'bar' +pub enum En { + foo, + bar, +} + +// @has foo/union.MyUnion.html +// @has - '//*[@class="sidebar-title"][@href="#fields"]' 'Fields' +// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f1"]' 'f1' +// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f2"]' 'f2' +// @!has - '//*[@class="sidebar-links"]/a' 'w' +pub union MyUnion { + pub f1: u32, + pub f2: f32, + w: u32, +} diff --git a/src/test/ui-fulldeps/auxiliary/lint_group_plugin_test.rs b/src/test/ui-fulldeps/auxiliary/lint_group_plugin_test.rs index 490aa0d469312..a0c72243d4821 100644 --- a/src/test/ui-fulldeps/auxiliary/lint_group_plugin_test.rs +++ b/src/test/ui-fulldeps/auxiliary/lint_group_plugin_test.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar)] #![feature(box_syntax, rustc_private)] +#![feature(macro_vis_matcher)] // Load rustc as a plugin to get macros #[macro_use] diff --git a/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs b/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs index 8647797270f9a..cbbfbd8060360 100644 --- a/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs +++ b/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs @@ -12,6 +12,7 @@ #![feature(plugin_registrar)] #![feature(box_syntax, rustc_private)] +#![feature(macro_vis_matcher)] extern crate syntax; diff --git a/src/test/ui-fulldeps/custom-derive/issue-36935.rs b/src/test/ui-fulldeps/custom-derive/issue-36935.rs index 2231c3c242285..4fd8763206733 100644 --- a/src/test/ui-fulldeps/custom-derive/issue-36935.rs +++ b/src/test/ui-fulldeps/custom-derive/issue-36935.rs @@ -15,7 +15,7 @@ #[macro_use] extern crate plugin; -#[derive(Foo, Bar)] +#[derive(Foo, Bar)] //~ ERROR proc-macro derive panicked struct Baz { a: i32, b: i32, diff --git a/src/test/ui-fulldeps/custom-derive/issue-36935.stderr b/src/test/ui-fulldeps/custom-derive/issue-36935.stderr index 46cc7a42b0429..55848c6553cca 100644 --- a/src/test/ui-fulldeps/custom-derive/issue-36935.stderr +++ b/src/test/ui-fulldeps/custom-derive/issue-36935.stderr @@ -1,7 +1,7 @@ error: proc-macro derive panicked --> $DIR/issue-36935.rs:18:15 | -18 | #[derive(Foo, Bar)] +18 | #[derive(Foo, Bar)] //~ ERROR proc-macro derive panicked | ^^^ | = help: message: lolnope diff --git a/src/test/ui-fulldeps/issue-44953/issue-44953.rs b/src/test/ui-fulldeps/issue-44953/issue-44953.rs new file mode 100644 index 0000000000000..de798e2cf0baa --- /dev/null +++ b/src/test/ui-fulldeps/issue-44953/issue-44953.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// + + +#![feature(proc_macro)] +#![allow(unused_macros)] + +#[macro_use] extern crate log; //~ ERROR use of unstable library feature + +pub fn main() { + info!("This is a log message."); +} diff --git a/src/test/ui-fulldeps/issue-44953/issue-44953.stderr b/src/test/ui-fulldeps/issue-44953/issue-44953.stderr new file mode 100644 index 0000000000000..e0e96ec3f4fca --- /dev/null +++ b/src/test/ui-fulldeps/issue-44953/issue-44953.stderr @@ -0,0 +1,19 @@ +error: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? (see issue #27812) + --> $DIR/issue-44953.rs:16:14 + | +16 | #[macro_use] extern crate log; //~ ERROR use of unstable library feature + | ^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(rustc_private)] to the crate attributes to enable + +error: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? (see issue #27812) + --> $DIR/issue-44953.rs:19:5 + | +19 | info!("This is a log message."); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(rustc_private)] to the crate attributes to enable + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/src/test/ui-fulldeps/proc-macro/auxiliary/three-equals.rs b/src/test/ui-fulldeps/proc-macro/auxiliary/three-equals.rs index 6fca32fece1d4..2381c61b87bd0 100644 --- a/src/test/ui-fulldeps/proc-macro/auxiliary/three-equals.rs +++ b/src/test/ui-fulldeps/proc-macro/auxiliary/three-equals.rs @@ -18,7 +18,7 @@ use proc_macro::{TokenStream, TokenNode, Span, Diagnostic}; fn parse(input: TokenStream) -> Result<(), Diagnostic> { let mut count = 0; - let mut last_span = Span::default(); + let mut last_span = Span::def_site(); for tree in input { let span = tree.span; if count >= 3 { @@ -37,7 +37,7 @@ fn parse(input: TokenStream) -> Result<(), Diagnostic> { } if count < 3 { - return Err(Span::default() + return Err(Span::def_site() .error(format!("found {} equal signs, need exactly 3", count)) .help("input must be: `===`")) } diff --git a/src/test/ui-fulldeps/proc-macro/three-equals.rs b/src/test/ui-fulldeps/proc-macro/three-equals.rs index 016e05c51f507..ef2d160529068 100644 --- a/src/test/ui-fulldeps/proc-macro/three-equals.rs +++ b/src/test/ui-fulldeps/proc-macro/three-equals.rs @@ -22,17 +22,17 @@ fn main() { three_equals!(===); // Need exactly three equals. - three_equals!(==); + three_equals!(==); //~ ERROR found 2 equal signs, need exactly 3 // Need exactly three equals. - three_equals!(=====); + three_equals!(=====); //~ ERROR expected EOF // Only equals accepted. - three_equals!(abc); + three_equals!(abc); //~ ERROR expected `=` // Only equals accepted. - three_equals!(!!); + three_equals!(!!); //~ ERROR expected `=` // Only three characters expected. - three_equals!(===a); + three_equals!(===a); //~ ERROR expected EOF } diff --git a/src/test/ui-fulldeps/proc-macro/three-equals.stderr b/src/test/ui-fulldeps/proc-macro/three-equals.stderr index 1afe0be280009..0ffaf16107872 100644 --- a/src/test/ui-fulldeps/proc-macro/three-equals.stderr +++ b/src/test/ui-fulldeps/proc-macro/three-equals.stderr @@ -1,7 +1,7 @@ error: found 2 equal signs, need exactly 3 --> $DIR/three-equals.rs:25:5 | -25 | three_equals!(==); +25 | three_equals!(==); //~ ERROR found 2 equal signs, need exactly 3 | ^^^^^^^^^^^^^^^^^^ | = help: input must be: `===` @@ -9,38 +9,38 @@ error: found 2 equal signs, need exactly 3 error: expected EOF, found `=`. --> $DIR/three-equals.rs:28:21 | -28 | three_equals!(=====); +28 | three_equals!(=====); //~ ERROR expected EOF | ^^ | note: last good input was here --> $DIR/three-equals.rs:28:21 | -28 | three_equals!(=====); +28 | three_equals!(=====); //~ ERROR expected EOF | ^^ = help: input must be: `===` error: expected `=`, found `abc`. --> $DIR/three-equals.rs:31:19 | -31 | three_equals!(abc); +31 | three_equals!(abc); //~ ERROR expected `=` | ^^^ error: expected `=`, found `!`. --> $DIR/three-equals.rs:34:19 | -34 | three_equals!(!!); +34 | three_equals!(!!); //~ ERROR expected `=` | ^ error: expected EOF, found `a`. --> $DIR/three-equals.rs:37:22 | -37 | three_equals!(===a); +37 | three_equals!(===a); //~ ERROR expected EOF | ^ | note: last good input was here --> $DIR/three-equals.rs:37:21 | -37 | three_equals!(===a); +37 | three_equals!(===a); //~ ERROR expected EOF | ^ = help: input must be: `===` diff --git a/src/test/ui-fulldeps/resolve-error.rs b/src/test/ui-fulldeps/resolve-error.rs index dfaa1d7a32e57..ae94a7f13e23e 100644 --- a/src/test/ui-fulldeps/resolve-error.rs +++ b/src/test/ui-fulldeps/resolve-error.rs @@ -35,29 +35,39 @@ macro_rules! attr_proc_mac { } #[derive(FooWithLongNan)] +//~^ ERROR cannot find struct Foo; #[attr_proc_macra] +//~^ ERROR cannot find struct Bar; #[FooWithLongNan] +//~^ ERROR cannot find struct Asdf; #[derive(Dlone)] +//~^ ERROR cannot find struct A; #[derive(Dlona)] +//~^ ERROR cannot find struct B; #[derive(attr_proc_macra)] +//~^ ERROR cannot find struct C; fn main() { FooWithLongNama!(); + //~^ ERROR cannot find attr_proc_macra!(); + //~^ ERROR cannot find Dlona!(); + //~^ ERROR cannot find bang_proc_macrp!(); + //~^ ERROR cannot find } diff --git a/src/test/ui-fulldeps/resolve-error.stderr b/src/test/ui-fulldeps/resolve-error.stderr index 754f6bc4f1c1c..be7ebae70adf5 100644 --- a/src/test/ui-fulldeps/resolve-error.stderr +++ b/src/test/ui-fulldeps/resolve-error.stderr @@ -5,57 +5,57 @@ error: cannot find derive macro `FooWithLongNan` in this scope | ^^^^^^^^^^^^^^ help: try: `FooWithLongName` error: cannot find attribute macro `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:40:3 + --> $DIR/resolve-error.rs:41:3 | -40 | #[attr_proc_macra] +41 | #[attr_proc_macra] | ^^^^^^^^^^^^^^^ help: try: `attr_proc_macro` error: cannot find attribute macro `FooWithLongNan` in this scope - --> $DIR/resolve-error.rs:43:3 + --> $DIR/resolve-error.rs:45:3 | -43 | #[FooWithLongNan] +45 | #[FooWithLongNan] | ^^^^^^^^^^^^^^ error: cannot find derive macro `Dlone` in this scope - --> $DIR/resolve-error.rs:46:10 + --> $DIR/resolve-error.rs:49:10 | -46 | #[derive(Dlone)] +49 | #[derive(Dlone)] | ^^^^^ help: try: `Clone` error: cannot find derive macro `Dlona` in this scope - --> $DIR/resolve-error.rs:49:10 + --> $DIR/resolve-error.rs:53:10 | -49 | #[derive(Dlona)] +53 | #[derive(Dlona)] | ^^^^^ help: try: `Clona` error: cannot find derive macro `attr_proc_macra` in this scope - --> $DIR/resolve-error.rs:52:10 + --> $DIR/resolve-error.rs:57:10 | -52 | #[derive(attr_proc_macra)] +57 | #[derive(attr_proc_macra)] | ^^^^^^^^^^^^^^^ error: cannot find macro `FooWithLongNama!` in this scope - --> $DIR/resolve-error.rs:56:5 + --> $DIR/resolve-error.rs:62:5 | -56 | FooWithLongNama!(); +62 | FooWithLongNama!(); | ^^^^^^^^^^^^^^^ help: you could try the macro: `FooWithLongNam!` error: cannot find macro `attr_proc_macra!` in this scope - --> $DIR/resolve-error.rs:58:5 + --> $DIR/resolve-error.rs:65:5 | -58 | attr_proc_macra!(); +65 | attr_proc_macra!(); | ^^^^^^^^^^^^^^^ help: you could try the macro: `attr_proc_mac!` error: cannot find macro `Dlona!` in this scope - --> $DIR/resolve-error.rs:60:5 + --> $DIR/resolve-error.rs:68:5 | -60 | Dlona!(); +68 | Dlona!(); | ^^^^^ error: cannot find macro `bang_proc_macrp!` in this scope - --> $DIR/resolve-error.rs:62:5 + --> $DIR/resolve-error.rs:71:5 | -62 | bang_proc_macrp!(); +71 | bang_proc_macrp!(); | ^^^^^^^^^^^^^^^ help: you could try the macro: `bang_proc_macro!` error: aborting due to 10 previous errors diff --git a/src/test/ui/anonymous-higher-ranked-lifetime.rs b/src/test/ui/anonymous-higher-ranked-lifetime.rs new file mode 100644 index 0000000000000..295e3d1a73521 --- /dev/null +++ b/src/test/ui/anonymous-higher-ranked-lifetime.rs @@ -0,0 +1,40 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + f1(|_: (), _: ()| {}); //~ ERROR type mismatch + f2(|_: (), _: ()| {}); //~ ERROR type mismatch + f3(|_: (), _: ()| {}); //~ ERROR type mismatch + f4(|_: (), _: ()| {}); //~ ERROR type mismatch + f5(|_: (), _: ()| {}); //~ ERROR type mismatch + g1(|_: (), _: ()| {}); //~ ERROR type mismatch + g2(|_: (), _: ()| {}); //~ ERROR type mismatch + g3(|_: (), _: ()| {}); //~ ERROR type mismatch + g4(|_: (), _: ()| {}); //~ ERROR type mismatch + h1(|_: (), _: (), _: (), _: ()| {}); //~ ERROR type mismatch + h2(|_: (), _: (), _: (), _: ()| {}); //~ ERROR type mismatch +} + +// Basic +fn f1(_: F) where F: Fn(&(), &()) {} +fn f2(_: F) where F: for<'a> Fn(&'a (), &()) {} +fn f3<'a, F>(_: F) where F: Fn(&'a (), &()) {} +fn f4(_: F) where F: for<'r> Fn(&(), &'r ()) {} +fn f5(_: F) where F: for<'r> Fn(&'r (), &'r ()) {} + +// Nested +fn g1(_: F) where F: Fn(&(), Box) {} +fn g2(_: F) where F: Fn(&(), fn(&())) {} +fn g3(_: F) where F: for<'s> Fn(&'s (), Box) {} +fn g4(_: F) where F: Fn(&(), for<'r> fn(&'r ())) {} + +// Mixed +fn h1(_: F) where F: Fn(&(), Box, &(), fn(&(), &())) {} +fn h2(_: F) where F: for<'t0> Fn(&(), Box, &'t0 (), fn(&(), &())) {} diff --git a/src/test/ui/anonymous-higher-ranked-lifetime.stderr b/src/test/ui/anonymous-higher-ranked-lifetime.stderr new file mode 100644 index 0000000000000..6f684f13e6f66 --- /dev/null +++ b/src/test/ui/anonymous-higher-ranked-lifetime.stderr @@ -0,0 +1,112 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:12:5 + | +12 | f1(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'r, 's> fn(&'r (), &'s ()) -> _` + | + = note: required by `f1` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:13:5 + | +13 | f2(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'a, 'r> fn(&'a (), &'r ()) -> _` + | + = note: required by `f2` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:14:5 + | +14 | f3(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'r> fn(&(), &'r ()) -> _` + | + = note: required by `f3` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:15:5 + | +15 | f4(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'s, 'r> fn(&'s (), &'r ()) -> _` + | + = note: required by `f4` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:16:5 + | +16 | f5(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'r> fn(&'r (), &'r ()) -> _` + | + = note: required by `f5` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:17:5 + | +17 | g1(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'r> fn(&'r (), std::boxed::Box std::ops::Fn(&'s ()) + 'static>) -> _` + | + = note: required by `g1` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:18:5 + | +18 | g2(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'r> fn(&'r (), for<'s> fn(&'s ())) -> _` + | + = note: required by `g2` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:19:5 + | +19 | g3(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'s> fn(&'s (), std::boxed::Box std::ops::Fn(&'r ()) + 'static>) -> _` + | + = note: required by `g3` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:20:5 + | +20 | g4(|_: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ----------------- found signature of `fn((), ()) -> _` + | | + | expected signature of `for<'s> fn(&'s (), for<'r> fn(&'r ())) -> _` + | + = note: required by `g4` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:21:5 + | +21 | h1(|_: (), _: (), _: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ------------------------------- found signature of `fn((), (), (), ()) -> _` + | | + | expected signature of `for<'r, 's> fn(&'r (), std::boxed::Box std::ops::Fn(&'t0 ()) + 'static>, &'s (), for<'t0, 't1> fn(&'t0 (), &'t1 ())) -> _` + | + = note: required by `h1` + +error[E0631]: type mismatch in closure arguments + --> $DIR/anonymous-higher-ranked-lifetime.rs:22:5 + | +22 | h2(|_: (), _: (), _: (), _: ()| {}); //~ ERROR type mismatch + | ^^ ------------------------------- found signature of `fn((), (), (), ()) -> _` + | | + | expected signature of `for<'r, 't0> fn(&'r (), std::boxed::Box std::ops::Fn(&'s ()) + 'static>, &'t0 (), for<'s, 't1> fn(&'s (), &'t1 ())) -> _` + | + = note: required by `h2` + +error: aborting due to 11 previous errors + diff --git a/src/test/ui/block-result/issue-11714.rs b/src/test/ui/block-result/issue-11714.rs index 192f78e41cb43..255eb771694cc 100644 --- a/src/test/ui/block-result/issue-11714.rs +++ b/src/test/ui/block-result/issue-11714.rs @@ -11,7 +11,7 @@ fn blah() -> i32 { //~ ERROR mismatched types 1 - ; //~ HELP consider removing this semicolon: + ; //~ HELP consider removing this semicolon } fn main() { } diff --git a/src/test/ui/block-result/issue-11714.stderr b/src/test/ui/block-result/issue-11714.stderr index 376834beab0da..4daf40e6172f6 100644 --- a/src/test/ui/block-result/issue-11714.stderr +++ b/src/test/ui/block-result/issue-11714.stderr @@ -5,7 +5,7 @@ error[E0308]: mismatched types | __________________^ 12 | | 1 13 | | -14 | | ; //~ HELP consider removing this semicolon: +14 | | ; //~ HELP consider removing this semicolon | | - help: consider removing this semicolon 15 | | } | |_^ expected i32, found () diff --git a/src/test/ui/block-result/issue-3563.rs b/src/test/ui/block-result/issue-3563.rs index 7928c04b9df87..31a363a6b863d 100644 --- a/src/test/ui/block-result/issue-3563.rs +++ b/src/test/ui/block-result/issue-3563.rs @@ -12,7 +12,6 @@ trait A { fn a(&self) { || self.b() //~^ ERROR no method named `b` found for type `&Self` in the current scope - //~| ERROR mismatched types } } fn main() {} diff --git a/src/test/ui/block-result/issue-3563.stderr b/src/test/ui/block-result/issue-3563.stderr index e3f0df6fb5f1a..c3d5f21b0a51e 100644 --- a/src/test/ui/block-result/issue-3563.stderr +++ b/src/test/ui/block-result/issue-3563.stderr @@ -6,16 +6,5 @@ error[E0599]: no method named `b` found for type `&Self` in the current scope | = help: did you mean `a`? -error[E0308]: mismatched types - --> $DIR/issue-3563.rs:13:9 - | -12 | fn a(&self) { - | - possibly return type missing here? -13 | || self.b() - | ^^^^^^^^^^^ expected (), found closure - | - = note: expected type `()` - found type `[closure@$DIR/issue-3563.rs:13:9: 13:20 self:_]` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/block-result/unexpected-return-on-unit.rs b/src/test/ui/block-result/unexpected-return-on-unit.rs index 291b7a16f141b..3cf76365c77b1 100644 --- a/src/test/ui/block-result/unexpected-return-on-unit.rs +++ b/src/test/ui/block-result/unexpected-return-on-unit.rs @@ -16,7 +16,7 @@ fn foo() -> usize { } fn bar() { - foo() + foo() //~ ERROR mismatched types } fn main() { diff --git a/src/test/ui/block-result/unexpected-return-on-unit.stderr b/src/test/ui/block-result/unexpected-return-on-unit.stderr index f052883815246..3881bb4625801 100644 --- a/src/test/ui/block-result/unexpected-return-on-unit.stderr +++ b/src/test/ui/block-result/unexpected-return-on-unit.stderr @@ -1,14 +1,14 @@ error[E0308]: mismatched types --> $DIR/unexpected-return-on-unit.rs:19:5 | -19 | foo() +19 | foo() //~ ERROR mismatched types | ^^^^^ expected (), found usize | = note: expected type `()` found type `usize` help: try adding a semicolon | -19 | foo(); +19 | foo(); //~ ERROR mismatched types | ^ help: try adding a return type | diff --git a/src/test/ui/borrowck/borrowck-closures-two-mut.rs b/src/test/ui/borrowck/borrowck-closures-two-mut.rs new file mode 100644 index 0000000000000..b6946154fa00a --- /dev/null +++ b/src/test/ui/borrowck/borrowck-closures-two-mut.rs @@ -0,0 +1,67 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that two closures cannot simultaneously have mutable +// access to the variable, whether that mutable access be used +// for direct assignment or for taking mutable ref. Issue #6801. + +// compile-flags: -Z borrowck=compare + +#![feature(box_syntax)] + +fn to_fn_mut(f: F) -> F { f } + +fn a() { + let mut x = 3; + let c1 = to_fn_mut(|| x = 4); + let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once + //~| ERROR cannot borrow `x` as mutable more than once +} + +fn set(x: &mut isize) { + *x = 4; +} + +fn b() { + let mut x = 3; + let c1 = to_fn_mut(|| set(&mut x)); + let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + //~| ERROR cannot borrow `x` as mutable more than once +} + +fn c() { + let mut x = 3; + let c1 = to_fn_mut(|| x = 5); + let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + //~| ERROR cannot borrow `x` as mutable more than once +} + +fn d() { + let mut x = 3; + let c1 = to_fn_mut(|| x = 5); + let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure) + //~^ ERROR cannot borrow `x` as mutable more than once + //~| ERROR cannot borrow `x` as mutable more than once +} + +fn g() { + struct Foo { + f: Box + } + + let mut x: Box<_> = box Foo { f: box 3 }; + let c1 = to_fn_mut(|| set(&mut *x.f)); + let c2 = to_fn_mut(|| set(&mut *x.f)); + //~^ ERROR cannot borrow `x` as mutable more than once + //~| ERROR cannot borrow `x` as mutable more than once +} + +fn main() { +} diff --git a/src/test/ui/borrowck/borrowck-closures-two-mut.stderr b/src/test/ui/borrowck/borrowck-closures-two-mut.stderr new file mode 100644 index 0000000000000..0ec744f4a0781 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-closures-two-mut.stderr @@ -0,0 +1,152 @@ +error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast) + --> $DIR/borrowck-closures-two-mut.rs:24:24 + | +23 | let c1 = to_fn_mut(|| x = 4); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +24 | let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +25 | //~| ERROR cannot borrow `x` as mutable more than once +26 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast) + --> $DIR/borrowck-closures-two-mut.rs:35:24 + | +34 | let c1 = to_fn_mut(|| set(&mut x)); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +35 | let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +36 | //~| ERROR cannot borrow `x` as mutable more than once +37 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast) + --> $DIR/borrowck-closures-two-mut.rs:42:24 + | +41 | let c1 = to_fn_mut(|| x = 5); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +42 | let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +43 | //~| ERROR cannot borrow `x` as mutable more than once +44 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast) + --> $DIR/borrowck-closures-two-mut.rs:49:24 + | +48 | let c1 = to_fn_mut(|| x = 5); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +49 | let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure) + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +... +52 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast) + --> $DIR/borrowck-closures-two-mut.rs:61:24 + | +60 | let c1 = to_fn_mut(|| set(&mut *x.f)); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +61 | let c2 = to_fn_mut(|| set(&mut *x.f)); + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +... +64 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir) + --> $DIR/borrowck-closures-two-mut.rs:24:24 + | +23 | let c1 = to_fn_mut(|| x = 4); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +24 | let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +25 | //~| ERROR cannot borrow `x` as mutable more than once +26 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir) + --> $DIR/borrowck-closures-two-mut.rs:35:24 + | +34 | let c1 = to_fn_mut(|| set(&mut x)); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +35 | let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +36 | //~| ERROR cannot borrow `x` as mutable more than once +37 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir) + --> $DIR/borrowck-closures-two-mut.rs:42:24 + | +41 | let c1 = to_fn_mut(|| x = 5); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +42 | let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +43 | //~| ERROR cannot borrow `x` as mutable more than once +44 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir) + --> $DIR/borrowck-closures-two-mut.rs:49:24 + | +48 | let c1 = to_fn_mut(|| x = 5); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +49 | let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure) + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +... +52 | } + | - first borrow ends here + +error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir) + --> $DIR/borrowck-closures-two-mut.rs:61:24 + | +60 | let c1 = to_fn_mut(|| set(&mut *x.f)); + | -- - previous borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +61 | let c2 = to_fn_mut(|| set(&mut *x.f)); + | ^^ - borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +... +64 | } + | - first borrow ends here + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/borrowck/borrowck-in-static.rs b/src/test/ui/borrowck/borrowck-in-static.rs index 9244c12347d95..b30234811acb2 100644 --- a/src/test/ui/borrowck/borrowck-in-static.rs +++ b/src/test/ui/borrowck/borrowck-in-static.rs @@ -11,8 +11,9 @@ // check that borrowck looks inside consts/statics static FN : &'static (Fn() -> (BoxBox>) + Sync) = &|| { - let x = Box::new(0); //~ NOTE moved + let x = Box::new(0); //~ NOTE captured outer variable Box::new(|| x) //~ ERROR cannot move out of captured outer variable + //~^ NOTE cannot move out of captured outer variable }; fn main() { diff --git a/src/test/ui/borrowck/borrowck-in-static.stderr b/src/test/ui/borrowck/borrowck-in-static.stderr index 6083a82b1b6de..92ca36e117e60 100644 --- a/src/test/ui/borrowck/borrowck-in-static.stderr +++ b/src/test/ui/borrowck/borrowck-in-static.stderr @@ -1,7 +1,7 @@ error[E0507]: cannot move out of captured outer variable in an `Fn` closure --> $DIR/borrowck-in-static.rs:15:17 | -14 | let x = Box::new(0); //~ NOTE moved +14 | let x = Box::new(0); //~ NOTE captured outer variable | - captured outer variable 15 | Box::new(|| x) //~ ERROR cannot move out of captured outer variable | ^ cannot move out of captured outer variable in an `Fn` closure diff --git a/src/test/ui/borrowck/borrowck-reinit.rs b/src/test/ui/borrowck/borrowck-reinit.rs new file mode 100644 index 0000000000000..2e07577c5eade --- /dev/null +++ b/src/test/ui/borrowck/borrowck-reinit.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z borrowck=compare + +fn main() { + let mut x = Box::new(0); + let _u = x; // error shouldn't note this move + x = Box::new(1); + drop(x); + let _ = (1,x); //~ ERROR use of moved value: `x` (Ast) + //~^ ERROR use of moved value: `x` (Mir) +} diff --git a/src/test/ui/borrowck/borrowck-reinit.stderr b/src/test/ui/borrowck/borrowck-reinit.stderr new file mode 100644 index 0000000000000..9f08bd198223e --- /dev/null +++ b/src/test/ui/borrowck/borrowck-reinit.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `x` (Ast) + --> $DIR/borrowck-reinit.rs:18:16 + | +17 | drop(x); + | - value moved here +18 | let _ = (1,x); //~ ERROR use of moved value: `x` (Ast) + | ^ value used here after move + | + = note: move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `x` (Mir) + --> $DIR/borrowck-reinit.rs:18:16 + | +17 | drop(x); + | - value moved here +18 | let _ = (1,x); //~ ERROR use of moved value: `x` (Ast) + | ^ value used here after move + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.rs b/src/test/ui/borrowck/mut-borrow-in-loop.rs index addda42775399..31b50d8e531c0 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.rs +++ b/src/test/ui/borrowck/mut-borrow-in-loop.rs @@ -17,20 +17,20 @@ struct FuncWrapper<'a, T : 'a> { impl<'a, T : 'a> FuncWrapper<'a, T> { fn in_loop(self, arg : &'a mut T) { loop { - (self.func)(arg) + (self.func)(arg) //~ ERROR cannot borrow } } fn in_while(self, arg : &'a mut T) { while true { - (self.func)(arg) + (self.func)(arg) //~ ERROR cannot borrow } } fn in_for(self, arg : &'a mut T) { let v : Vec<()> = vec![]; for _ in v.iter() { - (self.func)(arg) + (self.func)(arg) //~ ERROR cannot borrow } } } diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.stderr index a34d524d28f2e..2b614561d8268 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.stderr +++ b/src/test/ui/borrowck/mut-borrow-in-loop.stderr @@ -1,7 +1,7 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:20:25 | -20 | (self.func)(arg) +20 | (self.func)(arg) //~ ERROR cannot borrow | ^^^ mutable borrow starts here in previous iteration of loop 21 | } 22 | } @@ -10,7 +10,7 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:26:25 | -26 | (self.func)(arg) +26 | (self.func)(arg) //~ ERROR cannot borrow | ^^^ mutable borrow starts here in previous iteration of loop 27 | } 28 | } @@ -19,7 +19,7 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:33:25 | -33 | (self.func)(arg) +33 | (self.func)(arg) //~ ERROR cannot borrow | ^^^ mutable borrow starts here in previous iteration of loop 34 | } 35 | } diff --git a/src/test/ui/borrowck/mut-borrow-outside-loop.rs b/src/test/ui/borrowck/mut-borrow-outside-loop.rs index 97092b7f9d759..a1ab41bab337d 100644 --- a/src/test/ui/borrowck/mut-borrow-outside-loop.rs +++ b/src/test/ui/borrowck/mut-borrow-outside-loop.rs @@ -14,13 +14,13 @@ fn main() { let mut void = (); let first = &mut void; - let second = &mut void; + let second = &mut void; //~ ERROR cannot borrow loop { let mut inner_void = (); let inner_first = &mut inner_void; - let inner_second = &mut inner_void; + let inner_second = &mut inner_void; //~ ERROR cannot borrow } } diff --git a/src/test/ui/borrowck/mut-borrow-outside-loop.stderr b/src/test/ui/borrowck/mut-borrow-outside-loop.stderr index 02b32dc363ae7..716edd21982e9 100644 --- a/src/test/ui/borrowck/mut-borrow-outside-loop.stderr +++ b/src/test/ui/borrowck/mut-borrow-outside-loop.stderr @@ -3,7 +3,7 @@ error[E0499]: cannot borrow `void` as mutable more than once at a time | 16 | let first = &mut void; | ---- first mutable borrow occurs here -17 | let second = &mut void; +17 | let second = &mut void; //~ ERROR cannot borrow | ^^^^ second mutable borrow occurs here ... 25 | } @@ -14,7 +14,7 @@ error[E0499]: cannot borrow `inner_void` as mutable more than once at a time | 22 | let inner_first = &mut inner_void; | ---------- first mutable borrow occurs here -23 | let inner_second = &mut inner_void; +23 | let inner_second = &mut inner_void; //~ ERROR cannot borrow | ^^^^^^^^^^ second mutable borrow occurs here 24 | } | - first borrow ends here diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs index 9c89c26de006a..788d68caa524e 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs @@ -16,9 +16,10 @@ fn call(f: F) where F : Fn() { } fn main() { - let y = vec![format!("World")]; //~ NOTE moved + let y = vec![format!("World")]; //~ NOTE captured outer variable call(|| { y.into_iter(); //~^ ERROR cannot move out of captured outer variable in an `Fn` closure + //~| NOTE cannot move out of }); } diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index dbfcb2e0c2f95..895ce1ba31803 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -1,7 +1,7 @@ error[E0507]: cannot move out of captured outer variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:21:9 | -19 | let y = vec![format!("World")]; //~ NOTE moved +19 | let y = vec![format!("World")]; //~ NOTE captured outer variable | - captured outer variable 20 | call(|| { 21 | y.into_iter(); diff --git a/src/test/ui/cast-to-unsized-trait-object-suggestion.rs b/src/test/ui/cast-to-unsized-trait-object-suggestion.rs index c793454798275..010b5a1b1067e 100644 --- a/src/test/ui/cast-to-unsized-trait-object-suggestion.rs +++ b/src/test/ui/cast-to-unsized-trait-object-suggestion.rs @@ -9,6 +9,6 @@ // except according to those terms. fn main() { - &1 as Send; - Box::new(1) as Send; + &1 as Send; //~ ERROR cast to unsized + Box::new(1) as Send; //~ ERROR cast to unsized } diff --git a/src/test/ui/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast-to-unsized-trait-object-suggestion.stderr index 4d4eb7b4ecfdb..55d41848b17a9 100644 --- a/src/test/ui/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast-to-unsized-trait-object-suggestion.stderr @@ -1,7 +1,7 @@ error[E0620]: cast to unsized type: `&{integer}` as `std::marker::Send` --> $DIR/cast-to-unsized-trait-object-suggestion.rs:12:5 | -12 | &1 as Send; +12 | &1 as Send; //~ ERROR cast to unsized | ^^^^^^---- | | | help: try casting to a reference instead: `&Send` @@ -9,7 +9,7 @@ error[E0620]: cast to unsized type: `&{integer}` as `std::marker::Send` error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `std::marker::Send` --> $DIR/cast-to-unsized-trait-object-suggestion.rs:13:5 | -13 | Box::new(1) as Send; +13 | Box::new(1) as Send; //~ ERROR cast to unsized | ^^^^^^^^^^^^^^^---- | | | help: try casting to a `Box` instead: `Box` diff --git a/src/test/ui/check_match/issue-35609.rs b/src/test/ui/check_match/issue-35609.rs index 6497f69035dec..d52718b7bf41f 100644 --- a/src/test/ui/check_match/issue-35609.rs +++ b/src/test/ui/check_match/issue-35609.rs @@ -17,36 +17,36 @@ struct S(Enum, ()); struct Sd { x: Enum, y: () } fn main() { - match (A, ()) { + match (A, ()) { //~ ERROR non-exhaustive (A, _) => {} } - match (A, A) { + match (A, A) { //~ ERROR non-exhaustive (_, A) => {} } - match ((A, ()), ()) { + match ((A, ()), ()) { //~ ERROR non-exhaustive ((A, ()), _) => {} } - match ((A, ()), A) { + match ((A, ()), A) { //~ ERROR non-exhaustive ((A, ()), _) => {} } - match ((A, ()), ()) { + match ((A, ()), ()) { //~ ERROR non-exhaustive ((A, _), _) => {} } - match S(A, ()) { + match S(A, ()) { //~ ERROR non-exhaustive S(A, _) => {} } - match (Sd { x: A, y: () }) { + match (Sd { x: A, y: () }) { //~ ERROR non-exhaustive Sd { x: A, y: _ } => {} } - match Some(A) { + match Some(A) { //~ ERROR non-exhaustive Some(A) => (), None => () } diff --git a/src/test/ui/check_match/issue-35609.stderr b/src/test/ui/check_match/issue-35609.stderr index 0aafe3f17b3d0..1fc1d05636e91 100644 --- a/src/test/ui/check_match/issue-35609.stderr +++ b/src/test/ui/check_match/issue-35609.stderr @@ -1,49 +1,49 @@ error[E0004]: non-exhaustive patterns: `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered --> $DIR/issue-35609.rs:20:11 | -20 | match (A, ()) { +20 | match (A, ()) { //~ ERROR non-exhaustive | ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered error[E0004]: non-exhaustive patterns: `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered --> $DIR/issue-35609.rs:24:11 | -24 | match (A, A) { +24 | match (A, A) { //~ ERROR non-exhaustive | ^^^^^^ patterns `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:28:11 | -28 | match ((A, ()), ()) { +28 | match ((A, ()), ()) { //~ ERROR non-exhaustive | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:32:11 | -32 | match ((A, ()), A) { +32 | match ((A, ()), A) { //~ ERROR non-exhaustive | ^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:36:11 | -36 | match ((A, ()), ()) { +36 | match ((A, ()), ()) { //~ ERROR non-exhaustive | ^^^^^^^^^^^^^ patterns `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered error[E0004]: non-exhaustive patterns: `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered --> $DIR/issue-35609.rs:41:11 | -41 | match S(A, ()) { +41 | match S(A, ()) { //~ ERROR non-exhaustive | ^^^^^^^^ patterns `S(B, _)`, `S(C, _)`, `S(D, _)` and 2 more not covered error[E0004]: non-exhaustive patterns: `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered --> $DIR/issue-35609.rs:45:11 | -45 | match (Sd { x: A, y: () }) { +45 | match (Sd { x: A, y: () }) { //~ ERROR non-exhaustive | ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: B, .. }`, `Sd { x: C, .. }`, `Sd { x: D, .. }` and 2 more not covered error[E0004]: non-exhaustive patterns: `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered --> $DIR/issue-35609.rs:49:11 | -49 | match Some(A) { +49 | match Some(A) { //~ ERROR non-exhaustive | ^^^^^^^ patterns `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered error: aborting due to 8 previous errors diff --git a/src/test/ui/closure_context/issue-26046-fn-mut.rs b/src/test/ui/closure_context/issue-26046-fn-mut.rs index 5ed7ace5437d3..3b179a475e794 100644 --- a/src/test/ui/closure_context/issue-26046-fn-mut.rs +++ b/src/test/ui/closure_context/issue-26046-fn-mut.rs @@ -11,7 +11,7 @@ fn foo() -> Box { let num = 5; - let closure = || { + let closure = || { //~ ERROR expected a closure that num += 1; }; diff --git a/src/test/ui/closure_context/issue-26046-fn-mut.stderr b/src/test/ui/closure_context/issue-26046-fn-mut.stderr index 42fc2909dfb5a..82c83da4daec7 100644 --- a/src/test/ui/closure_context/issue-26046-fn-mut.stderr +++ b/src/test/ui/closure_context/issue-26046-fn-mut.stderr @@ -1,7 +1,7 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut` --> $DIR/issue-26046-fn-mut.rs:14:19 | -14 | let closure = || { +14 | let closure = || { //~ ERROR expected a closure that | ___________________^ 15 | | num += 1; 16 | | }; diff --git a/src/test/ui/closure_context/issue-26046-fn-once.rs b/src/test/ui/closure_context/issue-26046-fn-once.rs index de06de530c6c0..cf15985ee83ea 100644 --- a/src/test/ui/closure_context/issue-26046-fn-once.rs +++ b/src/test/ui/closure_context/issue-26046-fn-once.rs @@ -11,7 +11,7 @@ fn get_closure() -> Box Vec> { let vec = vec![1u8, 2u8]; - let closure = move || { + let closure = move || { //~ ERROR expected a closure vec }; diff --git a/src/test/ui/closure_context/issue-26046-fn-once.stderr b/src/test/ui/closure_context/issue-26046-fn-once.stderr index 7bfe72d3d6c9c..0bc84872dde5f 100644 --- a/src/test/ui/closure_context/issue-26046-fn-once.stderr +++ b/src/test/ui/closure_context/issue-26046-fn-once.stderr @@ -1,7 +1,7 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` --> $DIR/issue-26046-fn-once.rs:14:19 | -14 | let closure = move || { +14 | let closure = move || { //~ ERROR expected a closure | ___________________^ 15 | | vec 16 | | }; diff --git a/src/test/ui/closure_context/issue-42065.rs b/src/test/ui/closure_context/issue-42065.rs index 409964082f2b6..276c6a941b299 100644 --- a/src/test/ui/closure_context/issue-42065.rs +++ b/src/test/ui/closure_context/issue-42065.rs @@ -14,12 +14,13 @@ fn main() { let dict: HashMap = HashMap::new(); let debug_dump_dict = || { for (key, value) in dict { + //~^ NOTE closure cannot be invoked more than once println!("{:?} - {:?}", key, value); } }; debug_dump_dict(); + //~^ NOTE: value moved here debug_dump_dict(); //~^ ERROR use of moved value: `debug_dump_dict` - //~| NOTE closure cannot be invoked more than once because it moves the - //~| variable `dict` out of its environment + //~| NOTE value used here after move } diff --git a/src/test/ui/closure_context/issue-42065.stderr b/src/test/ui/closure_context/issue-42065.stderr index c195940ade6fa..b31322f6d168d 100644 --- a/src/test/ui/closure_context/issue-42065.stderr +++ b/src/test/ui/closure_context/issue-42065.stderr @@ -1,9 +1,10 @@ error[E0382]: use of moved value: `debug_dump_dict` - --> $DIR/issue-42065.rs:21:5 + --> $DIR/issue-42065.rs:23:5 | -20 | debug_dump_dict(); - | --------------- value moved here 21 | debug_dump_dict(); + | --------------- value moved here +22 | //~^ NOTE: value moved here +23 | debug_dump_dict(); | ^^^^^^^^^^^^^^^ value used here after move | note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment diff --git a/src/test/ui/codemap_tests/bad-format-args.stderr b/src/test/ui/codemap_tests/bad-format-args.stderr index 87255dfe77476..9d6ef54cb979f 100644 --- a/src/test/ui/codemap_tests/bad-format-args.stderr +++ b/src/test/ui/codemap_tests/bad-format-args.stderr @@ -4,7 +4,7 @@ error: requires at least a format string argument 12 | format!(); | ^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: expected token: `,` --> $DIR/bad-format-args.rs:13:5 @@ -12,7 +12,7 @@ error: expected token: `,` 13 | format!("" 1); | ^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: expected token: `,` --> $DIR/bad-format-args.rs:14:5 @@ -20,7 +20,7 @@ error: expected token: `,` 14 | format!("", 1 1); | ^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.rs b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.rs index a72ad0351e33b..532d173011d7f 100644 --- a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.rs +++ b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.rs @@ -11,6 +11,6 @@ #![allow(dead_code)] trait C {} -impl C { fn f() {} } +impl C { fn f() {} } //~ ERROR duplicate impl C { fn f() {} } fn main() { } diff --git a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr index 7f1ab929c6fc2..a7d52301476c9 100644 --- a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr +++ b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr @@ -1,7 +1,7 @@ error[E0592]: duplicate definitions with name `f` --> $DIR/coherence-overlapping-inherent-impl-trait.rs:14:10 | -14 | impl C { fn f() {} } +14 | impl C { fn f() {} } //~ ERROR duplicate | ^^^^^^^^^ duplicate definitions for `f` 15 | impl C { fn f() {} } | --------- other definition for `f` diff --git a/src/test/ui/codemap_tests/empty_span.rs b/src/test/ui/codemap_tests/empty_span.rs index 2cf3b66dd77c8..8e0395e3c5033 100644 --- a/src/test/ui/codemap_tests/empty_span.rs +++ b/src/test/ui/codemap_tests/empty_span.rs @@ -14,5 +14,5 @@ fn main() { impl !Sync for Foo {} - unsafe impl Send for &'static Foo { } + unsafe impl Send for &'static Foo { } //~ ERROR cross-crate traits with a default impl } diff --git a/src/test/ui/codemap_tests/empty_span.stderr b/src/test/ui/codemap_tests/empty_span.stderr index b33dee6b4a472..3474803b00dd1 100644 --- a/src/test/ui/codemap_tests/empty_span.stderr +++ b/src/test/ui/codemap_tests/empty_span.stderr @@ -1,7 +1,7 @@ error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static main::Foo` --> $DIR/empty_span.rs:17:5 | -17 | unsafe impl Send for &'static Foo { } +17 | unsafe impl Send for &'static Foo { } //~ ERROR cross-crate traits with a default impl | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.rs b/src/test/ui/codemap_tests/huge_multispan_highlight.rs index 5a058d483915a..cf593eab85329 100644 --- a/src/test/ui/codemap_tests/huge_multispan_highlight.rs +++ b/src/test/ui/codemap_tests/huge_multispan_highlight.rs @@ -97,5 +97,5 @@ fn main() { - let y = &mut x; + let y = &mut x; //~ ERROR cannot borrow } diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.stderr b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr index 914db98c78446..bc333bde93c6b 100644 --- a/src/test/ui/codemap_tests/huge_multispan_highlight.stderr +++ b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr @@ -4,7 +4,7 @@ error[E0596]: cannot borrow immutable local variable `x` as mutable 12 | let x = "foo"; | - consider changing this to `mut x` ... -100 | let y = &mut x; +100 | let y = &mut x; //~ ERROR cannot borrow | ^ cannot borrow mutably error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/issue-11715.rs b/src/test/ui/codemap_tests/issue-11715.rs index ba1ce6abcd3d4..75581d3892719 100644 --- a/src/test/ui/codemap_tests/issue-11715.rs +++ b/src/test/ui/codemap_tests/issue-11715.rs @@ -97,5 +97,5 @@ fn main() { let mut x = "foo"; let y = &mut x; - let z = &mut x; + let z = &mut x; //~ ERROR cannot borrow } diff --git a/src/test/ui/codemap_tests/issue-11715.stderr b/src/test/ui/codemap_tests/issue-11715.stderr index 4947cbedd200e..bd8ffba00d44b 100644 --- a/src/test/ui/codemap_tests/issue-11715.stderr +++ b/src/test/ui/codemap_tests/issue-11715.stderr @@ -3,7 +3,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time | 99 | let y = &mut x; | - first mutable borrow occurs here -100 | let z = &mut x; +100 | let z = &mut x; //~ ERROR cannot borrow | ^ second mutable borrow occurs here 101 | } | - first borrow ends here diff --git a/src/test/ui/codemap_tests/issue-28308.stderr b/src/test/ui/codemap_tests/issue-28308.stderr index 7a1478104fdf1..c5afa5ec1a4f8 100644 --- a/src/test/ui/codemap_tests/issue-28308.stderr +++ b/src/test/ui/codemap_tests/issue-28308.stderr @@ -4,7 +4,7 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` 12 | assert!("foo"); | ^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/one_line.rs b/src/test/ui/codemap_tests/one_line.rs index e50091d560622..3fb35dd26acab 100644 --- a/src/test/ui/codemap_tests/one_line.rs +++ b/src/test/ui/codemap_tests/one_line.rs @@ -10,5 +10,5 @@ fn main() { let mut v = vec![Some("foo"), Some("bar")]; - v.push(v.pop().unwrap()); + v.push(v.pop().unwrap()); //~ ERROR cannot borrow } diff --git a/src/test/ui/codemap_tests/one_line.stderr b/src/test/ui/codemap_tests/one_line.stderr index a73575a8d57f1..cfe3d527136a3 100644 --- a/src/test/ui/codemap_tests/one_line.stderr +++ b/src/test/ui/codemap_tests/one_line.stderr @@ -1,7 +1,7 @@ error[E0499]: cannot borrow `v` as mutable more than once at a time --> $DIR/one_line.rs:13:12 | -13 | v.push(v.pop().unwrap()); +13 | v.push(v.pop().unwrap()); //~ ERROR cannot borrow | - ^ - first borrow ends here | | | | | second mutable borrow occurs here diff --git a/src/test/ui/codemap_tests/overlapping_inherent_impls.rs b/src/test/ui/codemap_tests/overlapping_inherent_impls.rs index a626b63b31ba0..18e77ddfd2c5b 100644 --- a/src/test/ui/codemap_tests/overlapping_inherent_impls.rs +++ b/src/test/ui/codemap_tests/overlapping_inherent_impls.rs @@ -16,7 +16,7 @@ struct Foo; impl Foo { - fn id() {} + fn id() {} //~ ERROR duplicate definitions } impl Foo { @@ -26,7 +26,7 @@ impl Foo { struct Bar(T); impl Bar { - fn bar(&self) {} + fn bar(&self) {} //~ ERROR duplicate definitions } impl Bar { @@ -36,7 +36,7 @@ impl Bar { struct Baz(T); impl Baz { - fn baz(&self) {} + fn baz(&self) {} //~ ERROR duplicate definitions } impl Baz> { diff --git a/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr b/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr index eaf42cde22f76..0ccdd20765176 100644 --- a/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr +++ b/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr @@ -1,7 +1,7 @@ error[E0592]: duplicate definitions with name `id` --> $DIR/overlapping_inherent_impls.rs:19:5 | -19 | fn id() {} +19 | fn id() {} //~ ERROR duplicate definitions | ^^^^^^^^^^ duplicate definitions for `id` ... 23 | fn id() {} @@ -10,7 +10,7 @@ error[E0592]: duplicate definitions with name `id` error[E0592]: duplicate definitions with name `bar` --> $DIR/overlapping_inherent_impls.rs:29:5 | -29 | fn bar(&self) {} +29 | fn bar(&self) {} //~ ERROR duplicate definitions | ^^^^^^^^^^^^^^^^ duplicate definitions for `bar` ... 33 | fn bar(&self) {} @@ -19,7 +19,7 @@ error[E0592]: duplicate definitions with name `bar` error[E0592]: duplicate definitions with name `baz` --> $DIR/overlapping_inherent_impls.rs:39:5 | -39 | fn baz(&self) {} +39 | fn baz(&self) {} //~ ERROR duplicate definitions | ^^^^^^^^^^^^^^^^ duplicate definitions for `baz` ... 43 | fn baz(&self) {} diff --git a/src/test/ui/codemap_tests/overlapping_spans.rs b/src/test/ui/codemap_tests/overlapping_spans.rs index 7c1f0db16dd09..467e90bd5a51b 100644 --- a/src/test/ui/codemap_tests/overlapping_spans.rs +++ b/src/test/ui/codemap_tests/overlapping_spans.rs @@ -18,6 +18,6 @@ impl Drop for S { fn main() { match (S {f:"foo".to_string()}) { - S {f:_s} => {} + S {f:_s} => {} //~ ERROR cannot move out } } diff --git a/src/test/ui/codemap_tests/overlapping_spans.stderr b/src/test/ui/codemap_tests/overlapping_spans.stderr index d32b18d670308..dc801b20dfb9d 100644 --- a/src/test/ui/codemap_tests/overlapping_spans.stderr +++ b/src/test/ui/codemap_tests/overlapping_spans.stderr @@ -1,7 +1,7 @@ error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/overlapping_spans.rs:21:9 | -21 | S {f:_s} => {} +21 | S {f:_s} => {} //~ ERROR cannot move out | ^^^^^--^ | | | | | hint: to prevent move, use `ref _s` or `ref mut _s` diff --git a/src/test/ui/codemap_tests/tab.rs b/src/test/ui/codemap_tests/tab.rs index 146ad2283c2fc..b8dedb0daf5f0 100644 --- a/src/test/ui/codemap_tests/tab.rs +++ b/src/test/ui/codemap_tests/tab.rs @@ -11,9 +11,9 @@ // ignore-tidy-tab fn main() { - bar; + bar; //~ ERROR cannot find value `bar` } fn foo() { - "bar boo" + "bar boo" //~ ERROR mismatched types } diff --git a/src/test/ui/codemap_tests/tab.stderr b/src/test/ui/codemap_tests/tab.stderr index b3fa9b128c5eb..41ab60f017f61 100644 --- a/src/test/ui/codemap_tests/tab.stderr +++ b/src/test/ui/codemap_tests/tab.stderr @@ -1,7 +1,7 @@ error[E0425]: cannot find value `bar` in this scope --> $DIR/tab.rs:14:2 | -14 | bar; +14 | bar; //~ ERROR cannot find value `bar` | ^^^ not found in this scope error[E0308]: mismatched types @@ -9,7 +9,7 @@ error[E0308]: mismatched types | 17 | fn foo() { | - help: try adding a return type: `-> &'static str ` -18 | "bar boo" +18 | "bar boo" //~ ERROR mismatched types | ^^^^^^^^^^^ expected (), found reference | = note: expected type `()` diff --git a/src/test/ui/codemap_tests/tab_2.rs b/src/test/ui/codemap_tests/tab_2.rs index d26d7974d85ad..b759a4abcae21 100644 --- a/src/test/ui/codemap_tests/tab_2.rs +++ b/src/test/ui/codemap_tests/tab_2.rs @@ -11,5 +11,5 @@ // ignore-tidy-tab fn main() { - """; + """; //~ ERROR unterminated double quote } diff --git a/src/test/ui/codemap_tests/tab_2.stderr b/src/test/ui/codemap_tests/tab_2.stderr index a2b3ca7e4d4fd..7f6b55e7eb8ea 100644 --- a/src/test/ui/codemap_tests/tab_2.stderr +++ b/src/test/ui/codemap_tests/tab_2.stderr @@ -1,7 +1,7 @@ error: unterminated double quote string --> $DIR/tab_2.rs:14:7 | -14 | """; +14 | """; //~ ERROR unterminated double quote | _______^ 15 | | } | |__^ diff --git a/src/test/ui/codemap_tests/tab_3.rs b/src/test/ui/codemap_tests/tab_3.rs index 9b3513d5116f2..ea235ed71a9e6 100644 --- a/src/test/ui/codemap_tests/tab_3.rs +++ b/src/test/ui/codemap_tests/tab_3.rs @@ -14,6 +14,6 @@ fn main() { let some_vec = vec!["hi"]; some_vec.into_iter(); { - println!("{:?}", some_vec); + println!("{:?}", some_vec); //~ ERROR use of moved } } diff --git a/src/test/ui/codemap_tests/tab_3.stderr b/src/test/ui/codemap_tests/tab_3.stderr index f19f5f20d23ec..278e590a36d13 100644 --- a/src/test/ui/codemap_tests/tab_3.stderr +++ b/src/test/ui/codemap_tests/tab_3.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value: `some_vec` 15 | some_vec.into_iter(); | -------- value moved here 16 | { -17 | println!("{:?}", some_vec); +17 | println!("{:?}", some_vec); //~ ERROR use of moved | ^^^^^^^^ value used here after move | = note: move occurs because `some_vec` has type `std::vec::Vec<&str>`, which does not implement the `Copy` trait diff --git a/src/test/ui/codemap_tests/two_files.rs b/src/test/ui/codemap_tests/two_files.rs index fe5eba93b2331..4c99ee67598bc 100644 --- a/src/test/ui/codemap_tests/two_files.rs +++ b/src/test/ui/codemap_tests/two_files.rs @@ -12,6 +12,6 @@ include!("two_files_data.rs"); struct Baz { } -impl Bar for Baz { } +impl Bar for Baz { } //~ ERROR expected trait, found type alias fn main() { } diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr index 9db43dde1ac7e..c0cfeef194da6 100644 --- a/src/test/ui/codemap_tests/two_files.stderr +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -1,7 +1,7 @@ error[E0404]: expected trait, found type alias `Bar` --> $DIR/two_files.rs:15:6 | -15 | impl Bar for Baz { } +15 | impl Bar for Baz { } //~ ERROR expected trait, found type alias | ^^^ type aliases cannot be used for traits error: cannot continue compilation due to previous error diff --git a/src/test/ui/codemap_tests/unicode.rs b/src/test/ui/codemap_tests/unicode.rs index b206722d4f368..ac22906a62330 100644 --- a/src/test/ui/codemap_tests/unicode.rs +++ b/src/test/ui/codemap_tests/unicode.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern "路濫狼á́́" fn foo() {} +extern "路濫狼á́́" fn foo() {} //~ ERROR invalid ABI fn main() { } diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr index 0828fd28b5878..4f3c79410df9c 100644 --- a/src/test/ui/codemap_tests/unicode.stderr +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -1,8 +1,8 @@ error: invalid ABI: expected one of [cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted], found `路濫狼á́́` --> $DIR/unicode.rs:11:8 | -11 | extern "路濫狼á́́" fn foo() {} - | ^^^^^^^^ +11 | extern "路濫狼á́́" fn foo() {} //~ ERROR invalid ABI + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/unicode_2.rs b/src/test/ui/codemap_tests/unicode_2.rs new file mode 100644 index 0000000000000..c01b0b286afca --- /dev/null +++ b/src/test/ui/codemap_tests/unicode_2.rs @@ -0,0 +1,17 @@ +// 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. + +#![feature(non_ascii_idents)] + +fn main() { + let _ = ("a̐éö̲", 0u7); //~ ERROR invalid width + let _ = ("아あ", 1i42); //~ ERROR invalid width + let _ = a̐é; //~ ERROR cannot find +} diff --git a/src/test/ui/codemap_tests/unicode_2.stderr b/src/test/ui/codemap_tests/unicode_2.stderr new file mode 100644 index 0000000000000..9ffd08ca06f83 --- /dev/null +++ b/src/test/ui/codemap_tests/unicode_2.stderr @@ -0,0 +1,24 @@ +error: invalid width `7` for integer literal + --> $DIR/unicode_2.rs:14:25 + | +14 | let _ = ("a̐éö̲", 0u7); //~ ERROR invalid width + | ^^^ + | + = help: valid widths are 8, 16, 32, 64 and 128 + +error: invalid width `42` for integer literal + --> $DIR/unicode_2.rs:15:20 + | +15 | let _ = ("아あ", 1i42); //~ ERROR invalid width + | ^^^^ + | + = help: valid widths are 8, 16, 32, 64 and 128 + +error[E0425]: cannot find value `a̐é` in this scope + --> $DIR/unicode_2.rs:16:13 + | +16 | let _ = a̐é; //~ ERROR cannot find + | ^^ not found in this scope + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/codemap_tests/unicode_3.rs b/src/test/ui/codemap_tests/unicode_3.rs new file mode 100644 index 0000000000000..5294eedb8457c --- /dev/null +++ b/src/test/ui/codemap_tests/unicode_3.rs @@ -0,0 +1,14 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let s = "ZͨA͑ͦ͒͋ͤ͑̚L̄͑͋Ĝͨͥ̿͒̽̈́Oͥ͛ͭ!̏"; while true { break; } + println!("{}", s); +} diff --git a/src/test/ui/codemap_tests/unicode_3.stderr b/src/test/ui/codemap_tests/unicode_3.stderr new file mode 100644 index 0000000000000..a7514a6b79229 --- /dev/null +++ b/src/test/ui/codemap_tests/unicode_3.stderr @@ -0,0 +1,10 @@ +warning: denote infinite loops with `loop { ... }` + --> $DIR/unicode_3.rs:12:45 + | +12 | let s = "ZͨA͑ͦ͒͋ͤ͑̚L̄͑͋Ĝͨͥ̿͒̽̈́Oͥ͛ͭ!̏"; while true { break; } + | ----------^^^^^^^^^^^ + | | + | help: use `loop` + | + = note: #[warn(while_true)] on by default + diff --git a/src/test/ui/coercion-missing-tail-expected-type.rs b/src/test/ui/coercion-missing-tail-expected-type.rs index 15ce79a054f26..b235a0f21360c 100644 --- a/src/test/ui/coercion-missing-tail-expected-type.rs +++ b/src/test/ui/coercion-missing-tail-expected-type.rs @@ -10,11 +10,11 @@ // #41425 -- error message "mismatched types" has wrong types -fn plus_one(x: i32) -> i32 { +fn plus_one(x: i32) -> i32 { //~ ERROR mismatched types x + 1; } -fn foo() -> Result { +fn foo() -> Result { //~ ERROR mismatched types Ok(1); } diff --git a/src/test/ui/coercion-missing-tail-expected-type.stderr b/src/test/ui/coercion-missing-tail-expected-type.stderr index 0de0a25e68e24..93f57216ca063 100644 --- a/src/test/ui/coercion-missing-tail-expected-type.stderr +++ b/src/test/ui/coercion-missing-tail-expected-type.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/coercion-missing-tail-expected-type.rs:13:28 | -13 | fn plus_one(x: i32) -> i32 { +13 | fn plus_one(x: i32) -> i32 { //~ ERROR mismatched types | ____________________________^ 14 | | x + 1; | | - help: consider removing this semicolon @@ -14,7 +14,7 @@ error[E0308]: mismatched types error[E0308]: mismatched types --> $DIR/coercion-missing-tail-expected-type.rs:17:29 | -17 | fn foo() -> Result { +17 | fn foo() -> Result { //~ ERROR mismatched types | _____________________________^ 18 | | Ok(1); | | - help: consider removing this semicolon diff --git a/src/test/ui/command-line-diagnostics.rs b/src/test/ui/command-line-diagnostics.rs new file mode 100644 index 0000000000000..ac631c2e45e59 --- /dev/null +++ b/src/test/ui/command-line-diagnostics.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test checks the output format without the intermediate json representation +// compile-flags: --error-format=human + +pub fn main() { + let x = 42; + x = 43; +} diff --git a/src/test/ui/command-line-diagnostics.stderr b/src/test/ui/command-line-diagnostics.stderr new file mode 100644 index 0000000000000..48ca45914c65d --- /dev/null +++ b/src/test/ui/command-line-diagnostics.stderr @@ -0,0 +1,10 @@ +error[E0384]: cannot assign twice to immutable variable `x` + --> $DIR/command-line-diagnostics.rs:16:5 + | +15 | let x = 42; + | - first assignment to `x` +16 | x = 43; + | ^^^^^^ cannot assign twice to immutable variable + +error: aborting due to previous error + diff --git a/src/test/ui/compare-method/proj-outlives-region.stderr b/src/test/ui/compare-method/proj-outlives-region.stderr index e58251c846f8d..f871f034a924d 100644 --- a/src/test/ui/compare-method/proj-outlives-region.stderr +++ b/src/test/ui/compare-method/proj-outlives-region.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/proj-outlives-region.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where U: 'a { } //~ ERROR E0276 | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error diff --git a/src/test/ui/compare-method/region-extra.rs b/src/test/ui/compare-method/region-extra.rs index e359f08096885..9befa1ba60eed 100644 --- a/src/test/ui/compare-method/region-extra.rs +++ b/src/test/ui/compare-method/region-extra.rs @@ -16,7 +16,7 @@ trait Master<'a, 'b> { } impl<'a, 'b> Master<'a, 'b> for () { - fn foo() where 'a: 'b { } + fn foo() where 'a: 'b { } //~ ERROR impl has stricter } fn main() { diff --git a/src/test/ui/compare-method/region-extra.stderr b/src/test/ui/compare-method/region-extra.stderr index bc42b505818b8..d46376b4a42b7 100644 --- a/src/test/ui/compare-method/region-extra.stderr +++ b/src/test/ui/compare-method/region-extra.stderr @@ -4,7 +4,7 @@ error[E0276]: impl has stricter requirements than trait 15 | fn foo(); | --------- definition of `foo` from trait ... -19 | fn foo() where 'a: 'b { } +19 | fn foo() where 'a: 'b { } //~ ERROR impl has stricter | ^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'a: 'b` error: aborting due to previous error diff --git a/src/test/ui/compare-method/region-unrelated.rs b/src/test/ui/compare-method/region-unrelated.rs index 719e15fdb61fe..31ab6cb7fc42c 100644 --- a/src/test/ui/compare-method/region-unrelated.rs +++ b/src/test/ui/compare-method/region-unrelated.rs @@ -17,7 +17,7 @@ trait Master<'a, T: ?Sized, U> { // `U: 'a` does not imply `V: 'a` impl<'a, U, V> Master<'a, U, V> for () { fn foo() where V: 'a { } - //~^ ERROR parameter type `V` may not live long enough + //~^ ERROR impl has stricter requirements than trait } fn main() { diff --git a/src/test/ui/compare-method/region-unrelated.stderr b/src/test/ui/compare-method/region-unrelated.stderr index 95db68fea5cf7..1df83c7fb0c39 100644 --- a/src/test/ui/compare-method/region-unrelated.stderr +++ b/src/test/ui/compare-method/region-unrelated.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/region-unrelated.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where V: 'a { } | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 1d4ded6e7123a..85ab2a0052164 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -15,7 +15,9 @@ const fn foo(x: u32) -> u32 { } fn main() { - const X: u32 = 0-1; - const Y: u32 = foo(0-1); + const X: u32 = 0-1; //~ ERROR constant evaluation error + //~^ WARN constant evaluation error + const Y: u32 = foo(0-1); //~ ERROR constant evaluation error + //~^ WARN constant evaluation error println!("{} {}", X, Y); } diff --git a/src/test/ui/const-eval/issue-43197.stderr b/src/test/ui/const-eval/issue-43197.stderr index 5ff80060eac7a..82baab620ffab 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,27 +1,27 @@ -warning: constant evaluation error: attempt to subtract with overflow. This will become a HARD ERROR in the future +warning: constant evaluation error: attempt to subtract with overflow --> $DIR/issue-43197.rs:18:20 | -18 | const X: u32 = 0-1; +18 | const X: u32 = 0-1; //~ ERROR constant evaluation error | ^^^ | = note: #[warn(const_err)] on by default -warning: constant evaluation error: attempt to subtract with overflow. This will become a HARD ERROR in the future - --> $DIR/issue-43197.rs:19:20 +warning: constant evaluation error: attempt to subtract with overflow + --> $DIR/issue-43197.rs:20:20 | -19 | const Y: u32 = foo(0-1); +20 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error | ^^^^^^^^ error[E0080]: constant evaluation error --> $DIR/issue-43197.rs:18:20 | -18 | const X: u32 = 0-1; +18 | const X: u32 = 0-1; //~ ERROR constant evaluation error | ^^^ attempt to subtract with overflow error[E0080]: constant evaluation error - --> $DIR/issue-43197.rs:19:24 + --> $DIR/issue-43197.rs:20:24 | -19 | const Y: u32 = foo(0-1); +20 | const Y: u32 = foo(0-1); //~ ERROR constant evaluation error | ^^^ attempt to subtract with overflow error: aborting due to 2 previous errors diff --git a/src/test/compile-fail/task-rng-isnt-sendable.rs b/src/test/ui/const-expr-addr-operator.rs similarity index 59% rename from src/test/compile-fail/task-rng-isnt-sendable.rs rename to src/test/ui/const-expr-addr-operator.rs index d85717f8ce55e..24d4457f01d70 100644 --- a/src/test/compile-fail/task-rng-isnt-sendable.rs +++ b/src/test/ui/const-expr-addr-operator.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rand)] - -// ensure that the ThreadRng isn't/doesn't become accidentally sendable. - -use std::__rand::ThreadRng; - -fn test_send() {} +// Encountered while testing #44614. pub fn main() { - test_send::(); //~ ERROR std::marker::Send` is not satisfied + // Constant of generic type (int) + const X: &'static u32 = &22; //~ ERROR constant evaluation error + assert_eq!(0, match &22 { + X => 0, + _ => 1, + }); } diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr new file mode 100644 index 0000000000000..f6587c703bd7f --- /dev/null +++ b/src/test/ui/const-expr-addr-operator.stderr @@ -0,0 +1,14 @@ +error[E0080]: constant evaluation error + --> $DIR/const-expr-addr-operator.rs:15:29 + | +15 | const X: &'static u32 = &22; //~ ERROR constant evaluation error + | ^^^ unimplemented constant expression: address operator + | +note: for pattern here + --> $DIR/const-expr-addr-operator.rs:17:9 + | +17 | X => 0, + | ^ + +error: aborting due to previous error + diff --git a/src/test/compile-fail/const-pattern-irrefutable.rs b/src/test/ui/const-pattern-irrefutable.rs similarity index 55% rename from src/test/compile-fail/const-pattern-irrefutable.rs rename to src/test/ui/const-pattern-irrefutable.rs index 11003067070f4..af0b95e002d84 100644 --- a/src/test/compile-fail/const-pattern-irrefutable.rs +++ b/src/test/ui/const-pattern-irrefutable.rs @@ -13,17 +13,14 @@ mod foo { pub const d: u8 = 2; } -use foo::b as c; //~ NOTE is imported here -use foo::d; //~ NOTE is imported here +use foo::b as c; +use foo::d; -const a: u8 = 2; //~ NOTE is defined here +const a: u8 = 2; fn main() { - let a = 4; //~ ERROR let bindings cannot shadow constants - //~^ NOTE cannot be named the same as a constant - let c = 4; //~ ERROR let bindings cannot shadow constants - //~^ NOTE cannot be named the same as a constant - let d = 4; //~ ERROR let bindings cannot shadow constants - //~^ NOTE cannot be named the same as a constant + let a = 4; //~ ERROR refutable pattern in local binding: `_` not covered + let c = 4; //~ ERROR refutable pattern in local binding: `_` not covered + let d = 4; //~ ERROR refutable pattern in local binding: `_` not covered fn f() {} // Check that the `NOTE`s still work with an item here (c.f. issue #35115). } diff --git a/src/test/ui/const-pattern-irrefutable.stderr b/src/test/ui/const-pattern-irrefutable.stderr new file mode 100644 index 0000000000000..af48b7736381a --- /dev/null +++ b/src/test/ui/const-pattern-irrefutable.stderr @@ -0,0 +1,20 @@ +error[E0005]: refutable pattern in local binding: `_` not covered + --> $DIR/const-pattern-irrefutable.rs:22:9 + | +22 | let a = 4; //~ ERROR refutable pattern in local binding: `_` not covered + | ^ interpreted as a constant pattern, not new variable + +error[E0005]: refutable pattern in local binding: `_` not covered + --> $DIR/const-pattern-irrefutable.rs:23:9 + | +23 | let c = 4; //~ ERROR refutable pattern in local binding: `_` not covered + | ^ interpreted as a constant pattern, not new variable + +error[E0005]: refutable pattern in local binding: `_` not covered + --> $DIR/const-pattern-irrefutable.rs:24:9 + | +24 | let d = 4; //~ ERROR refutable pattern in local binding: `_` not covered + | ^ interpreted as a constant pattern, not new variable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/cross-crate-macro-backtrace/main.rs b/src/test/ui/cross-crate-macro-backtrace/main.rs index f8bb84abcd419..85640087a9d48 100644 --- a/src/test/ui/cross-crate-macro-backtrace/main.rs +++ b/src/test/ui/cross-crate-macro-backtrace/main.rs @@ -8,10 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// error-pattern: in format string + // aux-build:extern_macro_crate.rs #[macro_use(myprintln, myprint)] extern crate extern_macro_crate; fn main() { - myprintln!("{}"); //~ ERROR in this macro + myprintln!("{}"); } diff --git a/src/test/ui/cross-crate-macro-backtrace/main.stderr b/src/test/ui/cross-crate-macro-backtrace/main.stderr index 84db85ac092db..fc2343bdb1d4f 100644 --- a/src/test/ui/cross-crate-macro-backtrace/main.stderr +++ b/src/test/ui/cross-crate-macro-backtrace/main.stderr @@ -1,10 +1,10 @@ -error: invalid reference to argument `0` (no arguments given) - --> $DIR/main.rs:16:5 +error: 1 positional argument in format string, but no arguments were given + --> $DIR/main.rs:18:5 | -16 | myprintln!("{}"); //~ ERROR in this macro +18 | myprintln!("{}"); | ^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/deref-suggestion.rs b/src/test/ui/deref-suggestion.rs index 16d8226bfece1..04ba4ab905eb1 100644 --- a/src/test/ui/deref-suggestion.rs +++ b/src/test/ui/deref-suggestion.rs @@ -9,26 +9,26 @@ // except according to those terms. macro_rules! borrow { - ($x:expr) => { &$x } + ($x:expr) => { &$x } //~ ERROR mismatched types } fn foo(_: String) {} fn foo2(s: &String) { - foo(s); + foo(s); //~ ERROR mismatched types } fn foo3(_: u32) {} fn foo4(u: &u32) { - foo3(u); + foo3(u); //~ ERROR mismatched types } fn main() { let s = String::new(); let r_s = &s; foo2(r_s); - foo(&"aaa".to_owned()); - foo(&mut "aaa".to_owned()); + foo(&"aaa".to_owned()); //~ ERROR mismatched types + foo(&mut "aaa".to_owned()); //~ ERROR mismatched types foo3(borrow!(0)); foo4(&0); } diff --git a/src/test/ui/deref-suggestion.stderr b/src/test/ui/deref-suggestion.stderr index 5ad9c19fa8cc2..98ec3d9693fb8 100644 --- a/src/test/ui/deref-suggestion.stderr +++ b/src/test/ui/deref-suggestion.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:18:9 | -18 | foo(s); +18 | foo(s); //~ ERROR mismatched types | ^ expected struct `std::string::String`, found reference | = note: expected type `std::string::String` @@ -10,43 +10,49 @@ error[E0308]: mismatched types - .escape_debug() - .escape_default() - .escape_unicode() - - .to_lowercase() - - .to_uppercase() + - .to_ascii_lowercase() + - .to_ascii_uppercase() error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:23:10 | -23 | foo3(u); - | ^ expected u32, found &u32 +23 | foo3(u); //~ ERROR mismatched types + | ^ + | | + | expected u32, found &u32 + | help: consider dereferencing the borrow: `*u` | = note: expected type `u32` found type `&u32` - = help: try with `*u` error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:30:9 | -30 | foo(&"aaa".to_owned()); - | ^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found reference +30 | foo(&"aaa".to_owned()); //~ ERROR mismatched types + | ^^^^^^^^^^^^^^^^^ + | | + | expected struct `std::string::String`, found reference + | help: consider removing the borrow: `"aaa".to_owned()` | = note: expected type `std::string::String` found type `&std::string::String` - = help: try with `"aaa".to_owned()` error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:31:9 | -31 | foo(&mut "aaa".to_owned()); - | ^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found mutable reference +31 | foo(&mut "aaa".to_owned()); //~ ERROR mismatched types + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | expected struct `std::string::String`, found mutable reference + | help: consider removing the borrow: `"aaa".to_owned()` | = note: expected type `std::string::String` found type `&mut std::string::String` - = help: try with `"aaa".to_owned()` error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:12:20 | -12 | ($x:expr) => { &$x } +12 | ($x:expr) => { &$x } //~ ERROR mismatched types | ^^^ expected u32, found &{integer} ... 32 | foo3(borrow!(0)); diff --git a/src/test/ui/deriving-with-repr-packed.rs b/src/test/ui/deriving-with-repr-packed.rs new file mode 100644 index 0000000000000..0c52829799ea8 --- /dev/null +++ b/src/test/ui/deriving-with-repr-packed.rs @@ -0,0 +1,41 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(safe_packed_borrows)] + +// check that derive on a packed struct with non-Copy fields +// correctly. This can't be made to work perfectly because +// we can't just use the field from the struct as it might +// not be aligned. + +#[derive(Copy, Clone, PartialEq, Eq)] +//~^ ERROR #[derive] can't be used +//~| hard error +//~^^^ ERROR #[derive] can't be used +//~| hard error +#[repr(packed)] +pub struct Foo(T, T, T); + +#[derive(PartialEq, Eq)] +//~^ ERROR #[derive] can't be used +//~| hard error +#[repr(packed)] +pub struct Bar(u32, u32, u32); + +#[derive(PartialEq)] +struct Y(usize); + +#[derive(PartialEq)] +//~^ ERROR #[derive] can't be used on a non-Copy #[repr(packed)] +//~| hard error +#[repr(packed)] +struct X(Y); + +fn main() {} diff --git a/src/test/ui/deriving-with-repr-packed.stderr b/src/test/ui/deriving-with-repr-packed.stderr new file mode 100644 index 0000000000000..48208faa6b5e2 --- /dev/null +++ b/src/test/ui/deriving-with-repr-packed.stderr @@ -0,0 +1,43 @@ +error: #[derive] can't be used on a #[repr(packed)] struct with type parameters (error E0133) + --> $DIR/deriving-with-repr-packed.rs:18:16 + | +18 | #[derive(Copy, Clone, PartialEq, Eq)] + | ^^^^^ + | +note: lint level defined here + --> $DIR/deriving-with-repr-packed.rs:11:9 + | +11 | #![deny(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + +error: #[derive] can't be used on a #[repr(packed)] struct with type parameters (error E0133) + --> $DIR/deriving-with-repr-packed.rs:18:23 + | +18 | #[derive(Copy, Clone, PartialEq, Eq)] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + +error: #[derive] can't be used on a non-Copy #[repr(packed)] struct (error E0133) + --> $DIR/deriving-with-repr-packed.rs:26:10 + | +26 | #[derive(PartialEq, Eq)] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + +error: #[derive] can't be used on a non-Copy #[repr(packed)] struct (error E0133) + --> $DIR/deriving-with-repr-packed.rs:35:10 + | +35 | #[derive(PartialEq)] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/did_you_mean/E0178.rs b/src/test/ui/did_you_mean/E0178.rs index 8fb6c9815cef8..21cdb38fdb118 100644 --- a/src/test/ui/did_you_mean/E0178.rs +++ b/src/test/ui/did_you_mean/E0178.rs @@ -11,10 +11,10 @@ trait Foo {} struct Bar<'a> { - w: &'a Foo + Copy, - x: &'a Foo + 'a, - y: &'a mut Foo + 'a, - z: fn() -> Foo + 'a, + w: &'a Foo + Copy, //~ ERROR expected a path + x: &'a Foo + 'a, //~ ERROR expected a path + y: &'a mut Foo + 'a, //~ ERROR expected a path + z: fn() -> Foo + 'a, //~ ERROR expected a path } fn main() { diff --git a/src/test/ui/did_you_mean/E0178.stderr b/src/test/ui/did_you_mean/E0178.stderr index 15e7131cfd3be..4fe8849feef11 100644 --- a/src/test/ui/did_you_mean/E0178.stderr +++ b/src/test/ui/did_you_mean/E0178.stderr @@ -1,25 +1,25 @@ error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo` --> $DIR/E0178.rs:14:8 | -14 | w: &'a Foo + Copy, +14 | w: &'a Foo + Copy, //~ ERROR expected a path | ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Copy)` error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo` --> $DIR/E0178.rs:15:8 | -15 | x: &'a Foo + 'a, +15 | x: &'a Foo + 'a, //~ ERROR expected a path | ^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + 'a)` error[E0178]: expected a path on the left-hand side of `+`, not `&'a mut Foo` --> $DIR/E0178.rs:16:8 | -16 | y: &'a mut Foo + 'a, +16 | y: &'a mut Foo + 'a, //~ ERROR expected a path | ^^^^^^^^^^^^^^^^ help: try adding parentheses: `&'a mut (Foo + 'a)` error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> Foo` --> $DIR/E0178.rs:17:8 | -17 | z: fn() -> Foo + 'a, +17 | z: fn() -> Foo + 'a, //~ ERROR expected a path | ^^^^^^^^^^^^^^^^ perhaps you forgot parentheses? error: aborting due to 4 previous errors diff --git a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.rs b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.rs index 99035209e14bc..076b61b179061 100644 --- a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.rs +++ b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.rs @@ -33,7 +33,4 @@ fn main() { f1.foo(1usize); //~^ error: the trait bound `Bar: Foo` is not satisfied - //~| help: the following implementations were found: - //~| help: > - //~| help: > } diff --git a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.rs b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.rs index 2009c32c85436..6beff6ba2a100 100644 --- a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.rs +++ b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.rs @@ -37,10 +37,4 @@ fn main() { f1.foo(1usize); //~^ error: the trait bound `Bar: Foo` is not satisfied - //~| help: the following implementations were found: - //~| help: > - //~| help: > - //~| help: > - //~| help: > - //~| help: and 2 others } diff --git a/src/test/ui/did_you_mean/issue-31424.rs b/src/test/ui/did_you_mean/issue-31424.rs index 374d06bb71d27..1b31e064337e2 100644 --- a/src/test/ui/did_you_mean/issue-31424.rs +++ b/src/test/ui/did_you_mean/issue-31424.rs @@ -14,13 +14,13 @@ struct Struct; impl Struct { fn foo(&mut self) { - (&mut self).bar(); + (&mut self).bar(); //~ ERROR cannot borrow } // In this case we could keep the suggestion, but to distinguish the // two cases is pretty hard. It's an obscure case anyway. fn bar(self: &mut Self) { - (&mut self).bar(); + (&mut self).bar(); //~ ERROR cannot borrow } } diff --git a/src/test/ui/did_you_mean/issue-31424.stderr b/src/test/ui/did_you_mean/issue-31424.stderr index 47dc7c975724a..cd96d28ac98cb 100644 --- a/src/test/ui/did_you_mean/issue-31424.stderr +++ b/src/test/ui/did_you_mean/issue-31424.stderr @@ -1,7 +1,7 @@ error[E0596]: cannot borrow immutable argument `self` as mutable --> $DIR/issue-31424.rs:17:15 | -17 | (&mut self).bar(); +17 | (&mut self).bar(); //~ ERROR cannot borrow | ^^^^ | | | cannot reborrow mutably @@ -12,7 +12,7 @@ error[E0596]: cannot borrow immutable argument `self` as mutable | 22 | fn bar(self: &mut Self) { | --------------- consider changing this to `mut self: &mut Self` -23 | (&mut self).bar(); +23 | (&mut self).bar(); //~ ERROR cannot borrow | ^^^^ cannot borrow mutably error: aborting due to 2 previous errors diff --git a/src/test/ui/did_you_mean/issue-34126.rs b/src/test/ui/did_you_mean/issue-34126.rs index 9523e6bbf383b..9dfb38abd049f 100644 --- a/src/test/ui/did_you_mean/issue-34126.rs +++ b/src/test/ui/did_you_mean/issue-34126.rs @@ -13,7 +13,7 @@ struct Z { } impl Z { fn run(&self, z: &mut Z) { } fn start(&mut self) { - self.run(&mut self); + self.run(&mut self); //~ ERROR cannot borrow } } diff --git a/src/test/ui/did_you_mean/issue-34126.stderr b/src/test/ui/did_you_mean/issue-34126.stderr index d9ef0c4541041..a4921046c7833 100644 --- a/src/test/ui/did_you_mean/issue-34126.stderr +++ b/src/test/ui/did_you_mean/issue-34126.stderr @@ -1,7 +1,7 @@ error[E0596]: cannot borrow immutable argument `self` as mutable --> $DIR/issue-34126.rs:16:23 | -16 | self.run(&mut self); +16 | self.run(&mut self); //~ ERROR cannot borrow | ^^^^ | | | cannot reborrow mutably diff --git a/src/test/ui/did_you_mean/issue-34337.rs b/src/test/ui/did_you_mean/issue-34337.rs index 42853a5d83db0..a426c0f48ccec 100644 --- a/src/test/ui/did_you_mean/issue-34337.rs +++ b/src/test/ui/did_you_mean/issue-34337.rs @@ -13,5 +13,5 @@ fn get(key: &mut String) { } fn main() { let mut v: Vec = Vec::new(); let ref mut key = v[0]; - get(&mut key); + get(&mut key); //~ ERROR cannot borrow } diff --git a/src/test/ui/did_you_mean/issue-34337.stderr b/src/test/ui/did_you_mean/issue-34337.stderr index 20478165c7ea0..a53d3d7277aa3 100644 --- a/src/test/ui/did_you_mean/issue-34337.stderr +++ b/src/test/ui/did_you_mean/issue-34337.stderr @@ -1,7 +1,7 @@ error[E0596]: cannot borrow immutable local variable `key` as mutable --> $DIR/issue-34337.rs:16:14 | -16 | get(&mut key); +16 | get(&mut key); //~ ERROR cannot borrow | ^^^ | | | cannot reborrow mutably diff --git a/src/test/ui/did_you_mean/issue-35937.rs b/src/test/ui/did_you_mean/issue-35937.rs index 9ec8728fd32c1..867b47cf99e1e 100644 --- a/src/test/ui/did_you_mean/issue-35937.rs +++ b/src/test/ui/did_you_mean/issue-35937.rs @@ -14,7 +14,7 @@ struct Foo { fn main() { let f = Foo { v: Vec::new() }; - f.v.push("cat".to_string()); + f.v.push("cat".to_string()); //~ ERROR cannot borrow } @@ -23,9 +23,9 @@ struct S { } fn foo() { let s = S { x: 42 }; - s.x += 1; + s.x += 1; //~ ERROR cannot assign } fn bar(s: S) { - s.x += 1; + s.x += 1; //~ ERROR cannot assign } diff --git a/src/test/ui/did_you_mean/issue-35937.stderr b/src/test/ui/did_you_mean/issue-35937.stderr index 1cd1fb76aa330..ec44755cb7c95 100644 --- a/src/test/ui/did_you_mean/issue-35937.stderr +++ b/src/test/ui/did_you_mean/issue-35937.stderr @@ -3,7 +3,7 @@ error[E0596]: cannot borrow immutable field `f.v` as mutable | 16 | let f = Foo { v: Vec::new() }; | - consider changing this to `mut f` -17 | f.v.push("cat".to_string()); +17 | f.v.push("cat".to_string()); //~ ERROR cannot borrow | ^^^ cannot mutably borrow immutable field error[E0594]: cannot assign to immutable field `s.x` @@ -11,7 +11,7 @@ error[E0594]: cannot assign to immutable field `s.x` | 25 | let s = S { x: 42 }; | - consider changing this to `mut s` -26 | s.x += 1; +26 | s.x += 1; //~ ERROR cannot assign | ^^^^^^^^ cannot mutably borrow immutable field error[E0594]: cannot assign to immutable field `s.x` @@ -19,7 +19,7 @@ error[E0594]: cannot assign to immutable field `s.x` | 29 | fn bar(s: S) { | - consider changing this to `mut s` -30 | s.x += 1; +30 | s.x += 1; //~ ERROR cannot assign | ^^^^^^^^ cannot mutably borrow immutable field error: aborting due to 3 previous errors diff --git a/src/test/ui/did_you_mean/issue-36798.rs b/src/test/ui/did_you_mean/issue-36798.rs index cd0d0951abf8a..6e641ff025ce3 100644 --- a/src/test/ui/did_you_mean/issue-36798.rs +++ b/src/test/ui/did_you_mean/issue-36798.rs @@ -14,5 +14,5 @@ struct Foo { fn main() { let f = Foo { bar: 22 }; - f.baz; + f.baz; //~ ERROR no field } diff --git a/src/test/ui/did_you_mean/issue-36798.stderr b/src/test/ui/did_you_mean/issue-36798.stderr index 72fd09c035719..73319d567bd76 100644 --- a/src/test/ui/did_you_mean/issue-36798.stderr +++ b/src/test/ui/did_you_mean/issue-36798.stderr @@ -1,7 +1,7 @@ error[E0609]: no field `baz` on type `Foo` --> $DIR/issue-36798.rs:17:7 | -17 | f.baz; +17 | f.baz; //~ ERROR no field | ^^^ did you mean `bar`? error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-36798_unknown_field.rs b/src/test/ui/did_you_mean/issue-36798_unknown_field.rs index 2970a325a6af8..ec54a8d2b4390 100644 --- a/src/test/ui/did_you_mean/issue-36798_unknown_field.rs +++ b/src/test/ui/did_you_mean/issue-36798_unknown_field.rs @@ -14,5 +14,5 @@ struct Foo { fn main() { let f = Foo { bar: 22 }; - f.zz; + f.zz; //~ ERROR no field } diff --git a/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr b/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr index 20bb7d4c91de3..f17672b234fc6 100644 --- a/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr +++ b/src/test/ui/did_you_mean/issue-36798_unknown_field.stderr @@ -1,7 +1,7 @@ error[E0609]: no field `zz` on type `Foo` --> $DIR/issue-36798_unknown_field.rs:17:7 | -17 | f.zz; +17 | f.zz; //~ ERROR no field | ^^ unknown field | = note: available fields are: `bar` diff --git a/src/test/ui/did_you_mean/issue-37139.rs b/src/test/ui/did_you_mean/issue-37139.rs index 65181768053c0..8a1a7ce0c320b 100644 --- a/src/test/ui/did_you_mean/issue-37139.rs +++ b/src/test/ui/did_you_mean/issue-37139.rs @@ -19,7 +19,7 @@ fn main() { let mut x = TestEnum::Item(10); match x { TestEnum::Item(ref mut x) => { - test(&mut x); + test(&mut x); //~ ERROR cannot borrow immutable } } } diff --git a/src/test/ui/did_you_mean/issue-37139.stderr b/src/test/ui/did_you_mean/issue-37139.stderr index 9fc364f861275..65de724616d10 100644 --- a/src/test/ui/did_you_mean/issue-37139.stderr +++ b/src/test/ui/did_you_mean/issue-37139.stderr @@ -1,7 +1,7 @@ error[E0596]: cannot borrow immutable local variable `x` as mutable --> $DIR/issue-37139.rs:22:23 | -22 | test(&mut x); +22 | test(&mut x); //~ ERROR cannot borrow immutable | ^ | | | cannot reborrow mutably diff --git a/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.rs b/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.rs index 1938d33e53030..c9c1c5d141d6a 100644 --- a/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.rs +++ b/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use Foo; +use Foo; //~ ERROR unresolved -use Foo1; +use Foo1; //~ ERROR unresolved fn main() {} diff --git a/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.stderr b/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.stderr index 325f55e686c62..c58958c7f5e3f 100644 --- a/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.stderr +++ b/src/test/ui/did_you_mean/issue-38054-do-not-show-unresolved-names.stderr @@ -1,13 +1,13 @@ error[E0432]: unresolved import `Foo` --> $DIR/issue-38054-do-not-show-unresolved-names.rs:11:5 | -11 | use Foo; +11 | use Foo; //~ ERROR unresolved | ^^^ no `Foo` in the root error[E0432]: unresolved import `Foo1` --> $DIR/issue-38054-do-not-show-unresolved-names.rs:13:5 | -13 | use Foo1; +13 | use Foo1; //~ ERROR unresolved | ^^^^ no `Foo1` in the root error: aborting due to 2 previous errors diff --git a/src/test/ui/did_you_mean/issue-38147-1.rs b/src/test/ui/did_you_mean/issue-38147-1.rs index 136921dd0a569..a7ce7406566b3 100644 --- a/src/test/ui/did_you_mean/issue-38147-1.rs +++ b/src/test/ui/did_you_mean/issue-38147-1.rs @@ -24,7 +24,7 @@ struct Foo<'a> { impl<'a> Foo<'a> { fn f(&self) { - self.s.push('x'); + self.s.push('x'); //~ ERROR cannot borrow data mutably } } diff --git a/src/test/ui/did_you_mean/issue-38147-1.stderr b/src/test/ui/did_you_mean/issue-38147-1.stderr index e9f2b1dad806d..6a262b3102638 100644 --- a/src/test/ui/did_you_mean/issue-38147-1.stderr +++ b/src/test/ui/did_you_mean/issue-38147-1.stderr @@ -3,7 +3,7 @@ error[E0389]: cannot borrow data mutably in a `&` reference | 26 | fn f(&self) { | ----- use `&mut self` here to make mutable -27 | self.s.push('x'); +27 | self.s.push('x'); //~ ERROR cannot borrow data mutably | ^^^^^^ assignment into an immutable reference error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-38147-2.rs b/src/test/ui/did_you_mean/issue-38147-2.rs index a5d533edf75ec..cc6be98bcf8a7 100644 --- a/src/test/ui/did_you_mean/issue-38147-2.rs +++ b/src/test/ui/did_you_mean/issue-38147-2.rs @@ -14,7 +14,7 @@ struct Bar<'a> { impl<'a> Bar<'a> { fn f(&mut self) { - self.s.push('x'); + self.s.push('x'); //~ ERROR cannot borrow immutable borrowed } } diff --git a/src/test/ui/did_you_mean/issue-38147-2.stderr b/src/test/ui/did_you_mean/issue-38147-2.stderr index e81bc722fa098..b09ecf9057c0b 100644 --- a/src/test/ui/did_you_mean/issue-38147-2.stderr +++ b/src/test/ui/did_you_mean/issue-38147-2.stderr @@ -4,7 +4,7 @@ error[E0596]: cannot borrow immutable borrowed content `*self.s` as mutable 12 | s: &'a String | ---------- use `&'a mut String` here to make mutable ... -17 | self.s.push('x'); +17 | self.s.push('x'); //~ ERROR cannot borrow immutable borrowed | ^^^^^^ cannot borrow as mutable error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-38147-3.rs b/src/test/ui/did_you_mean/issue-38147-3.rs index 5e8f2d3eaefaa..42b2910051722 100644 --- a/src/test/ui/did_you_mean/issue-38147-3.rs +++ b/src/test/ui/did_you_mean/issue-38147-3.rs @@ -14,7 +14,7 @@ struct Qux<'a> { impl<'a> Qux<'a> { fn f(&self) { - self.s.push('x'); + self.s.push('x'); //~ ERROR cannot borrow immutable borrowed } } diff --git a/src/test/ui/did_you_mean/issue-38147-3.stderr b/src/test/ui/did_you_mean/issue-38147-3.stderr index 749795f4d8fbd..ca721f133a44b 100644 --- a/src/test/ui/did_you_mean/issue-38147-3.stderr +++ b/src/test/ui/did_you_mean/issue-38147-3.stderr @@ -4,7 +4,7 @@ error[E0596]: cannot borrow immutable borrowed content `*self.s` as mutable 12 | s: &'a String | ---------- use `&'a mut String` here to make mutable ... -17 | self.s.push('x'); +17 | self.s.push('x'); //~ ERROR cannot borrow immutable borrowed | ^^^^^^ cannot borrow as mutable error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-38147-4.rs b/src/test/ui/did_you_mean/issue-38147-4.rs index 4eb20ceeed48e..49a8f2b6ff691 100644 --- a/src/test/ui/did_you_mean/issue-38147-4.rs +++ b/src/test/ui/did_you_mean/issue-38147-4.rs @@ -13,7 +13,7 @@ struct Foo<'a> { } fn f(x: usize, f: &Foo) { - f.s.push('x'); + f.s.push('x'); //~ ERROR cannot borrow data mutably } fn main() {} diff --git a/src/test/ui/did_you_mean/issue-38147-4.stderr b/src/test/ui/did_you_mean/issue-38147-4.stderr index 9a8853f4fbbdb..33bf2e1160c90 100644 --- a/src/test/ui/did_you_mean/issue-38147-4.stderr +++ b/src/test/ui/did_you_mean/issue-38147-4.stderr @@ -3,7 +3,7 @@ error[E0389]: cannot borrow data mutably in a `&` reference | 15 | fn f(x: usize, f: &Foo) { | ---- use `&mut Foo` here to make mutable -16 | f.s.push('x'); +16 | f.s.push('x'); //~ ERROR cannot borrow data mutably | ^^^ assignment into an immutable reference error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-39544.rs b/src/test/ui/did_you_mean/issue-39544.rs index d7c8935560623..7cd7768078a0c 100644 --- a/src/test/ui/did_you_mean/issue-39544.rs +++ b/src/test/ui/did_you_mean/issue-39544.rs @@ -18,42 +18,42 @@ pub struct Z { fn main() { let z = Z { x: X::Y }; - let _ = &mut z.x; + let _ = &mut z.x; //~ ERROR cannot borrow } impl Z { fn foo<'z>(&'z self) { - let _ = &mut self.x; + let _ = &mut self.x; //~ ERROR cannot borrow } fn foo1(&self, other: &Z) { - let _ = &mut self.x; - let _ = &mut other.x; + let _ = &mut self.x; //~ ERROR cannot borrow + let _ = &mut other.x; //~ ERROR cannot borrow } fn foo2<'a>(&'a self, other: &Z) { - let _ = &mut self.x; - let _ = &mut other.x; + let _ = &mut self.x; //~ ERROR cannot borrow + let _ = &mut other.x; //~ ERROR cannot borrow } fn foo3<'a>(self: &'a Self, other: &Z) { - let _ = &mut self.x; - let _ = &mut other.x; + let _ = &mut self.x; //~ ERROR cannot borrow + let _ = &mut other.x; //~ ERROR cannot borrow } fn foo4(other: &Z) { - let _ = &mut other.x; + let _ = &mut other.x; //~ ERROR cannot borrow } } pub fn with_arg(z: Z, w: &Z) { - let _ = &mut z.x; - let _ = &mut w.x; + let _ = &mut z.x; //~ ERROR cannot borrow + let _ = &mut w.x; //~ ERROR cannot borrow } pub fn with_tuple() { let mut y = 0; let x = (&y,); - *x.0 = 1; + *x.0 = 1; //~ ERROR cannot assign to immutable borrowed content } diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index 28aaab97bdad1..1fcb05374f681 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -3,7 +3,7 @@ error[E0596]: cannot borrow immutable field `z.x` as mutable | 20 | let z = Z { x: X::Y }; | - consider changing this to `mut z` -21 | let _ = &mut z.x; +21 | let _ = &mut z.x; //~ ERROR cannot borrow | ^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `self.x` as mutable @@ -11,7 +11,7 @@ error[E0596]: cannot borrow immutable field `self.x` as mutable | 25 | fn foo<'z>(&'z self) { | -------- use `&'z mut self` here to make mutable -26 | let _ = &mut self.x; +26 | let _ = &mut self.x; //~ ERROR cannot borrow | ^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `self.x` as mutable @@ -19,7 +19,7 @@ error[E0596]: cannot borrow immutable field `self.x` as mutable | 29 | fn foo1(&self, other: &Z) { | ----- use `&mut self` here to make mutable -30 | let _ = &mut self.x; +30 | let _ = &mut self.x; //~ ERROR cannot borrow | ^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `other.x` as mutable @@ -27,8 +27,8 @@ error[E0596]: cannot borrow immutable field `other.x` as mutable | 29 | fn foo1(&self, other: &Z) { | -- use `&mut Z` here to make mutable -30 | let _ = &mut self.x; -31 | let _ = &mut other.x; +30 | let _ = &mut self.x; //~ ERROR cannot borrow +31 | let _ = &mut other.x; //~ ERROR cannot borrow | ^^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `self.x` as mutable @@ -36,7 +36,7 @@ error[E0596]: cannot borrow immutable field `self.x` as mutable | 34 | fn foo2<'a>(&'a self, other: &Z) { | -------- use `&'a mut self` here to make mutable -35 | let _ = &mut self.x; +35 | let _ = &mut self.x; //~ ERROR cannot borrow | ^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `other.x` as mutable @@ -44,8 +44,8 @@ error[E0596]: cannot borrow immutable field `other.x` as mutable | 34 | fn foo2<'a>(&'a self, other: &Z) { | -- use `&mut Z` here to make mutable -35 | let _ = &mut self.x; -36 | let _ = &mut other.x; +35 | let _ = &mut self.x; //~ ERROR cannot borrow +36 | let _ = &mut other.x; //~ ERROR cannot borrow | ^^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `self.x` as mutable @@ -53,7 +53,7 @@ error[E0596]: cannot borrow immutable field `self.x` as mutable | 39 | fn foo3<'a>(self: &'a Self, other: &Z) { | -------- use `&'a mut Self` here to make mutable -40 | let _ = &mut self.x; +40 | let _ = &mut self.x; //~ ERROR cannot borrow | ^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `other.x` as mutable @@ -61,8 +61,8 @@ error[E0596]: cannot borrow immutable field `other.x` as mutable | 39 | fn foo3<'a>(self: &'a Self, other: &Z) { | -- use `&mut Z` here to make mutable -40 | let _ = &mut self.x; -41 | let _ = &mut other.x; +40 | let _ = &mut self.x; //~ ERROR cannot borrow +41 | let _ = &mut other.x; //~ ERROR cannot borrow | ^^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `other.x` as mutable @@ -70,7 +70,7 @@ error[E0596]: cannot borrow immutable field `other.x` as mutable | 44 | fn foo4(other: &Z) { | -- use `&mut Z` here to make mutable -45 | let _ = &mut other.x; +45 | let _ = &mut other.x; //~ ERROR cannot borrow | ^^^^^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `z.x` as mutable @@ -78,7 +78,7 @@ error[E0596]: cannot borrow immutable field `z.x` as mutable | 50 | pub fn with_arg(z: Z, w: &Z) { | - consider changing this to `mut z` -51 | let _ = &mut z.x; +51 | let _ = &mut z.x; //~ ERROR cannot borrow | ^^^ cannot mutably borrow immutable field error[E0596]: cannot borrow immutable field `w.x` as mutable @@ -86,14 +86,14 @@ error[E0596]: cannot borrow immutable field `w.x` as mutable | 50 | pub fn with_arg(z: Z, w: &Z) { | -- use `&mut Z` here to make mutable -51 | let _ = &mut z.x; -52 | let _ = &mut w.x; +51 | let _ = &mut z.x; //~ ERROR cannot borrow +52 | let _ = &mut w.x; //~ ERROR cannot borrow | ^^^ cannot mutably borrow immutable field error[E0594]: cannot assign to immutable borrowed content `*x.0` --> $DIR/issue-39544.rs:58:5 | -58 | *x.0 = 1; +58 | *x.0 = 1; //~ ERROR cannot assign to immutable borrowed content | ^^^^^^^^ cannot borrow as mutable error: aborting due to 12 previous errors diff --git a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.rs b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.rs index 68b1f79c89bbe..660aedc3596ed 100644 --- a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.rs +++ b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.rs @@ -31,7 +31,7 @@ impl Foo for bool {} impl Foo for bool {} fn main() { - Foo::::bar(&1i8); - Foo::::bar(&1u8); - Foo::::bar(&true); + Foo::::bar(&1i8); //~ ERROR is not satisfied + Foo::::bar(&1u8); //~ ERROR is not satisfied + Foo::::bar(&true); //~ ERROR is not satisfied } diff --git a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr index 4ea4adfcfe0fc..d5c4add34b526 100644 --- a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr +++ b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `i8: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:34:5 | -34 | Foo::::bar(&1i8); +34 | Foo::::bar(&1i8); //~ ERROR is not satisfied | ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i8` | = help: the following implementations were found: @@ -15,7 +15,7 @@ error[E0277]: the trait bound `i8: Foo` is not satisfied error[E0277]: the trait bound `u8: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:35:5 | -35 | Foo::::bar(&1u8); +35 | Foo::::bar(&1u8); //~ ERROR is not satisfied | ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `u8` | = help: the following implementations were found: @@ -28,7 +28,7 @@ error[E0277]: the trait bound `u8: Foo` is not satisfied error[E0277]: the trait bound `bool: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:36:5 | -36 | Foo::::bar(&true); +36 | Foo::::bar(&true); //~ ERROR is not satisfied | ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `bool` | = help: the following implementations were found: diff --git a/src/test/ui/did_you_mean/issue-40006.rs b/src/test/ui/did_you_mean/issue-40006.rs index d68c25faa8a84..62316b96db03f 100644 --- a/src/test/ui/did_you_mean/issue-40006.rs +++ b/src/test/ui/did_you_mean/issue-40006.rs @@ -8,22 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -impl X { +impl X { //~ ERROR cannot be made into an object +//~^ ERROR missing Y } struct S; -trait X { +trait X { //~ ERROR missing X() {} - fn xxx() { ### } - L = M; - Z = { 2 + 3 }; - ::Y (); + fn xxx() { ### } //~ ERROR missing + //~^ ERROR expected + L = M; //~ ERROR missing + Z = { 2 + 3 }; //~ ERROR expected one of + ::Y (); //~ ERROR expected one of } impl S { - pub hello_method(&self) { + pub hello_method(&self) { //~ ERROR missing println!("Hello"); } } diff --git a/src/test/ui/did_you_mean/issue-40006.stderr b/src/test/ui/did_you_mean/issue-40006.stderr index 3b7f32cf8904a..88d63cdbb5db4 100644 --- a/src/test/ui/did_you_mean/issue-40006.stderr +++ b/src/test/ui/did_you_mean/issue-40006.stderr @@ -1,65 +1,65 @@ error: missing `fn`, `type`, or `const` for impl-item declaration --> $DIR/issue-40006.rs:11:9 | -11 | impl X { +11 | impl X { //~ ERROR cannot be made into an object | _________^ -12 | | Y +12 | | //~^ ERROR missing +13 | | Y | |____^ missing `fn`, `type`, or `const` error: missing `fn`, `type`, or `const` for trait-item declaration - --> $DIR/issue-40006.rs:17:10 + --> $DIR/issue-40006.rs:18:10 | -17 | trait X { +18 | trait X { //~ ERROR missing | __________^ -18 | | X() {} +19 | | X() {} | |____^ missing `fn`, `type`, or `const` error: expected `[`, found `#` - --> $DIR/issue-40006.rs:19:17 + --> $DIR/issue-40006.rs:20:17 | -19 | fn xxx() { ### } +20 | fn xxx() { ### } //~ ERROR missing | ^ error: missing `fn`, `type`, or `const` for trait-item declaration - --> $DIR/issue-40006.rs:19:21 + --> $DIR/issue-40006.rs:20:21 | -19 | fn xxx() { ### } +20 | fn xxx() { ### } //~ ERROR missing | _____________________^ -20 | | L = M; +21 | | //~^ ERROR expected +22 | | L = M; //~ ERROR missing | |____^ missing `fn`, `type`, or `const` error: missing `fn`, `type`, or `const` for trait-item declaration - --> $DIR/issue-40006.rs:20:11 + --> $DIR/issue-40006.rs:22:11 | -20 | L = M; +22 | L = M; //~ ERROR missing | ___________^ -21 | | Z = { 2 + 3 }; +23 | | Z = { 2 + 3 }; //~ ERROR expected one of | |____^ missing `fn`, `type`, or `const` error: expected one of `const`, `extern`, `fn`, `type`, `unsafe`, or `}`, found `;` - --> $DIR/issue-40006.rs:21:18 + --> $DIR/issue-40006.rs:23:18 | -21 | Z = { 2 + 3 }; +23 | Z = { 2 + 3 }; //~ ERROR expected one of | ^ expected one of `const`, `extern`, `fn`, `type`, `unsafe`, or `}` here error: expected one of `!` or `::`, found `(` - --> $DIR/issue-40006.rs:22:9 + --> $DIR/issue-40006.rs:24:9 | -22 | ::Y (); - | -^ unexpected token - | | - | expected one of `!` or `::` here +24 | ::Y (); //~ ERROR expected one of + | ^ expected one of `!` or `::` here error: missing `fn`, `type`, or `const` for impl-item declaration - --> $DIR/issue-40006.rs:26:8 + --> $DIR/issue-40006.rs:28:8 | -26 | pub hello_method(&self) { +28 | pub hello_method(&self) { //~ ERROR missing | ^ missing `fn`, `type`, or `const` error[E0038]: the trait `X` cannot be made into an object --> $DIR/issue-40006.rs:11:6 | -11 | impl X { +11 | impl X { //~ ERROR cannot be made into an object | ^ the trait `X` cannot be made into an object | = note: method `xxx` has no receiver diff --git a/src/test/ui/did_you_mean/issue-40396.rs b/src/test/ui/did_you_mean/issue-40396.rs index 1eae180976ad3..eb62dc5408494 100644 --- a/src/test/ui/did_you_mean/issue-40396.rs +++ b/src/test/ui/did_you_mean/issue-40396.rs @@ -9,15 +9,16 @@ // except according to those terms. fn foo() { - println!("{:?}", (0..13).collect>()); + println!("{:?}", (0..13).collect>()); //~ ERROR chained comparison } fn bar() { - println!("{:?}", Vec::new()); + println!("{:?}", Vec::new()); //~ ERROR chained comparison } fn qux() { - println!("{:?}", (0..13).collect()); + println!("{:?}", (0..13).collect()); //~ ERROR chained comparison + //~^ ERROR chained comparison } fn main() {} diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr index 1a0c74dc01a09..8f4118b3ff05a 100644 --- a/src/test/ui/did_you_mean/issue-40396.stderr +++ b/src/test/ui/did_you_mean/issue-40396.stderr @@ -1,34 +1,38 @@ error: chained comparison operators require parentheses --> $DIR/issue-40396.rs:12:37 | -12 | println!("{:?}", (0..13).collect>()); +12 | println!("{:?}", (0..13).collect>()); //~ ERROR chained comparison | ^^^^^^^^ | = help: use `::<...>` instead of `<...>` if you meant to specify type arguments + = help: or use `(...)` if you meant to specify fn arguments error: chained comparison operators require parentheses --> $DIR/issue-40396.rs:16:25 | -16 | println!("{:?}", Vec::new()); +16 | println!("{:?}", Vec::new()); //~ ERROR chained comparison | ^^^^^^^ | = help: use `::<...>` instead of `<...>` if you meant to specify type arguments + = help: or use `(...)` if you meant to specify fn arguments error: chained comparison operators require parentheses --> $DIR/issue-40396.rs:20:37 | -20 | println!("{:?}", (0..13).collect()); +20 | println!("{:?}", (0..13).collect()); //~ ERROR chained comparison | ^^^^^^^^ | = help: use `::<...>` instead of `<...>` if you meant to specify type arguments + = help: or use `(...)` if you meant to specify fn arguments error: chained comparison operators require parentheses --> $DIR/issue-40396.rs:20:41 | -20 | println!("{:?}", (0..13).collect()); +20 | println!("{:?}", (0..13).collect()); //~ ERROR chained comparison | ^^^^^^ | = help: use `::<...>` instead of `<...>` if you meant to specify type arguments + = help: or use `(...)` if you meant to specify fn arguments error: aborting due to 4 previous errors diff --git a/src/test/ui/did_you_mean/issue-40823.rs b/src/test/ui/did_you_mean/issue-40823.rs index f4ae325727982..3b48cef19021b 100644 --- a/src/test/ui/did_you_mean/issue-40823.rs +++ b/src/test/ui/did_you_mean/issue-40823.rs @@ -10,5 +10,5 @@ fn main() { let mut buf = &[1, 2, 3, 4]; - buf.iter_mut(); + buf.iter_mut(); //~ ERROR cannot borrow immutable borrowed content } diff --git a/src/test/ui/did_you_mean/issue-40823.stderr b/src/test/ui/did_you_mean/issue-40823.stderr index 7daab09c09e37..0b71fc1d306a2 100644 --- a/src/test/ui/did_you_mean/issue-40823.stderr +++ b/src/test/ui/did_you_mean/issue-40823.stderr @@ -1,7 +1,7 @@ error[E0596]: cannot borrow immutable borrowed content `*buf` as mutable --> $DIR/issue-40823.rs:13:5 | -13 | buf.iter_mut(); +13 | buf.iter_mut(); //~ ERROR cannot borrow immutable borrowed content | ^^^ cannot borrow as mutable error: aborting due to previous error diff --git a/src/test/ui/did_you_mean/issue-41679.rs b/src/test/ui/did_you_mean/issue-41679.rs index 5091b9efc3422..98c909e212fdd 100644 --- a/src/test/ui/did_you_mean/issue-41679.rs +++ b/src/test/ui/did_you_mean/issue-41679.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let x = ~1; + let x = ~1; //~ ERROR can not be used as a unary operator } diff --git a/src/test/ui/did_you_mean/issue-41679.stderr b/src/test/ui/did_you_mean/issue-41679.stderr index 2abbbf65ba9b5..f1ccb3e6e141c 100644 --- a/src/test/ui/did_you_mean/issue-41679.stderr +++ b/src/test/ui/did_you_mean/issue-41679.stderr @@ -1,7 +1,7 @@ error: `~` can not be used as a unary operator --> $DIR/issue-41679.rs:12:13 | -12 | let x = ~1; +12 | let x = ~1; //~ ERROR can not be used as a unary operator | ^ did you mean `!`? | = help: use `!` instead of `~` if you meant to perform bitwise negation diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs b/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs index 7fe9950801227..ad5bedcefc2dd 100644 --- a/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.rs @@ -24,10 +24,12 @@ mod submodule { impl Demo { fn new_with_secret_two() -> Self { Self { secret_integer: 2, inocently_mispellable: () } + //~^ ERROR no field } fn new_with_secret_three() -> Self { Self { secret_integer: 3, egregiously_nonexistent_field: () } + //~^ ERROR no field } } @@ -38,6 +40,8 @@ fn main() { let demo = Demo::default(); let innocent_field_misaccess = demo.inocently_mispellable; + //~^ ERROR no field // note shouldn't suggest private fields let egregious_field_misaccess = demo.egregiously_nonexistent_field; + //~^ ERROR no field } diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr index e2bb7fbd9a895..d5dcef638471e 100644 --- a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -5,23 +5,23 @@ error[E0560]: struct `submodule::Demo` has no field named `inocently_mispellable | ^^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`? error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field` - --> $DIR/issue-42599_available_fields_note.rs:30:39 + --> $DIR/issue-42599_available_fields_note.rs:31:39 | -30 | Self { secret_integer: 3, egregiously_nonexistent_field: () } +31 | Self { secret_integer: 3, egregiously_nonexistent_field: () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field | = note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others error[E0609]: no field `inocently_mispellable` on type `submodule::Demo` - --> $DIR/issue-42599_available_fields_note.rs:40:41 + --> $DIR/issue-42599_available_fields_note.rs:42:41 | -40 | let innocent_field_misaccess = demo.inocently_mispellable; +42 | let innocent_field_misaccess = demo.inocently_mispellable; | ^^^^^^^^^^^^^^^^^^^^^ did you mean `innocently_misspellable`? error[E0609]: no field `egregiously_nonexistent_field` on type `submodule::Demo` - --> $DIR/issue-42599_available_fields_note.rs:42:42 + --> $DIR/issue-42599_available_fields_note.rs:45:42 | -42 | let egregious_field_misaccess = demo.egregiously_nonexistent_field; +45 | let egregious_field_misaccess = demo.egregiously_nonexistent_field; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown field | = note: available fields are: `favorite_integer`, `innocently_misspellable` diff --git a/src/test/ui/did_you_mean/issue-42764.rs b/src/test/ui/did_you_mean/issue-42764.rs index ecaeb7b1161f7..ff4bb428d5f5f 100644 --- a/src/test/ui/did_you_mean/issue-42764.rs +++ b/src/test/ui/did_you_mean/issue-42764.rs @@ -19,4 +19,5 @@ fn this_function_expects_a_double_option(d: DoubleOption) {} fn main() { let n: usize = 42; this_function_expects_a_double_option(n); + //~^ ERROR mismatched types } diff --git a/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs new file mode 100644 index 0000000000000..7b877523e35cc --- /dev/null +++ b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Example { Ex(String), NotEx } + +fn result_test() { + let x = Option(1); //~ ERROR expected function, found enum + + if let Option(_) = x { //~ ERROR expected tuple struct/variant, found enum + println!("It is OK."); + } + + let y = Example::Ex(String::from("test")); + + if let Example(_) = y { //~ ERROR expected tuple struct/variant, found enum + println!("It is OK."); + } +} + +fn main() {} diff --git a/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr new file mode 100644 index 0000000000000..5390e541fb714 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr @@ -0,0 +1,32 @@ +error[E0423]: expected function, found enum `Option` + --> $DIR/issue-43871-enum-instead-of-variant.rs:14:13 + | +14 | let x = Option(1); //~ ERROR expected function, found enum + | ^^^^^^ + | + = note: did you mean to use one of the following variants? + - `std::prelude::v1::Option::None` + - `std::prelude::v1::Option::Some` + +error[E0532]: expected tuple struct/variant, found enum `Option` + --> $DIR/issue-43871-enum-instead-of-variant.rs:16:12 + | +16 | if let Option(_) = x { //~ ERROR expected tuple struct/variant, found enum + | ^^^^^^ + | + = note: did you mean to use one of the following variants? + - `std::prelude::v1::Option::None` + - `std::prelude::v1::Option::Some` + +error[E0532]: expected tuple struct/variant, found enum `Example` + --> $DIR/issue-43871-enum-instead-of-variant.rs:22:12 + | +22 | if let Example(_) = y { //~ ERROR expected tuple struct/variant, found enum + | ^^^^^^^ + | + = note: did you mean to use one of the following variants? + - `Example::Ex` + - `Example::NotEx` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/did_you_mean/recursion_limit.rs b/src/test/ui/did_you_mean/recursion_limit.rs index becb81b1fff7e..2d27f167a03a4 100644 --- a/src/test/ui/did_you_mean/recursion_limit.rs +++ b/src/test/ui/did_you_mean/recursion_limit.rs @@ -41,5 +41,5 @@ enum N { N(usize) } fn is_send() { } fn main() { - is_send::(); + is_send::(); //~ ERROR overflow evaluating the requirement } diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr index d157c5de6c7f5..7fac604ba49d7 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -1,7 +1,7 @@ error[E0275]: overflow evaluating the requirement `K: std::marker::Send` --> $DIR/recursion_limit.rs:44:5 | -44 | is_send::(); +44 | is_send::(); //~ ERROR overflow evaluating the requirement | ^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="20"]` attribute to your crate diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.rs b/src/test/ui/did_you_mean/recursion_limit_deref.rs index ebc56c94adf84..3e261ec636c08 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.rs +++ b/src/test/ui/did_you_mean/recursion_limit_deref.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//~^^^^^^^^^^ ERROR reached the recursion limit + // Test that the recursion limit can be changed and that the compiler // suggests a fix. In this case, we have a long chain of Deref impls // which will cause an overflow during the autoderef loop. @@ -57,6 +59,7 @@ link!(K, Bottom); fn main() { let t = Top::new(); - let x: &Bottom = &t; + let x: &Bottom = &t; //~ ERROR mismatched types + //~^ error recursion limit } diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr index 57b28d0373622..951b0b1058060 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -1,7 +1,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing I - --> $DIR/recursion_limit_deref.rs:60:22 + --> $DIR/recursion_limit_deref.rs:62:22 | -60 | let x: &Bottom = &t; +62 | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ deref recursion limit reached | = help: consider adding a `#[recursion_limit="20"]` attribute to your crate @@ -11,9 +11,9 @@ error[E0055]: reached the recursion limit while auto-dereferencing I = help: consider adding a `#[recursion_limit="20"]` attribute to your crate error[E0308]: mismatched types - --> $DIR/recursion_limit_deref.rs:60:22 + --> $DIR/recursion_limit_deref.rs:62:22 | -60 | let x: &Bottom = &t; +62 | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ expected struct `Bottom`, found struct `Top` | = note: expected type `&Bottom` diff --git a/src/test/ui/did_you_mean/recursion_limit_macro.rs b/src/test/ui/did_you_mean/recursion_limit_macro.rs index 9fb82b730c9b3..16d07f369907b 100644 --- a/src/test/ui/did_you_mean/recursion_limit_macro.rs +++ b/src/test/ui/did_you_mean/recursion_limit_macro.rs @@ -17,7 +17,7 @@ macro_rules! recurse { () => { }; - ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; + ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; //~ ERROR recursion limit } fn main() { diff --git a/src/test/ui/did_you_mean/recursion_limit_macro.stderr b/src/test/ui/did_you_mean/recursion_limit_macro.stderr index 19aac1f77e7c1..24e223c797b21 100644 --- a/src/test/ui/did_you_mean/recursion_limit_macro.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_macro.stderr @@ -1,7 +1,7 @@ error: recursion limit reached while expanding the macro `recurse` --> $DIR/recursion_limit_macro.rs:20:31 | -20 | ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; +20 | ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; //~ ERROR recursion limit | ^^^^^^^^^^^^^^^^^^^ ... 24 | recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9); diff --git a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs index 11757abae9c97..76bc971e115f7 100644 --- a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs +++ b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs @@ -9,6 +9,7 @@ // except according to those terms. fn main() { - let _: &Copy + 'static; - let _: &'static Copy + 'static; + let _: &Copy + 'static; //~ ERROR expected a path + //~^ ERROR cannot be made into an object + let _: &'static Copy + 'static; //~ ERROR expected a path } diff --git a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 498255cb9ea37..325a19eee140b 100644 --- a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -1,19 +1,19 @@ error[E0178]: expected a path on the left-hand side of `+`, not `&Copy` --> $DIR/trait-object-reference-without-parens-suggestion.rs:12:12 | -12 | let _: &Copy + 'static; +12 | let _: &Copy + 'static; //~ ERROR expected a path | ^^^^^^^^^^^^^^^ help: try adding parentheses: `&(Copy + 'static)` error[E0178]: expected a path on the left-hand side of `+`, not `&'static Copy` - --> $DIR/trait-object-reference-without-parens-suggestion.rs:13:12 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:14:12 | -13 | let _: &'static Copy + 'static; +14 | let _: &'static Copy + 'static; //~ ERROR expected a path | ^^^^^^^^^^^^^^^^^^^^^^^ help: try adding parentheses: `&'static (Copy + 'static)` error[E0038]: the trait `std::marker::Copy` cannot be made into an object --> $DIR/trait-object-reference-without-parens-suggestion.rs:12:12 | -12 | let _: &Copy + 'static; +12 | let _: &Copy + 'static; //~ ERROR expected a path | ^^^^^ the trait `std::marker::Copy` cannot be made into an object | = note: the trait cannot require that `Self : Sized` diff --git a/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs b/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs index 5e200dbdbfa01..f76c2251f8c70 100644 --- a/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs +++ b/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs @@ -36,20 +36,23 @@ fn main() { dt = Dt("dt", &c_long); dr = Dr("dr", &c_long); // Error: destructor order imprecisely modelled - dt = Dt("dt", &c); //~ ERROR `c` does not live long enough - dr = Dr("dr", &c); //~ ERROR `c` does not live long enough + dt = Dt("dt", &c); + dr = Dr("dr", &c); // No error: Drop impl asserts .1 (A and &'a _) are not accessed pt = Pt("pt", &c, &c_long); pr = Pr("pr", &c, &c_long); // Error: Drop impl's assertion does not apply to `B` nor `&'b _` - pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough - pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough + pt = Pt("pt", &c_long, &c); + pr = Pr("pr", &c_long, &c); // No error: St and Sr have no destructor. st = St("st", &c); sr = Sr("sr", &c); println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0)); -} +}//~ ERROR `c` does not live long enough +//~^ ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough diff --git a/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr index 62ce3209c919d..43d5294c93a05 100644 --- a/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr @@ -1,10 +1,10 @@ error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-extern-crate.rs:55:1 | -39 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough +39 | dt = Dt("dt", &c); | - borrow occurs here ... -55 | } +55 | }//~ ERROR `c` does not live long enough | ^ `c` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created @@ -12,10 +12,10 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-extern-crate.rs:55:1 | -40 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough +40 | dr = Dr("dr", &c); | - borrow occurs here ... -55 | } +55 | }//~ ERROR `c` does not live long enough | ^ `c` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created @@ -23,10 +23,10 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-extern-crate.rs:55:1 | -47 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough +47 | pt = Pt("pt", &c_long, &c); | - borrow occurs here ... -55 | } +55 | }//~ ERROR `c` does not live long enough | ^ `c` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created @@ -34,10 +34,10 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-extern-crate.rs:55:1 | -48 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough +48 | pr = Pr("pr", &c_long, &c); | - borrow occurs here ... -55 | } +55 | }//~ ERROR `c` does not live long enough | ^ `c` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.rs b/src/test/ui/dropck/dropck-eyepatch-reorder.rs index 68b0ff3b5f096..95ee45a6117c7 100644 --- a/src/test/ui/dropck/dropck-eyepatch-reorder.rs +++ b/src/test/ui/dropck/dropck-eyepatch-reorder.rs @@ -54,16 +54,16 @@ fn main() { dt = Dt("dt", &c_long); dr = Dr("dr", &c_long); // Error: destructor order imprecisely modelled - dt = Dt("dt", &c); //~ ERROR `c` does not live long enough - dr = Dr("dr", &c); //~ ERROR `c` does not live long enough + dt = Dt("dt", &c); + dr = Dr("dr", &c); // No error: Drop impl asserts .1 (A and &'a _) are not accessed pt = Pt("pt", &c, &c_long); pr = Pr("pr", &c, &c_long); // Error: Drop impl's assertion does not apply to `B` nor `&'b _` - pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough - pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough + pt = Pt("pt", &c_long, &c); + pr = Pr("pr", &c_long, &c); // No error: St and Sr have no destructor. st = St("st", &c); @@ -71,3 +71,7 @@ fn main() { println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0)); } +//~^ ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.stderr b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr index d94808bbcb6d0..1ca456c7ba38b 100644 --- a/src/test/ui/dropck/dropck-eyepatch-reorder.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr @@ -1,7 +1,7 @@ error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-reorder.rs:73:1 | -57 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough +57 | dt = Dt("dt", &c); | - borrow occurs here ... 73 | } @@ -12,7 +12,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-reorder.rs:73:1 | -58 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough +58 | dr = Dr("dr", &c); | - borrow occurs here ... 73 | } @@ -23,7 +23,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-reorder.rs:73:1 | -65 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough +65 | pt = Pt("pt", &c_long, &c); | - borrow occurs here ... 73 | } @@ -34,7 +34,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch-reorder.rs:73:1 | -66 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough +66 | pr = Pr("pr", &c_long, &c); | - borrow occurs here ... 73 | } diff --git a/src/test/ui/dropck/dropck-eyepatch.rs b/src/test/ui/dropck/dropck-eyepatch.rs index e442fade19730..de94954e9218b 100644 --- a/src/test/ui/dropck/dropck-eyepatch.rs +++ b/src/test/ui/dropck/dropck-eyepatch.rs @@ -77,16 +77,16 @@ fn main() { dt = Dt("dt", &c_long); dr = Dr("dr", &c_long); // Error: destructor order imprecisely modelled - dt = Dt("dt", &c); //~ ERROR `c` does not live long enough - dr = Dr("dr", &c); //~ ERROR `c` does not live long enough + dt = Dt("dt", &c); + dr = Dr("dr", &c); // No error: Drop impl asserts .1 (A and &'a _) are not accessed pt = Pt("pt", &c, &c_long); pr = Pr("pr", &c, &c_long); // Error: Drop impl's assertion does not apply to `B` nor `&'b _` - pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough - pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough + pt = Pt("pt", &c_long, &c); + pr = Pr("pr", &c_long, &c); // No error: St and Sr have no destructor. st = St("st", &c); @@ -94,3 +94,7 @@ fn main() { println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0)); } +//~^ ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough +//~| ERROR `c` does not live long enough diff --git a/src/test/ui/dropck/dropck-eyepatch.stderr b/src/test/ui/dropck/dropck-eyepatch.stderr index 811eee0e85f15..d41ff37411976 100644 --- a/src/test/ui/dropck/dropck-eyepatch.stderr +++ b/src/test/ui/dropck/dropck-eyepatch.stderr @@ -1,7 +1,7 @@ error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch.rs:96:1 | -80 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough +80 | dt = Dt("dt", &c); | - borrow occurs here ... 96 | } @@ -12,7 +12,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch.rs:96:1 | -81 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough +81 | dr = Dr("dr", &c); | - borrow occurs here ... 96 | } @@ -23,7 +23,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch.rs:96:1 | -88 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough +88 | pt = Pt("pt", &c_long, &c); | - borrow occurs here ... 96 | } @@ -34,7 +34,7 @@ error[E0597]: `c` does not live long enough error[E0597]: `c` does not live long enough --> $DIR/dropck-eyepatch.rs:96:1 | -89 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough +89 | pr = Pr("pr", &c_long, &c); | - borrow occurs here ... 96 | } diff --git a/src/test/ui/e0119/auxiliary/complex_impl_support.rs b/src/test/ui/e0119/auxiliary/complex_impl_support.rs new file mode 100644 index 0000000000000..b30db9660992c --- /dev/null +++ b/src/test/ui/e0119/auxiliary/complex_impl_support.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::marker::PhantomData; + +pub trait External {} + +pub struct M<'a, 'b, 'c, T, U, V> { + a: PhantomData<&'a ()>, + b: PhantomData<&'b ()>, + c: PhantomData<&'c ()>, + d: PhantomData, + e: PhantomData, + f: PhantomData, +} + +impl<'a, 'b, 'c, T, U, V, W> External for (T, M<'a, 'b, 'c, Box, V, W>) +where + 'b: 'a, + T: 'a, + U: (FnOnce(T) -> V) + 'static, + V: Iterator + Clone, + W: std::ops::Add, + W::Output: Copy, +{} diff --git a/src/test/ui/e0119/auxiliary/issue_23563_a.rs b/src/test/ui/e0119/auxiliary/issue_23563_a.rs new file mode 100644 index 0000000000000..57a0da0248d5c --- /dev/null +++ b/src/test/ui/e0119/auxiliary/issue_23563_a.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ref: https://github.com/rust-lang/rust/issues/23563#issuecomment-260751672 + +pub trait LolTo { + fn convert_to(&self) -> T; +} + +pub trait LolInto: Sized { + fn convert_into(self) -> T; +} + +pub trait LolFrom { + fn from(T) -> Self; +} + +impl<'a, T: ?Sized, U> LolInto for &'a T where T: LolTo { + fn convert_into(self) -> U { + self.convert_to() + } +} + +impl LolFrom for U where T: LolInto { + fn from(t: T) -> U { + t.convert_into() + } +} diff --git a/src/test/ui/e0119/complex-impl.rs b/src/test/ui/e0119/complex-impl.rs new file mode 100644 index 0000000000000..b8b30a2841421 --- /dev/null +++ b/src/test/ui/e0119/complex-impl.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:complex_impl_support.rs + +extern crate complex_impl_support; + +use complex_impl_support::{External, M}; + +struct Q; + +impl External for (Q, R) {} //~ ERROR must be used +//~^ ERROR conflicting implementations of trait + +fn main() {} diff --git a/src/test/ui/e0119/complex-impl.stderr b/src/test/ui/e0119/complex-impl.stderr new file mode 100644 index 0000000000000..e4f8020145c65 --- /dev/null +++ b/src/test/ui/e0119/complex-impl.stderr @@ -0,0 +1,18 @@ +error[E0119]: conflicting implementations of trait `complex_impl_support::External` for type `(Q, complex_impl_support::M<'_, '_, '_, std::boxed::Box<_>, _, _>)`: + --> $DIR/complex-impl.rs:19:1 + | +19 | impl External for (Q, R) {} //~ ERROR must be used + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `complex_impl_support`: + - impl<'a, 'b, 'c, T, U, V, W> complex_impl_support::External for (T, complex_impl_support::M<'a, 'b, 'c, std::boxed::Box, V, W>) + where >::Output == V, ::Item == T, 'b : 'a, T : 'a, U: std::ops::FnOnce<(T,)>, U : 'static, V: std::iter::Iterator, V: std::clone::Clone, W: std::ops::Add, ::Output: std::marker::Copy; + +error[E0210]: type parameter `R` must be used as the type parameter for some local type (e.g. `MyStruct`); only traits defined in the current crate can be implemented for a type parameter + --> $DIR/complex-impl.rs:19:1 + | +19 | impl External for (Q, R) {} //~ ERROR must be used + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/e0119/conflict-with-std.rs b/src/test/ui/e0119/conflict-with-std.rs new file mode 100644 index 0000000000000..ed9033ad53d56 --- /dev/null +++ b/src/test/ui/e0119/conflict-with-std.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(try_from)] + +use std::marker::PhantomData; +use std::convert::{TryFrom, AsRef}; + +struct Q; +impl AsRef for Box { //~ ERROR conflicting implementations + fn as_ref(&self) -> &Q { + &**self + } +} + +struct S; +impl From for S { //~ ERROR conflicting implementations + fn from(s: S) -> S { + s + } +} + +struct X; +impl TryFrom for X { //~ ERROR conflicting implementations + type Error = (); + fn try_from(u: X) -> Result { + Ok(u) + } +} + +fn main() {} diff --git a/src/test/ui/e0119/conflict-with-std.stderr b/src/test/ui/e0119/conflict-with-std.stderr new file mode 100644 index 0000000000000..21f2dd05b4d79 --- /dev/null +++ b/src/test/ui/e0119/conflict-with-std.stderr @@ -0,0 +1,44 @@ +error[E0119]: conflicting implementations of trait `std::convert::AsRef` for type `std::boxed::Box`: + --> $DIR/conflict-with-std.rs:17:1 + | +17 | / impl AsRef for Box { //~ ERROR conflicting implementations +18 | | fn as_ref(&self) -> &Q { +19 | | &**self +20 | | } +21 | | } + | |_^ + | + = note: conflicting implementation in crate `alloc`: + - impl std::convert::AsRef for std::boxed::Box + where T: ?Sized; + +error[E0119]: conflicting implementations of trait `std::convert::From` for type `S`: + --> $DIR/conflict-with-std.rs:24:1 + | +24 | / impl From for S { //~ ERROR conflicting implementations +25 | | fn from(s: S) -> S { +26 | | s +27 | | } +28 | | } + | |_^ + | + = note: conflicting implementation in crate `core`: + - impl std::convert::From for T; + +error[E0119]: conflicting implementations of trait `std::convert::TryFrom` for type `X`: + --> $DIR/conflict-with-std.rs:31:1 + | +31 | / impl TryFrom for X { //~ ERROR conflicting implementations +32 | | type Error = (); +33 | | fn try_from(u: X) -> Result { +34 | | Ok(u) +35 | | } +36 | | } + | |_^ + | + = note: conflicting implementation in crate `core`: + - impl std::convert::TryFrom for T + where T: std::convert::From; + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/e0119/issue-23563.rs b/src/test/ui/e0119/issue-23563.rs new file mode 100644 index 0000000000000..c6d03a4cfc0a7 --- /dev/null +++ b/src/test/ui/e0119/issue-23563.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue_23563_a.rs + +// Ref: https://github.com/rust-lang/rust/issues/23563#issuecomment-260751672 + +extern crate issue_23563_a as a; + +use a::LolFrom; +use a::LolInto; +use a::LolTo; + +struct LocalType(Option); + +impl<'a, T> LolFrom<&'a [T]> for LocalType { //~ ERROR conflicting implementations of trait + fn from(_: &'a [T]) -> LocalType { LocalType(None) } +} + +impl LolInto> for LocalType { + fn convert_into(self) -> LocalType { + self + } +} + +impl LolTo> for [u8] { + fn convert_to(&self) -> LocalType { + LocalType(None) + } +} + +fn main() {} diff --git a/src/test/ui/e0119/issue-23563.stderr b/src/test/ui/e0119/issue-23563.stderr new file mode 100644 index 0000000000000..9dddf193063fb --- /dev/null +++ b/src/test/ui/e0119/issue-23563.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `a::LolFrom<&[_]>` for type `LocalType<_>`: + --> $DIR/issue-23563.rs:23:1 + | +23 | / impl<'a, T> LolFrom<&'a [T]> for LocalType { //~ ERROR conflicting implementations of trait +24 | | fn from(_: &'a [T]) -> LocalType { LocalType(None) } +25 | | } + | |_^ + | + = note: conflicting implementation in crate `issue_23563_a`: + - impl a::LolFrom for U + where T: a::LolInto; + +error: aborting due to previous error + diff --git a/src/test/ui/e0119/issue-27403.rs b/src/test/ui/e0119/issue-27403.rs new file mode 100644 index 0000000000000..98953153faf96 --- /dev/null +++ b/src/test/ui/e0119/issue-27403.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct GenX { + inner: S, +} + +impl Into for GenX { //~ ERROR conflicting implementations + fn into(self) -> S { + self.inner + } +} + +fn main() {} diff --git a/src/test/ui/e0119/issue-27403.stderr b/src/test/ui/e0119/issue-27403.stderr new file mode 100644 index 0000000000000..68d7235f6aaee --- /dev/null +++ b/src/test/ui/e0119/issue-27403.stderr @@ -0,0 +1,16 @@ +error[E0119]: conflicting implementations of trait `std::convert::Into<_>` for type `GenX<_>`: + --> $DIR/issue-27403.rs:15:1 + | +15 | / impl Into for GenX { //~ ERROR conflicting implementations +16 | | fn into(self) -> S { +17 | | self.inner +18 | | } +19 | | } + | |_^ + | + = note: conflicting implementation in crate `core`: + - impl std::convert::Into for T + where U: std::convert::From; + +error: aborting due to previous error + diff --git a/src/test/ui/e0119/issue-28981.rs b/src/test/ui/e0119/issue-28981.rs new file mode 100644 index 0000000000000..8a52464ff50a4 --- /dev/null +++ b/src/test/ui/e0119/issue-28981.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +struct Foo; + +impl Deref for Foo { } //~ ERROR must be used +//~^ ERROR conflicting implementations + +fn main() {} diff --git a/src/test/ui/e0119/issue-28981.stderr b/src/test/ui/e0119/issue-28981.stderr new file mode 100644 index 0000000000000..aac9f7ae964a4 --- /dev/null +++ b/src/test/ui/e0119/issue-28981.stderr @@ -0,0 +1,18 @@ +error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `&_`: + --> $DIR/issue-28981.rs:15:1 + | +15 | impl Deref for Foo { } //~ ERROR must be used + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl<'a, T> std::ops::Deref for &'a T + where T: ?Sized; + +error[E0210]: type parameter `Foo` must be used as the type parameter for some local type (e.g. `MyStruct`); only traits defined in the current crate can be implemented for a type parameter + --> $DIR/issue-28981.rs:15:1 + | +15 | impl Deref for Foo { } //~ ERROR must be used + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/e0119/so-37347311.rs b/src/test/ui/e0119/so-37347311.rs new file mode 100644 index 0000000000000..933cdb3cd533d --- /dev/null +++ b/src/test/ui/e0119/so-37347311.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ref: https://stackoverflow.com/q/37347311 + +trait Storage { + type Error; +} + +enum MyError { + StorageProblem(S::Error), +} + +impl From for MyError { //~ ERROR conflicting implementations + fn from(error: S::Error) -> MyError { + MyError::StorageProblem(error) + } +} + +fn main() {} diff --git a/src/test/ui/e0119/so-37347311.stderr b/src/test/ui/e0119/so-37347311.stderr new file mode 100644 index 0000000000000..351c0e1bbb626 --- /dev/null +++ b/src/test/ui/e0119/so-37347311.stderr @@ -0,0 +1,15 @@ +error[E0119]: conflicting implementations of trait `std::convert::From>` for type `MyError<_>`: + --> $DIR/so-37347311.rs:21:1 + | +21 | / impl From for MyError { //~ ERROR conflicting implementations +22 | | fn from(error: S::Error) -> MyError { +23 | | MyError::StorageProblem(error) +24 | | } +25 | | } + | |_^ + | + = note: conflicting implementation in crate `core`: + - impl std::convert::From for T; + +error: aborting due to previous error + diff --git a/src/test/ui/fmt/format-string-error.stderr b/src/test/ui/fmt/format-string-error.stderr index 58b392f0b8d65..1c775929cf452 100644 --- a/src/test/ui/fmt/format-string-error.stderr +++ b/src/test/ui/fmt/format-string-error.stderr @@ -5,7 +5,7 @@ error: invalid format string: expected `'}'` but string was terminated | ^^^^^^^^^^^^^^ | = note: if you intended to print `{`, you can escape it using `{{` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: invalid format string: unmatched `}` found --> $DIR/format-string-error.rs:14:5 @@ -14,7 +14,7 @@ error: invalid format string: unmatched `}` found | ^^^^^^^^^^^^^^ | = note: if you intended to print `}`, you can escape it using `}}` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/fmt/send-sync.rs b/src/test/ui/fmt/send-sync.rs new file mode 100644 index 0000000000000..3f13fd2e4913a --- /dev/null +++ b/src/test/ui/fmt/send-sync.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn send(_: T) {} +fn sync(_: T) {} + +fn main() { + // `Cell` is not `Sync`, so `&Cell` is neither `Sync` nor `Send`, + // `std::fmt::Arguments` used to forget this... + let c = std::cell::Cell::new(42); + send(format_args!("{:?}", c)); //~ ERROR Sync` is not satisfied + sync(format_args!("{:?}", c)); //~ ERROR Sync` is not satisfied +} diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr new file mode 100644 index 0000000000000..9e0e563c35f65 --- /dev/null +++ b/src/test/ui/fmt/send-sync.stderr @@ -0,0 +1,34 @@ +error[E0277]: the trait bound `*mut std::ops::Fn() + 'static: std::marker::Sync` is not satisfied in `[std::fmt::ArgumentV1<'_>]` + --> $DIR/send-sync.rs:18:5 + | +18 | send(format_args!("{:?}", c)); //~ ERROR Sync` is not satisfied + | ^^^^ `*mut std::ops::Fn() + 'static` cannot be shared between threads safely + | + = help: within `[std::fmt::ArgumentV1<'_>]`, the trait `std::marker::Sync` is not implemented for `*mut std::ops::Fn() + 'static` + = note: required because it appears within the type `std::marker::PhantomData<*mut std::ops::Fn() + 'static>` + = note: required because it appears within the type `core::fmt::Void` + = note: required because it appears within the type `&core::fmt::Void` + = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` + = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` + = note: required because of the requirements on the impl of `std::marker::Send` for `&[std::fmt::ArgumentV1<'_>]` + = note: required because it appears within the type `std::fmt::Arguments<'_>` + = note: required by `send` + +error[E0277]: the trait bound `*mut std::ops::Fn() + 'static: std::marker::Sync` is not satisfied in `std::fmt::Arguments<'_>` + --> $DIR/send-sync.rs:19:5 + | +19 | sync(format_args!("{:?}", c)); //~ ERROR Sync` is not satisfied + | ^^^^ `*mut std::ops::Fn() + 'static` cannot be shared between threads safely + | + = help: within `std::fmt::Arguments<'_>`, the trait `std::marker::Sync` is not implemented for `*mut std::ops::Fn() + 'static` + = note: required because it appears within the type `std::marker::PhantomData<*mut std::ops::Fn() + 'static>` + = note: required because it appears within the type `core::fmt::Void` + = note: required because it appears within the type `&core::fmt::Void` + = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` + = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` + = note: required because it appears within the type `&[std::fmt::ArgumentV1<'_>]` + = note: required because it appears within the type `std::fmt::Arguments<'_>` + = note: required by `sync` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/generator/ref-escapes-but-not-over-yield.rs b/src/test/ui/generator/ref-escapes-but-not-over-yield.rs index 87edbb22baae1..299106bd552f2 100644 --- a/src/test/ui/generator/ref-escapes-but-not-over-yield.rs +++ b/src/test/ui/generator/ref-escapes-but-not-over-yield.rs @@ -21,8 +21,8 @@ fn foo(x: &i32) { let mut b = move || { yield(); let b = 5; - a = &b; //~ ERROR - }; + a = &b; + }; //~ ERROR } fn main() { } diff --git a/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr b/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr index e30d28c2db83b..7310e54925ff8 100644 --- a/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr +++ b/src/test/ui/generator/ref-escapes-but-not-over-yield.stderr @@ -1,9 +1,9 @@ error[E0597]: `b` does not live long enough --> $DIR/ref-escapes-but-not-over-yield.rs:25:5 | -24 | a = &b; //~ ERROR +24 | a = &b; | - borrow occurs here -25 | }; +25 | }; //~ ERROR | ^ `b` dropped here while still borrowed 26 | } | - borrowed value needs to live until here diff --git a/src/test/ui/generator/yield-while-local-borrowed.rs b/src/test/ui/generator/yield-while-local-borrowed.rs index d21c86e88681e..504f3e8739f24 100644 --- a/src/test/ui/generator/yield-while-local-borrowed.rs +++ b/src/test/ui/generator/yield-while-local-borrowed.rs @@ -19,7 +19,7 @@ fn borrow_local_inline() { // (This error occurs because the region shows up in the type of // `b` and gets extended by region inference.) let mut b = move || { - let a = &3; //~ ERROR + let a = &3; yield(); println!("{}", a); }; diff --git a/src/test/compile-fail/impl-trait/auto-trait-leak.rs b/src/test/ui/impl-trait/auto-trait-leak.rs similarity index 100% rename from src/test/compile-fail/impl-trait/auto-trait-leak.rs rename to src/test/ui/impl-trait/auto-trait-leak.rs diff --git a/src/test/ui/impl-trait/auto-trait-leak.stderr b/src/test/ui/impl-trait/auto-trait-leak.stderr new file mode 100644 index 0000000000000..1c03e9d852645 --- /dev/null +++ b/src/test/ui/impl-trait/auto-trait-leak.stderr @@ -0,0 +1,52 @@ +error[E0277]: the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied in `impl std::ops::Fn<(i32,)>` + --> $DIR/auto-trait-leak.rs:27:5 + | +27 | send(before()); + | ^^^^ `std::rc::Rc>` cannot be sent between threads safely + | + = help: within `impl std::ops::Fn<(i32,)>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc>` + = note: required because it appears within the type `[closure@$DIR/auto-trait-leak.rs:21:5: 21:22 p:std::rc::Rc>]` + = note: required because it appears within the type `impl std::ops::Fn<(i32,)>` + = note: required by `send` + +error[E0277]: the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied in `impl std::ops::Fn<(i32,)>` + --> $DIR/auto-trait-leak.rs:34:5 + | +34 | send(after()); + | ^^^^ `std::rc::Rc>` cannot be sent between threads safely + | + = help: within `impl std::ops::Fn<(i32,)>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc>` + = note: required because it appears within the type `[closure@$DIR/auto-trait-leak.rs:46:5: 46:22 p:std::rc::Rc>]` + = note: required because it appears within the type `impl std::ops::Fn<(i32,)>` + = note: required by `send` + +error[E0391]: unsupported cyclic reference between types/traits detected + --> $DIR/auto-trait-leak.rs:52:1 + | +52 | fn cycle1() -> impl Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic reference + | +note: the cycle begins when processing `cycle1`... + --> $DIR/auto-trait-leak.rs:52:1 + | +52 | fn cycle1() -> impl Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which then requires processing `cycle2::{{impl-Trait}}`... + --> $DIR/auto-trait-leak.rs:63:16 + | +63 | fn cycle2() -> impl Clone { + | ^^^^^^^^^^ +note: ...which then requires processing `cycle2`... + --> $DIR/auto-trait-leak.rs:63:1 + | +63 | fn cycle2() -> impl Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which then requires processing `cycle1::{{impl-Trait}}`... + --> $DIR/auto-trait-leak.rs:52:16 + | +52 | fn cycle1() -> impl Clone { + | ^^^^^^^^^^ + = note: ...which then again requires processing `cycle1`, completing the cycle. + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/impl-trait/equality.rs b/src/test/ui/impl-trait/equality.rs index 96db53ad2e46e..36df4f0eb4d46 100644 --- a/src/test/ui/impl-trait/equality.rs +++ b/src/test/ui/impl-trait/equality.rs @@ -32,7 +32,7 @@ fn sum_to(n: u32) -> impl Foo { 0 } else { n + sum_to(n - 1) - //~^ ERROR no implementation for `u32 + impl Foo` + //~^ ERROR the trait bound `u32: std::ops::Add` is not satisfied } } diff --git a/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.rs b/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.rs index 0bb944edb9d84..9120cdab59861 100644 --- a/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.rs +++ b/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.rs @@ -30,5 +30,4 @@ fn main() { f1.foo(1usize); //~^ error: method named `foo` found for type `Bar` in the current scope //~| help: items from traits can only be used if the trait is implemented and in scope - //~| help: candidate #1: `Foo` } diff --git a/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.stderr b/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.stderr index 3bc281726ef3a..297694568493d 100644 --- a/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.stderr +++ b/src/test/ui/impl-trait/issue-21659-show-relevant-trait-impls-3.stderr @@ -1,6 +1,9 @@ error[E0599]: no method named `foo` found for type `Bar` in the current scope --> $DIR/issue-21659-show-relevant-trait-impls-3.rs:30:8 | +23 | struct Bar; + | ----------- method `foo` not found for this +... 30 | f1.foo(1usize); | ^^^ | diff --git a/src/test/ui/impl-trait/method-suggestion-no-duplication.rs b/src/test/ui/impl-trait/method-suggestion-no-duplication.rs index 390b8f07b2fbd..15ddadf4c513d 100644 --- a/src/test/ui/impl-trait/method-suggestion-no-duplication.rs +++ b/src/test/ui/impl-trait/method-suggestion-no-duplication.rs @@ -18,8 +18,5 @@ fn foo(f: F) where F: FnMut(Foo) {} fn main() { foo(|s| s.is_empty()); //~^ ERROR no method named `is_empty` found - //~^^ HELP #1: `std::iter::ExactSizeIterator` - //~^^^ HELP #2: `core::slice::SliceExt` - //~^^^^ HELP #3: `core::str::StrExt` - //~^^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them: + //~| HELP items from traits can only be used if the trait is implemented and in scope } diff --git a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr index d3dbb77490b87..52d3931011a77 100644 --- a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr +++ b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr @@ -1,6 +1,9 @@ error[E0599]: no method named `is_empty` found for type `Foo` in the current scope --> $DIR/method-suggestion-no-duplication.rs:19:15 | +14 | struct Foo; + | ----------- method `is_empty` not found for this +... 19 | foo(|s| s.is_empty()); | ^^^^^^^^ | diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.rs b/src/test/ui/impl-trait/no-method-suggested-traits.rs index 15891b00028dc..d9866772bdd37 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.rs +++ b/src/test/ui/impl-trait/no-method-suggested-traits.rs @@ -11,7 +11,12 @@ // aux-build:no_method_suggested_traits.rs extern crate no_method_suggested_traits; -struct Foo; +struct Foo; //~ HELP perhaps add a `use` for it +//~^ HELP perhaps add a `use` for it +//~| HELP perhaps add a `use` for it +//~| HELP perhaps add a `use` for it +//~| HELP perhaps add a `use` for one of them +//~| HELP perhaps add a `use` for one of them enum Bar { X } mod foo { @@ -31,95 +36,65 @@ fn main() { 1u32.method(); - //~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them - //~| ERROR no method named - //~| HELP `use foo::Bar;` - //~| HELP `use no_method_suggested_traits::foo::PubPub;` + //~^ ERROR no method named + //~|items from traits can only be used if the trait is in scope std::rc::Rc::new(&mut Box::new(&1u32)).method(); - //~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them - //~| ERROR no method named - //~| HELP `use foo::Bar;` - //~| HELP `use no_method_suggested_traits::foo::PubPub;` + //~^items from traits can only be used if the trait is in scope + //~| ERROR no method named `method` found for type 'a'.method(); //~^ ERROR no method named - //~| HELP the following trait is implemented but not in scope, perhaps add a `use` for it: - //~| HELP `use foo::Bar;` + //~| HELP items from traits can only be used if the trait is in scope std::rc::Rc::new(&mut Box::new(&'a')).method(); //~^ ERROR no method named - //~| HELP the following trait is implemented but not in scope, perhaps add a `use` for it: - //~| HELP `use foo::Bar;` + //~| HELP items from traits can only be used if the trait is in scope 1i32.method(); //~^ ERROR no method named - //~| HELP the following trait is implemented but not in scope, perhaps add a `use` for it: - //~| HELP `use no_method_suggested_traits::foo::PubPub;` + //~| HELP items from traits can only be used if the trait is in scope std::rc::Rc::new(&mut Box::new(&1i32)).method(); //~^ ERROR no method named - //~| HELP the following trait is implemented but not in scope, perhaps add a `use` for it: - //~| HELP `use no_method_suggested_traits::foo::PubPub;` + //~| HELP items from traits can only be used if the trait is in scope Foo.method(); //~^ ERROR no method named - //~| HELP following traits define an item `method`, perhaps you need to implement one of them - //~| HELP `foo::Bar` - //~| HELP `no_method_suggested_traits::foo::PubPub` - //~| HELP `no_method_suggested_traits::Reexported` - //~| HELP `no_method_suggested_traits::bar::PubPriv` - //~| HELP `no_method_suggested_traits::qux::PrivPub` - //~| HELP `no_method_suggested_traits::quz::PrivPriv` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&Foo)).method(); //~^ ERROR no method named - //~| HELP following traits define an item `method`, perhaps you need to implement one of them - //~| HELP `foo::Bar` - //~| HELP `no_method_suggested_traits::foo::PubPub` - //~| HELP `no_method_suggested_traits::Reexported` - //~| HELP `no_method_suggested_traits::bar::PubPriv` - //~| HELP `no_method_suggested_traits::qux::PrivPub` - //~| HELP `no_method_suggested_traits::quz::PrivPriv` + //~| HELP items from traits can only be used if the trait is implemented and in scope 1u64.method2(); //~^ ERROR no method named - //~| HELP the following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&1u64)).method2(); //~^ ERROR no method named - //~| HELP the following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope no_method_suggested_traits::Foo.method2(); //~^ ERROR no method named - //~| HELP following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); //~^ ERROR no method named - //~| HELP following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope no_method_suggested_traits::Bar::X.method2(); //~^ ERROR no method named - //~| HELP following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); //~^ ERROR no method named - //~| HELP following trait defines an item `method2`, perhaps you need to implement it - //~| HELP `foo::Bar` + //~| HELP items from traits can only be used if the trait is implemented and in scope Foo.method3(); //~^ ERROR no method named - //~| HELP following trait defines an item `method3`, perhaps you need to implement it - //~| HELP `no_method_suggested_traits::foo::PubPub` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&Foo)).method3(); //~^ ERROR no method named - //~| HELP following trait defines an item `method3`, perhaps you need to implement it - //~| HELP `no_method_suggested_traits::foo::PubPub` + //~| HELP items from traits can only be used if the trait is implemented and in scope Bar::X.method3(); //~^ ERROR no method named - //~| HELP following trait defines an item `method3`, perhaps you need to implement it - //~| HELP `no_method_suggested_traits::foo::PubPub` + //~| HELP items from traits can only be used if the trait is implemented and in scope std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); //~^ ERROR no method named - //~| HELP following trait defines an item `method3`, perhaps you need to implement it - //~| HELP `no_method_suggested_traits::foo::PubPub` + //~| HELP items from traits can only be used if the trait is implemented and in scope // should have no help: 1_usize.method3(); //~ ERROR no method named diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index 23f115858cd5e..2d519c11b948e 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -1,38 +1,50 @@ error[E0599]: no method named `method` found for type `u32` in the current scope - --> $DIR/no-method-suggested-traits.rs:33:10 + --> $DIR/no-method-suggested-traits.rs:38:10 | -33 | 1u32.method(); +38 | 1u32.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them: - candidate #1: `use foo::Bar;` - candidate #2: `use no_method_suggested_traits::foo::PubPub;` - candidate #3: `use no_method_suggested_traits::qux::PrivPub;` - candidate #4: `use no_method_suggested_traits::Reexported;` +help: the following traits are implemented but not in scope, perhaps add a `use` for one of them: + | +14 | use foo::Bar; + | +14 | use no_method_suggested_traits::foo::PubPub; + | +14 | use no_method_suggested_traits::qux::PrivPub; + | +14 | use no_method_suggested_traits::Reexported; + | error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:38:44 + --> $DIR/no-method-suggested-traits.rs:41:44 | -38 | std::rc::Rc::new(&mut Box::new(&1u32)).method(); +41 | std::rc::Rc::new(&mut Box::new(&1u32)).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them: - candidate #1: `use foo::Bar;` - candidate #2: `use no_method_suggested_traits::foo::PubPub;` - candidate #3: `use no_method_suggested_traits::qux::PrivPub;` - candidate #4: `use no_method_suggested_traits::Reexported;` +help: the following traits are implemented but not in scope, perhaps add a `use` for one of them: + | +14 | use foo::Bar; + | +14 | use no_method_suggested_traits::foo::PubPub; + | +14 | use no_method_suggested_traits::qux::PrivPub; + | +14 | use no_method_suggested_traits::Reexported; + | error[E0599]: no method named `method` found for type `char` in the current scope - --> $DIR/no-method-suggested-traits.rs:44:9 + --> $DIR/no-method-suggested-traits.rs:45:9 | -44 | 'a'.method(); +45 | 'a'.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use foo::Bar;` +help: the following trait is implemented but not in scope, perhaps add a `use` for it: + | +14 | use foo::Bar; + | error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:48:43 @@ -41,33 +53,42 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use foo::Bar;` +help: the following trait is implemented but not in scope, perhaps add a `use` for it: + | +14 | use foo::Bar; + | error[E0599]: no method named `method` found for type `i32` in the current scope - --> $DIR/no-method-suggested-traits.rs:53:10 + --> $DIR/no-method-suggested-traits.rs:52:10 | -53 | 1i32.method(); +52 | 1i32.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use no_method_suggested_traits::foo::PubPub;` +help: the following trait is implemented but not in scope, perhaps add a `use` for it: + | +14 | use no_method_suggested_traits::foo::PubPub; + | error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:57:44 + --> $DIR/no-method-suggested-traits.rs:55:44 | -57 | std::rc::Rc::new(&mut Box::new(&1i32)).method(); +55 | std::rc::Rc::new(&mut Box::new(&1i32)).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use no_method_suggested_traits::foo::PubPub;` +help: the following trait is implemented but not in scope, perhaps add a `use` for it: + | +14 | use no_method_suggested_traits::foo::PubPub; + | error[E0599]: no method named `method` found for type `Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:62:9 + --> $DIR/no-method-suggested-traits.rs:59:9 | -62 | Foo.method(); +14 | struct Foo; //~ HELP perhaps add a `use` for it + | ----------- method `method` not found for this +... +59 | Foo.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -80,9 +101,9 @@ error[E0599]: no method named `method` found for type `Foo` in the current scope candidate #6: `no_method_suggested_traits::Reexported` error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:71:43 + --> $DIR/no-method-suggested-traits.rs:62:43 | -71 | std::rc::Rc::new(&mut Box::new(&Foo)).method(); +62 | std::rc::Rc::new(&mut Box::new(&Foo)).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -95,9 +116,9 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box candidate #6: `no_method_suggested_traits::Reexported` error[E0599]: no method named `method2` found for type `u64` in the current scope - --> $DIR/no-method-suggested-traits.rs:81:10 + --> $DIR/no-method-suggested-traits.rs:66:10 | -81 | 1u64.method2(); +66 | 1u64.method2(); | ^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -105,9 +126,9 @@ error[E0599]: no method named `method2` found for type `u64` in the current scop candidate #1: `foo::Bar` error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&u64>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:85:44 + --> $DIR/no-method-suggested-traits.rs:69:44 | -85 | std::rc::Rc::new(&mut Box::new(&1u64)).method2(); +69 | std::rc::Rc::new(&mut Box::new(&1u64)).method2(); | ^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -115,9 +136,9 @@ error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::bo candidate #1: `foo::Bar` error[E0599]: no method named `method2` found for type `no_method_suggested_traits::Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:90:37 + --> $DIR/no-method-suggested-traits.rs:73:37 | -90 | no_method_suggested_traits::Foo.method2(); +73 | no_method_suggested_traits::Foo.method2(); | ^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -125,9 +146,9 @@ error[E0599]: no method named `method2` found for type `no_method_suggested_trai candidate #1: `foo::Bar` error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:94:71 + --> $DIR/no-method-suggested-traits.rs:76:71 | -94 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); +76 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); | ^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -135,9 +156,9 @@ error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::bo candidate #1: `foo::Bar` error[E0599]: no method named `method2` found for type `no_method_suggested_traits::Bar` in the current scope - --> $DIR/no-method-suggested-traits.rs:98:40 + --> $DIR/no-method-suggested-traits.rs:79:40 | -98 | no_method_suggested_traits::Bar::X.method2(); +79 | no_method_suggested_traits::Bar::X.method2(); | ^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope @@ -145,89 +166,95 @@ error[E0599]: no method named `method2` found for type `no_method_suggested_trai candidate #1: `foo::Bar` error[E0599]: no method named `method2` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:102:74 - | -102 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); - | ^^^^^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `method2`, perhaps you need to implement it: - candidate #1: `foo::Bar` + --> $DIR/no-method-suggested-traits.rs:82:74 + | +82 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); + | ^^^^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `method2`, perhaps you need to implement it: + candidate #1: `foo::Bar` error[E0599]: no method named `method3` found for type `Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:107:9 - | -107 | Foo.method3(); - | ^^^^^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + --> $DIR/no-method-suggested-traits.rs:86:9 + | +14 | struct Foo; //~ HELP perhaps add a `use` for it + | ----------- method `method3` not found for this +... +86 | Foo.method3(); + | ^^^^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `method3`, perhaps you need to implement it: + candidate #1: `no_method_suggested_traits::foo::PubPub` error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:111:43 - | -111 | std::rc::Rc::new(&mut Box::new(&Foo)).method3(); - | ^^^^^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + --> $DIR/no-method-suggested-traits.rs:89:43 + | +89 | std::rc::Rc::new(&mut Box::new(&Foo)).method3(); + | ^^^^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `method3`, perhaps you need to implement it: + candidate #1: `no_method_suggested_traits::foo::PubPub` error[E0599]: no method named `method3` found for type `Bar` in the current scope - --> $DIR/no-method-suggested-traits.rs:115:12 - | -115 | Bar::X.method3(); - | ^^^^^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + --> $DIR/no-method-suggested-traits.rs:92:12 + | +20 | enum Bar { X } + | -------- method `method3` not found for this +... +92 | Bar::X.method3(); + | ^^^^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `method3`, perhaps you need to implement it: + candidate #1: `no_method_suggested_traits::foo::PubPub` error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&Bar>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:119:46 - | -119 | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); - | ^^^^^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + --> $DIR/no-method-suggested-traits.rs:95:46 + | +95 | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); + | ^^^^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `method3`, perhaps you need to implement it: + candidate #1: `no_method_suggested_traits::foo::PubPub` error[E0599]: no method named `method3` found for type `usize` in the current scope - --> $DIR/no-method-suggested-traits.rs:125:13 + --> $DIR/no-method-suggested-traits.rs:100:13 | -125 | 1_usize.method3(); //~ ERROR no method named +100 | 1_usize.method3(); //~ ERROR no method named | ^^^^^^^ error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&usize>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:126:47 + --> $DIR/no-method-suggested-traits.rs:101:47 | -126 | std::rc::Rc::new(&mut Box::new(&1_usize)).method3(); //~ ERROR no method named +101 | std::rc::Rc::new(&mut Box::new(&1_usize)).method3(); //~ ERROR no method named | ^^^^^^^ error[E0599]: no method named `method3` found for type `no_method_suggested_traits::Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:127:37 + --> $DIR/no-method-suggested-traits.rs:102:37 | -127 | no_method_suggested_traits::Foo.method3(); //~ ERROR no method named +102 | no_method_suggested_traits::Foo.method3(); //~ ERROR no method named | ^^^^^^^ error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:128:71 + --> $DIR/no-method-suggested-traits.rs:103:71 | -128 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); +103 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); | ^^^^^^^ error[E0599]: no method named `method3` found for type `no_method_suggested_traits::Bar` in the current scope - --> $DIR/no-method-suggested-traits.rs:130:40 + --> $DIR/no-method-suggested-traits.rs:105:40 | -130 | no_method_suggested_traits::Bar::X.method3(); //~ ERROR no method named +105 | no_method_suggested_traits::Bar::X.method3(); //~ ERROR no method named | ^^^^^^^ error[E0599]: no method named `method3` found for type `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope - --> $DIR/no-method-suggested-traits.rs:131:74 + --> $DIR/no-method-suggested-traits.rs:106:74 | -131 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); +106 | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); | ^^^^^^^ error: aborting due to 24 previous errors diff --git a/src/test/ui/impl-trait/trait_type.rs b/src/test/ui/impl-trait/trait_type.rs index 3507dcfbe172a..7eefa5c600617 100644 --- a/src/test/ui/impl-trait/trait_type.rs +++ b/src/test/ui/impl-trait/trait_type.rs @@ -15,16 +15,20 @@ struct MyType4; impl std::fmt::Display for MyType { fn fmt(&self, x: &str) -> () { } + //~^ ERROR method `fmt` has an incompatible type } impl std::fmt::Display for MyType2 { fn fmt(&self) -> () { } + //~^ ERROR method `fmt` has 1 parameter } impl std::fmt::Display for MyType3 { fn fmt() -> () { } + //~^ ERROR method `fmt` has a `&self` declaration in the trait } impl std::fmt::Display for MyType4 {} +//~^ ERROR not all trait items fn main() {} diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 9216c6e290775..42e1dcdb1c42a 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -8,25 +8,25 @@ error[E0053]: method `fmt` has an incompatible type for trait found type `fn(&MyType, &str)` error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fmt::Display::fmt` has 2 - --> $DIR/trait_type.rs:21:11 + --> $DIR/trait_type.rs:22:11 | -21 | fn fmt(&self) -> () { } +22 | fn fmt(&self) -> () { } | ^^^^^ expected 2 parameters, found 1 | = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in the impl - --> $DIR/trait_type.rs:25:4 + --> $DIR/trait_type.rs:27:4 | -25 | fn fmt() -> () { } +27 | fn fmt() -> () { } | ^^^^^^^^^^^^^^^^^^ expected `&self` in impl | = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` error[E0046]: not all trait items implemented, missing: `fmt` - --> $DIR/trait_type.rs:28:1 + --> $DIR/trait_type.rs:31:1 | -28 | impl std::fmt::Display for MyType4 {} +31 | impl std::fmt::Display for MyType4 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation | = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` diff --git a/src/test/ui/impl-trait/universal-mismatched-type.rs b/src/test/ui/impl-trait/universal-mismatched-type.rs new file mode 100644 index 0000000000000..00fc22ff0d853 --- /dev/null +++ b/src/test/ui/impl-trait/universal-mismatched-type.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +use std::fmt::Debug; + +fn foo(x: impl Debug) -> String { + x //~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/ui/impl-trait/universal-mismatched-type.stderr b/src/test/ui/impl-trait/universal-mismatched-type.stderr new file mode 100644 index 0000000000000..b4dd6c8446c59 --- /dev/null +++ b/src/test/ui/impl-trait/universal-mismatched-type.stderr @@ -0,0 +1,13 @@ +error[E0308]: mismatched types + --> $DIR/universal-mismatched-type.rs:16:5 + | +15 | fn foo(x: impl Debug) -> String { + | ------ expected `std::string::String` because of return type +16 | x //~ ERROR mismatched types + | ^ expected struct `std::string::String`, found type parameter + | + = note: expected type `std::string::String` + found type `impl Debug` + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/universal-two-impl-traits.rs b/src/test/ui/impl-trait/universal-two-impl-traits.rs new file mode 100644 index 0000000000000..9a4847b56062a --- /dev/null +++ b/src/test/ui/impl-trait/universal-two-impl-traits.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +use std::fmt::Debug; + +fn foo(x: impl Debug, y: impl Debug) -> String { + let mut a = x; + a = y; //~ ERROR mismatched + format!("{:?}", a) +} + +fn main() { } diff --git a/src/test/ui/impl-trait/universal-two-impl-traits.stderr b/src/test/ui/impl-trait/universal-two-impl-traits.stderr new file mode 100644 index 0000000000000..9903e26bbbd0b --- /dev/null +++ b/src/test/ui/impl-trait/universal-two-impl-traits.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/universal-two-impl-traits.rs:17:9 + | +17 | a = y; //~ ERROR mismatched + | ^ expected type parameter, found a different type parameter + | + = note: expected type `impl Debug` (type parameter) + found type `impl Debug` (type parameter) + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/universal_wrong_bounds.rs b/src/test/ui/impl-trait/universal_wrong_bounds.rs new file mode 100644 index 0000000000000..36d9f615c5f57 --- /dev/null +++ b/src/test/ui/impl-trait/universal_wrong_bounds.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(universal_impl_trait)] + +use std::fmt::Display; + +fn foo(f: impl Display + Clone) -> String { + wants_debug(f); + wants_display(f); + wants_clone(f); //~ ERROR cannot find +} + +fn wants_debug(g: impl Debug) { } //~ ERROR cannot find +fn wants_display(g: impl Debug) { } //~ ERROR cannot find +fn wants_cone(g: impl Clone) { } + +fn main() { +} diff --git a/src/test/ui/impl-trait/universal_wrong_bounds.stderr b/src/test/ui/impl-trait/universal_wrong_bounds.stderr new file mode 100644 index 0000000000000..b457e025c29ff --- /dev/null +++ b/src/test/ui/impl-trait/universal_wrong_bounds.stderr @@ -0,0 +1,28 @@ +error[E0425]: cannot find function `wants_clone` in this scope + --> $DIR/universal_wrong_bounds.rs:18:5 + | +18 | wants_clone(f); //~ ERROR cannot find + | ^^^^^^^^^^^ did you mean `wants_cone`? + +error[E0405]: cannot find trait `Debug` in this scope + --> $DIR/universal_wrong_bounds.rs:21:24 + | +21 | fn wants_debug(g: impl Debug) { } //~ ERROR cannot find + | ^^^^^ not found in this scope +help: possible candidate is found in another module, you can import it into scope + | +13 | use std::fmt::Debug; + | + +error[E0405]: cannot find trait `Debug` in this scope + --> $DIR/universal_wrong_bounds.rs:22:26 + | +22 | fn wants_display(g: impl Debug) { } //~ ERROR cannot find + | ^^^^^ not found in this scope +help: possible candidate is found in another module, you can import it into scope + | +13 | use std::fmt::Debug; + | + +error: cannot continue compilation due to previous error + diff --git a/src/test/ui/in-band-lifetimes/E0687.rs b/src/test/ui/in-band-lifetimes/E0687.rs new file mode 100644 index 0000000000000..4eddebb15e120 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +fn foo(x: fn(&'a u32)) {} //~ ERROR must be explicitly + +fn bar(x: &Fn(&'a u32)) {} //~ ERROR must be explicitly + +fn baz(x: fn(&'a u32), y: &'a u32) {} //~ ERROR must be explicitly + +struct Foo<'a> { x: &'a u32 } + +impl Foo<'a> { + fn bar(&self, x: fn(&'a u32)) {} //~ ERROR must be explicitly +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0687.stderr b/src/test/ui/in-band-lifetimes/E0687.stderr new file mode 100644 index 0000000000000..42714f21685f9 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687.stderr @@ -0,0 +1,26 @@ +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:14:15 + | +14 | fn foo(x: fn(&'a u32)) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:16:16 + | +16 | fn bar(x: &Fn(&'a u32)) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:18:15 + | +18 | fn baz(x: fn(&'a u32), y: &'a u32) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:23:26 + | +23 | fn bar(&self, x: fn(&'a u32)) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/in-band-lifetimes/E0687_where.rs b/src/test/ui/in-band-lifetimes/E0687_where.rs new file mode 100644 index 0000000000000..ac67558772007 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687_where.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes, universal_impl_trait)] + +fn bar(x: &F) where F: Fn(&'a u32) {} //~ ERROR must be explicitly + +fn baz(x: &impl Fn(&'a u32)) {} //~ ERROR must be explicitly + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0687_where.stderr b/src/test/ui/in-band-lifetimes/E0687_where.stderr new file mode 100644 index 0000000000000..a9913f6b64464 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687_where.stderr @@ -0,0 +1,14 @@ +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687_where.rs:14:31 + | +14 | fn bar(x: &F) where F: Fn(&'a u32) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687_where.rs:16:21 + | +16 | fn baz(x: &impl Fn(&'a u32)) {} //~ ERROR must be explicitly + | ^^ in-band lifetime definition + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/E0688.rs b/src/test/ui/in-band-lifetimes/E0688.rs new file mode 100644 index 0000000000000..29b954e9a8360 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0688.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +fn foo<'a>(x: &'a u32, y: &'b u32) {} //~ ERROR cannot mix + +struct Foo<'a> { x: &'a u32 } + +impl Foo<'a> { + fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {} //~ ERROR cannot mix +} + +impl<'b> Foo<'a> { //~ ERROR cannot mix + fn baz() {} +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0688.stderr b/src/test/ui/in-band-lifetimes/E0688.stderr new file mode 100644 index 0000000000000..c33b088f0faab --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0688.stderr @@ -0,0 +1,26 @@ +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:14:28 + | +14 | fn foo<'a>(x: &'a u32, y: &'b u32) {} //~ ERROR cannot mix + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:19:44 + | +19 | fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {} //~ ERROR cannot mix + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:22:14 + | +22 | impl<'b> Foo<'a> { //~ ERROR cannot mix + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/in-band-lifetimes/mismatched.rs b/src/test/ui/in-band-lifetimes/mismatched.rs new file mode 100644 index 0000000000000..80bc56c0f441c --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +fn foo(x: &'a u32, y: &u32) -> &'a u32 { y } //~ ERROR explicit lifetime required + +fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y } //~ ERROR lifetime mismatch + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched.stderr b/src/test/ui/in-band-lifetimes/mismatched.stderr new file mode 100644 index 0000000000000..0c1231e01de6e --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched.stderr @@ -0,0 +1,18 @@ +error[E0621]: explicit lifetime required in the type of `y` + --> $DIR/mismatched.rs:14:42 + | +14 | fn foo(x: &'a u32, y: &u32) -> &'a u32 { y } //~ ERROR explicit lifetime required + | - ^ lifetime `'a` required + | | + | consider changing the type of `y` to `&'a u32` + +error[E0623]: lifetime mismatch + --> $DIR/mismatched.rs:16:46 + | +16 | fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y } //~ ERROR lifetime mismatch + | ------- ------- ^ ...but data from `y` is returned here + | | + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.rs b/src/test/ui/in-band-lifetimes/mismatched_trait.rs new file mode 100644 index 0000000000000..bc175803ebda7 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +trait Get { + fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 { + y //~ ERROR explicit lifetime required + } +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr new file mode 100644 index 0000000000000..58ff1694fb74c --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr @@ -0,0 +1,10 @@ +error[E0621]: explicit lifetime required in the type of `y` + --> $DIR/mismatched_trait.rs:16:9 + | +15 | fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 { + | - consider changing the type of `y` to `&'a u32` +16 | y //~ ERROR explicit lifetime required + | ^ lifetime `'a` required + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs new file mode 100644 index 0000000000000..52641059b1fa8 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +trait Get { + fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; +} + +impl Get for i32 { + fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer + x + } +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr new file mode 100644 index 0000000000000..e96f7181a6dae --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -0,0 +1,39 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'a in generic type due to conflicting requirements + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer +20 | | x +21 | | } + | |_____^ + | +note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 19:5... + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer +20 | | x +21 | | } + | |_____^ +note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32) + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer +20 | | x +21 | | } + | |_____^ +note: but, the lifetime must be valid for the lifetime 'a as defined on the method body at 19:5... + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer +20 | | x +21 | | } + | |_____^ +note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32) + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { //~ ERROR cannot infer +20 | | x +21 | | } + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.rs b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs new file mode 100644 index 0000000000000..08ce13d0bccd1 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +fn foo(x: &'a u32) -> &'a u32 { x } + +fn main() { + let mut p = 3; + let r = foo(&p); + p += 1; //~ ERROR cannot assign to `p` because it is borrowed + println!("{}", r); +} diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr new file mode 100644 index 0000000000000..14f9098c6c2f6 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `p` because it is borrowed + --> $DIR/mut_while_borrow.rs:19:5 + | +18 | let r = foo(&p); + | - borrow of `p` occurs here +19 | p += 1; //~ ERROR cannot assign to `p` because it is borrowed + | ^^^^^^ assignment to borrowed `p` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs new file mode 100644 index 0000000000000..0d3e6ba644e63 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +struct Foo { + x: &'test u32, //~ ERROR undeclared lifetime +} + +enum Bar { + Baz(&'test u32), //~ ERROR undeclared lifetime +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr new file mode 100644 index 0000000000000..a8df6dbca0a11 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr @@ -0,0 +1,14 @@ +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_in_band_in_struct.rs:15:9 + | +15 | x: &'test u32, //~ ERROR undeclared lifetime + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_in_band_in_struct.rs:19:10 + | +19 | Baz(&'test u32), //~ ERROR undeclared lifetime + | ^^^^^ undeclared lifetime + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs new file mode 100644 index 0000000000000..eaa082a35da59 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +fn foo(x: &u32) { + let y: &'test u32 = x; //~ ERROR use of undeclared lifetime +} + +fn foo2(x: &u32) {} +fn bar() { + let y: fn(&'test u32) = foo2; //~ ERROR use of undeclared lifetime +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr new file mode 100644 index 0000000000000..e2340dbba23e1 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr @@ -0,0 +1,14 @@ +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_introducing_in_band_in_locals.rs:15:13 + | +15 | let y: &'test u32 = x; //~ ERROR use of undeclared lifetime + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_introducing_in_band_in_locals.rs:20:16 + | +20 | let y: fn(&'test u32) = foo2; //~ ERROR use of undeclared lifetime + | ^^^^^ undeclared lifetime + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/shadow.rs b/src/test/ui/in-band-lifetimes/shadow.rs new file mode 100644 index 0000000000000..b6438f01af5f3 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/shadow.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(in_band_lifetimes)] + +struct Foo(T); + +impl Foo<&'s u8> { + fn bar<'s>(&self, x: &'s u8) {} //~ ERROR shadows a lifetime name + fn baz(x: for<'s> fn(&'s u32)) {} //~ ERROR shadows a lifetime name +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/shadow.stderr b/src/test/ui/in-band-lifetimes/shadow.stderr new file mode 100644 index 0000000000000..49b82fa495a05 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/shadow.stderr @@ -0,0 +1,19 @@ +error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope + --> $DIR/shadow.rs:17:12 + | +16 | impl Foo<&'s u8> { + | -- first declared here +17 | fn bar<'s>(&self, x: &'s u8) {} //~ ERROR shadows a lifetime name + | ^^ lifetime 's already in scope + +error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope + --> $DIR/shadow.rs:18:19 + | +16 | impl Foo<&'s u8> { + | -- first declared here +17 | fn bar<'s>(&self, x: &'s u8) {} //~ ERROR shadows a lifetime name +18 | fn baz(x: for<'s> fn(&'s u32)) {} //~ ERROR shadows a lifetime name + | ^^ lifetime 's already in scope + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/interior-mutability/interior-mutability.rs b/src/test/ui/interior-mutability/interior-mutability.rs index 60d85d1b3b787..a772d1f90cc0b 100644 --- a/src/test/ui/interior-mutability/interior-mutability.rs +++ b/src/test/ui/interior-mutability/interior-mutability.rs @@ -12,5 +12,5 @@ use std::cell::Cell; use std::panic::catch_unwind; fn main() { let mut x = Cell::new(22); - catch_unwind(|| { x.set(23); }); + catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound } diff --git a/src/test/ui/interior-mutability/interior-mutability.stderr b/src/test/ui/interior-mutability/interior-mutability.stderr index 76362f1f494a6..f4beb44b82dc7 100644 --- a/src/test/ui/interior-mutability/interior-mutability.stderr +++ b/src/test/ui/interior-mutability/interior-mutability.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell` --> $DIR/interior-mutability.rs:15:5 | -15 | catch_unwind(|| { x.set(23); }); +15 | catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound | ^^^^^^^^^^^^ the type std::cell::UnsafeCell may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | = help: within `std::cell::Cell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` diff --git a/src/test/ui/issue-13483.rs b/src/test/ui/issue-13483.rs index 8637804391276..c44465b221cdc 100644 --- a/src/test/ui/issue-13483.rs +++ b/src/test/ui/issue-13483.rs @@ -10,14 +10,14 @@ fn main() { if true { - } else if { + } else if { //~ ERROR missing condition } else { } } fn foo() { if true { - } else if { + } else if { //~ ERROR missing condition } bar(); } diff --git a/src/test/ui/issue-13483.stderr b/src/test/ui/issue-13483.stderr index 3446969dfd213..344e179695369 100644 --- a/src/test/ui/issue-13483.stderr +++ b/src/test/ui/issue-13483.stderr @@ -1,13 +1,13 @@ error: missing condition for `if` statemement --> $DIR/issue-13483.rs:13:14 | -13 | } else if { +13 | } else if { //~ ERROR missing condition | ^ expected if condition here error: missing condition for `if` statemement --> $DIR/issue-13483.rs:20:14 | -20 | } else if { +20 | } else if { //~ ERROR missing condition | ^ expected if condition here error: aborting due to 2 previous errors diff --git a/src/test/ui/issue-22644.rs b/src/test/ui/issue-22644.rs index b482d0595f7b6..f787e43dbdf57 100644 --- a/src/test/ui/issue-22644.rs +++ b/src/test/ui/issue-22644.rs @@ -13,17 +13,19 @@ fn main() { let long_name : usize = 0; println!("{}", a as usize > long_name); - println!("{}", a as usize < long_name); + println!("{}", a as usize < long_name); //~ ERROR `<` is interpreted as a start of generic println!("{}{}", a as usize < long_name, long_name); - println!("{}", a as usize < 4); + //~^ ERROR `<` is interpreted as a start of generic + println!("{}", a as usize < 4); //~ ERROR `<` is interpreted as a start of generic println!("{}", a: usize > long_name); println!("{}{}", a: usize < long_name, long_name); - println!("{}", a: usize < 4); + //~^ ERROR `<` is interpreted as a start of generic + println!("{}", a: usize < 4); //~ ERROR `<` is interpreted as a start of generic println!("{}", a as usize - < + < //~ ERROR `<` is interpreted as a start of generic 4); println!("{}", a @@ -32,8 +34,10 @@ fn main() { usize - < + < //~ ERROR `<` is interpreted as a start of generic 5); - println!("{}", a: &mut 4); + println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted as a start of generic + + println!("{}", a: &mut 4); //~ ERROR expected type, found `4` } diff --git a/src/test/ui/issue-22644.stderr b/src/test/ui/issue-22644.stderr index 54c325b24a3b7..91107fbe35610 100644 --- a/src/test/ui/issue-22644.stderr +++ b/src/test/ui/issue-22644.stderr @@ -1,7 +1,7 @@ error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison --> $DIR/issue-22644.rs:16:31 | -16 | println!("{}", a as usize < long_name); +16 | println!("{}", a as usize < long_name); //~ ERROR `<` is interpreted as a start of generic | ---------- ^ --------- interpreted as generic arguments | | | | | not interpreted as comparison @@ -17,68 +17,75 @@ error: `<` is interpreted as a start of generic arguments for `usize`, not a com | help: try comparing the casted value: `(a as usize)` error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:18:31 + --> $DIR/issue-22644.rs:19:31 | -18 | println!("{}", a as usize < 4); +19 | println!("{}", a as usize < 4); //~ ERROR `<` is interpreted as a start of generic | ---------- ^ - interpreted as generic arguments | | | | | not interpreted as comparison | help: try comparing the casted value: `(a as usize)` error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:20:31 + --> $DIR/issue-22644.rs:21:31 | -20 | println!("{}{}", a: usize < long_name, long_name); +21 | println!("{}{}", a: usize < long_name, long_name); | -------- ^ -------------------- interpreted as generic arguments | | | | | not interpreted as comparison | help: try comparing the casted value: `(a: usize)` error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:21:29 + --> $DIR/issue-22644.rs:23:29 | -21 | println!("{}", a: usize < 4); +23 | println!("{}", a: usize < 4); //~ ERROR `<` is interpreted as a start of generic | -------- ^ - interpreted as generic arguments | | | | | not interpreted as comparison | help: try comparing the casted value: `(a: usize)` error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:26:20 + --> $DIR/issue-22644.rs:28:20 | -26 | < +28 | < //~ ERROR `<` is interpreted as a start of generic | ^ not interpreted as comparison -27 | 4); +29 | 4); | - interpreted as generic arguments - | help: try comparing the casted value | -23 | println!("{}", (a -24 | as -25 | usize) +25 | println!("{}", (a +26 | as +27 | usize) | error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:35:20 + --> $DIR/issue-22644.rs:37:20 | -35 | < +37 | < //~ ERROR `<` is interpreted as a start of generic | ^ not interpreted as comparison -36 | 5); +38 | 5); | - interpreted as generic arguments - | help: try comparing the casted value | -28 | println!("{}", (a -29 | -30 | -31 | as +30 | println!("{}", (a +31 | 32 | -33 | +33 | as +34 | +35 | ... +error: `<` is interpreted as a start of generic arguments for `usize`, not a shift + --> $DIR/issue-22644.rs:40:31 + | +40 | println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted as a start of generic + | ---------- ^^ --------- interpreted as generic arguments + | | | + | | not interpreted as shift + | help: try shifting the casted value: `(a as usize)` + error: expected type, found `4` - --> $DIR/issue-22644.rs:38:28 + --> $DIR/issue-22644.rs:42:28 | -38 | println!("{}", a: &mut 4); +42 | println!("{}", a: &mut 4); //~ ERROR expected type, found `4` | ^ expecting a type here because of type ascription diff --git a/src/test/ui/issue-26548.stderr b/src/test/ui/issue-26548.stderr deleted file mode 100644 index 8bfe4ac733b6d..0000000000000 --- a/src/test/ui/issue-26548.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0391]: unsupported cyclic reference between types/traits detected - | -note: the cycle begins when computing layout of `S`... -note: ...which then requires computing layout of `std::option::Option<::It>`... -note: ...which then requires computing layout of `::It`... - = note: ...which then again requires computing layout of `S`, completing the cycle. - -error: aborting due to previous error - diff --git a/src/test/ui/issue-33525.rs b/src/test/ui/issue-33525.rs index 0e777fe8a9454..0589618a82faf 100644 --- a/src/test/ui/issue-33525.rs +++ b/src/test/ui/issue-33525.rs @@ -9,7 +9,7 @@ // except according to those terms. fn main() { - a; - "".lorem; - "".ipsum; + a; //~ ERROR cannot find value `a` + "".lorem; //~ ERROR no field + "".ipsum; //~ ERROR no field } diff --git a/src/test/ui/issue-33525.stderr b/src/test/ui/issue-33525.stderr index 5de2d98f86a9d..4909340fa4c35 100644 --- a/src/test/ui/issue-33525.stderr +++ b/src/test/ui/issue-33525.stderr @@ -1,19 +1,19 @@ error[E0425]: cannot find value `a` in this scope --> $DIR/issue-33525.rs:12:5 | -12 | a; +12 | a; //~ ERROR cannot find value `a` | ^ not found in this scope error[E0609]: no field `lorem` on type `&'static str` --> $DIR/issue-33525.rs:13:8 | -13 | "".lorem; +13 | "".lorem; //~ ERROR no field | ^^^^^ error[E0609]: no field `ipsum` on type `&'static str` --> $DIR/issue-33525.rs:14:8 | -14 | "".ipsum; +14 | "".ipsum; //~ ERROR no field | ^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/issue-33941.rs b/src/test/ui/issue-33941.rs new file mode 100644 index 0000000000000..21c169c663828 --- /dev/null +++ b/src/test/ui/issue-33941.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; + +fn main() { + for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch + //~^ ERROR type mismatch +} diff --git a/src/test/ui/issue-33941.stderr b/src/test/ui/issue-33941.stderr new file mode 100644 index 0000000000000..953e6fe77d716 --- /dev/null +++ b/src/test/ui/issue-33941.stderr @@ -0,0 +1,21 @@ +error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == &_` + --> $DIR/issue-33941.rs:14:36 + | +14 | for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch + | ^^^^^^ expected tuple, found reference + | + = note: expected type `(&_, &_)` + found type `&_` + +error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == &_` + --> $DIR/issue-33941.rs:14:5 + | +14 | for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference + | + = note: expected type `(&_, &_)` + found type `&_` + = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned>` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issue-35241.rs b/src/test/ui/issue-35241.rs new file mode 100644 index 0000000000000..4616f25bdfb86 --- /dev/null +++ b/src/test/ui/issue-35241.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo(u32); + +fn test() -> Foo { Foo } //~ ERROR mismatched types + +fn main() {} diff --git a/src/test/ui/issue-35241.stderr b/src/test/ui/issue-35241.stderr new file mode 100644 index 0000000000000..25cef7388977d --- /dev/null +++ b/src/test/ui/issue-35241.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-35241.rs:13:20 + | +13 | fn test() -> Foo { Foo } //~ ERROR mismatched types + | --- ^^^ + | | | + | | expected struct `Foo`, found fn item + | | did you mean `Foo(/* fields */)`? + | expected `Foo` because of return type + | + = note: expected type `Foo` + found type `fn(u32) -> Foo {Foo::{{constructor}}}` + +error: aborting due to previous error + diff --git a/src/test/ui/issue-35675.rs b/src/test/ui/issue-35675.rs index 001c1f2eddca1..ee9d1324cdbb7 100644 --- a/src/test/ui/issue-35675.rs +++ b/src/test/ui/issue-35675.rs @@ -12,14 +12,13 @@ enum Fruit { //~ HELP possible candidate is found in another module, you can import it into scope //~^ HELP possible candidate is found in another module, you can import it into scope Apple(i64), - //~^ HELP there is an enum variant `Fruit::Apple`, did you mean to use `Fruit`? - //~| HELP there is an enum variant `Fruit::Apple`, did you mean to use `Fruit`? Orange(i64), } fn should_return_fruit() -> Apple { //~^ ERROR cannot find type `Apple` in this scope //~| NOTE not found in this scope + //~| HELP you can try using the variant's enum Apple(5) //~^ ERROR cannot find function `Apple` in this scope //~| NOTE not found in this scope @@ -28,6 +27,7 @@ fn should_return_fruit() -> Apple { fn should_return_fruit_too() -> Fruit::Apple { //~^ ERROR expected type, found variant `Fruit::Apple` //~| NOTE not a type + //~| HELP you can try using the variant's enum Apple(5) //~^ ERROR cannot find function `Apple` in this scope //~| NOTE not found in this scope @@ -44,6 +44,7 @@ fn foo() -> Ok { fn bar() -> Variant3 { //~^ ERROR cannot find type `Variant3` in this scope //~| NOTE not found in this scope + //~| HELP you can try using the variant's enum } fn qux() -> Some { @@ -61,7 +62,6 @@ mod x { Variant1, Variant2(), Variant3(usize), - //~^ HELP there is an enum variant `x::Enum::Variant3`, did you mean to use `x::Enum`? Variant4 {}, } } diff --git a/src/test/ui/issue-35675.stderr b/src/test/ui/issue-35675.stderr index ed330f47208eb..550e094dc51bb 100644 --- a/src/test/ui/issue-35675.stderr +++ b/src/test/ui/issue-35675.stderr @@ -1,27 +1,26 @@ error[E0412]: cannot find type `Apple` in this scope - --> $DIR/issue-35675.rs:20:29 + --> $DIR/issue-35675.rs:18:29 | -20 | fn should_return_fruit() -> Apple { +18 | fn should_return_fruit() -> Apple { | ^^^^^ | | | not found in this scope | help: you can try using the variant's enum: `Fruit` error[E0425]: cannot find function `Apple` in this scope - --> $DIR/issue-35675.rs:23:5 + --> $DIR/issue-35675.rs:22:5 | -23 | Apple(5) +22 | Apple(5) | ^^^^^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 12 | use Fruit::Apple; | error[E0573]: expected type, found variant `Fruit::Apple` - --> $DIR/issue-35675.rs:28:33 + --> $DIR/issue-35675.rs:27:33 | -28 | fn should_return_fruit_too() -> Fruit::Apple { +27 | fn should_return_fruit_too() -> Fruit::Apple { | ^^^^^^^^^^^^ | | | not a type @@ -32,7 +31,6 @@ error[E0425]: cannot find function `Apple` in this scope | 31 | Apple(5) | ^^^^^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 12 | use Fruit::Apple; @@ -57,9 +55,9 @@ error[E0412]: cannot find type `Variant3` in this scope | help: you can try using the variant's enum: `x::Enum` error[E0573]: expected type, found variant `Some` - --> $DIR/issue-35675.rs:49:13 + --> $DIR/issue-35675.rs:50:13 | -49 | fn qux() -> Some { +50 | fn qux() -> Some { | ^^^^ not a type | = help: there is an enum variant `std::prelude::v1::Option::Some`, try using `std::prelude::v1::Option`? diff --git a/src/test/ui/issue-35976.rs b/src/test/ui/issue-35976.rs index 169d7b5591670..d45b0c5a0416c 100644 --- a/src/test/ui/issue-35976.rs +++ b/src/test/ui/issue-35976.rs @@ -23,7 +23,6 @@ mod private { fn bar(arg: Box) { arg.wait(); //~^ ERROR the `wait` method cannot be invoked on a trait object - //~| another candidate was found in the following trait, perhaps add a `use` for it: } fn main() { diff --git a/src/test/ui/issue-35976.stderr b/src/test/ui/issue-35976.stderr index 9fb67449734bc..146d0ff72d83f 100644 --- a/src/test/ui/issue-35976.stderr +++ b/src/test/ui/issue-35976.stderr @@ -3,9 +3,10 @@ error: the `wait` method cannot be invoked on a trait object | 24 | arg.wait(); | ^^^^ +help: another candidate was found in the following trait, perhaps add a `use` for it: + | +11 | use private::Future; | - = note: another candidate was found in the following trait, perhaps add a `use` for it: - candidate #1: `use private::Future;` error: aborting due to previous error diff --git a/src/test/ui/issue-36400.rs b/src/test/ui/issue-36400.rs new file mode 100644 index 0000000000000..fa4361e42aa5b --- /dev/null +++ b/src/test/ui/issue-36400.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f(x: &mut u32) {} + +fn main() { + let x = Box::new(3); + f(&mut *x); //~ ERROR cannot borrow immutable +} diff --git a/src/test/ui/issue-36400.stderr b/src/test/ui/issue-36400.stderr new file mode 100644 index 0000000000000..84e6855e23b46 --- /dev/null +++ b/src/test/ui/issue-36400.stderr @@ -0,0 +1,10 @@ +error[E0596]: cannot borrow immutable `Box` content `*x` as mutable + --> $DIR/issue-36400.rs:15:12 + | +14 | let x = Box::new(3); + | - consider changing this to `mut x` +15 | f(&mut *x); //~ ERROR cannot borrow immutable + | ^^ cannot borrow as mutable + +error: aborting due to previous error + diff --git a/src/test/ui/issue-37311-type-length-limit/issue-37311.rs b/src/test/ui/issue-37311-type-length-limit/issue-37311.rs index add96461f1bfe..1e05bdb0c6086 100644 --- a/src/test/ui/issue-37311-type-length-limit/issue-37311.rs +++ b/src/test/ui/issue-37311-type-length-limit/issue-37311.rs @@ -20,7 +20,7 @@ trait Foo { impl Foo for T { #[allow(unconditional_recursion)] - fn recurse(&self) { + fn recurse(&self) { //~ ERROR reached the type-length limit (self, self).recurse(); } } diff --git a/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr index b51b683a1ac3a..fe173867da109 100644 --- a/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr +++ b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr @@ -1,7 +1,7 @@ error: reached the type-length limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(), &()), &(&()...` --> $DIR/issue-37311.rs:23:5 | -23 | / fn recurse(&self) { +23 | / fn recurse(&self) { //~ ERROR reached the type-length limit 24 | | (self, self).recurse(); 25 | | } | |_____^ diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-1.rs b/src/test/ui/issue-40402-ref-hints/issue-40402-1.rs index 7efa3bd9d5b31..f2de2030bd196 100644 --- a/src/test/ui/issue-40402-ref-hints/issue-40402-1.rs +++ b/src/test/ui/issue-40402-ref-hints/issue-40402-1.rs @@ -16,5 +16,5 @@ struct Foo { fn main() { let mut f = Foo { v: Vec::new() }; f.v.push("hello".to_string()); - let e = f.v[0]; + let e = f.v[0]; //~ ERROR cannot move out of indexed content } diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-1.stderr b/src/test/ui/issue-40402-ref-hints/issue-40402-1.stderr index 56d0a5351ce83..173a60b0f0885 100644 --- a/src/test/ui/issue-40402-ref-hints/issue-40402-1.stderr +++ b/src/test/ui/issue-40402-ref-hints/issue-40402-1.stderr @@ -1,7 +1,7 @@ error[E0507]: cannot move out of indexed content --> $DIR/issue-40402-1.rs:19:13 | -19 | let e = f.v[0]; +19 | let e = f.v[0]; //~ ERROR cannot move out of indexed content | ^^^^^^ | | | cannot move out of indexed content diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-2.rs b/src/test/ui/issue-40402-ref-hints/issue-40402-2.rs index 76e038b696e8f..894923605c024 100644 --- a/src/test/ui/issue-40402-ref-hints/issue-40402-2.rs +++ b/src/test/ui/issue-40402-ref-hints/issue-40402-2.rs @@ -12,5 +12,5 @@ // are nested within a pattern fn main() { let x = vec![(String::new(), String::new())]; - let (a, b) = x[0]; + let (a, b) = x[0]; //~ ERROR cannot move out of indexed content } diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-2.stderr b/src/test/ui/issue-40402-ref-hints/issue-40402-2.stderr index 0060b683bba43..7b992e376dc74 100644 --- a/src/test/ui/issue-40402-ref-hints/issue-40402-2.stderr +++ b/src/test/ui/issue-40402-ref-hints/issue-40402-2.stderr @@ -1,7 +1,7 @@ error[E0507]: cannot move out of indexed content --> $DIR/issue-40402-2.rs:15:18 | -15 | let (a, b) = x[0]; +15 | let (a, b) = x[0]; //~ ERROR cannot move out of indexed content | - - ^^^^ cannot move out of indexed content | | | | | ...and here (use `ref b` or `ref mut b`) diff --git a/src/test/ui/issue-40782.rs b/src/test/ui/issue-40782.rs new file mode 100644 index 0000000000000..10dc177c7e95d --- /dev/null +++ b/src/test/ui/issue-40782.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + for i 0..2 { //~ ERROR missing `in` + } +} + diff --git a/src/test/ui/issue-40782.stderr b/src/test/ui/issue-40782.stderr new file mode 100644 index 0000000000000..543233e0cc620 --- /dev/null +++ b/src/test/ui/issue-40782.stderr @@ -0,0 +1,8 @@ +error: missing `in` in `for` loop + --> $DIR/issue-40782.rs:12:10 + | +12 | for i 0..2 { //~ ERROR missing `in` + | ^ help: try adding `in` here + +error: aborting due to previous error + diff --git a/src/test/ui/issue-42106.rs b/src/test/ui/issue-42106.rs new file mode 100644 index 0000000000000..f35eee186a2a5 --- /dev/null +++ b/src/test/ui/issue-42106.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn do_something(collection: &mut Vec) { + let _a = &collection; + collection.swap(1, 2); //~ ERROR also borrowed as immutable +} + +fn main() {} diff --git a/src/test/ui/issue-42106.stderr b/src/test/ui/issue-42106.stderr new file mode 100644 index 0000000000000..0f96377c062a8 --- /dev/null +++ b/src/test/ui/issue-42106.stderr @@ -0,0 +1,12 @@ +error[E0502]: cannot borrow `*collection` as mutable because `collection` is also borrowed as immutable + --> $DIR/issue-42106.rs:13:5 + | +12 | let _a = &collection; + | ---------- immutable borrow occurs here +13 | collection.swap(1, 2); //~ ERROR also borrowed as immutable + | ^^^^^^^^^^ mutable borrow occurs here +14 | } + | - immutable borrow ends here + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issue-42954.rs b/src/test/ui/issue-42954.rs index bdfdf44c0e28a..6fa2c69bf6696 100644 --- a/src/test/ui/issue-42954.rs +++ b/src/test/ui/issue-42954.rs @@ -10,7 +10,7 @@ macro_rules! is_plainly_printable { ($i: ident) => { - $i as u32 < 0 + $i as u32 < 0 //~ `<` is interpreted as a start of generic arguments }; } diff --git a/src/test/ui/issue-42954.stderr b/src/test/ui/issue-42954.stderr index 7287c8f37eb06..d0fc410c474a4 100644 --- a/src/test/ui/issue-42954.stderr +++ b/src/test/ui/issue-42954.stderr @@ -1,7 +1,7 @@ error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison --> $DIR/issue-42954.rs:13:19 | -13 | $i as u32 < 0 +13 | $i as u32 < 0 //~ `<` is interpreted as a start of generic arguments | --------- ^ - interpreted as generic arguments | | | | | not interpreted as comparison diff --git a/src/test/ui/issue-44023.rs b/src/test/ui/issue-44023.rs index 295d480828930..97b82dc58dcfc 100644 --- a/src/test/ui/issue-44023.rs +++ b/src/test/ui/issue-44023.rs @@ -12,5 +12,5 @@ pub fn main () {} -fn საჭმელად_გემრიელი_სადილი ( ) -> isize { +fn საჭმელად_გემრიელი_სადილი ( ) -> isize { //~ ERROR mismatched types } diff --git a/src/test/ui/issue-44023.stderr b/src/test/ui/issue-44023.stderr index a17512ba4abc4..fc6363dc921a2 100644 --- a/src/test/ui/issue-44023.stderr +++ b/src/test/ui/issue-44023.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/issue-44023.rs:15:42 | -15 | fn საჭმელად_გემრიელი_სადილი ( ) -> isize { +15 | fn საჭმელად_გემრიელი_სადილი ( ) -> isize { //~ ERROR mismatched types | __________________________________________^ 16 | | } | |_^ expected isize, found () diff --git a/src/test/ui/issue-44078.rs b/src/test/ui/issue-44078.rs index ef47214f2b393..356a7be0b419e 100644 --- a/src/test/ui/issue-44078.rs +++ b/src/test/ui/issue-44078.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - "😊""; + "😊""; //~ ERROR unterminated double quote } diff --git a/src/test/ui/issue-44078.stderr b/src/test/ui/issue-44078.stderr index 389f3b2479aa4..49e461bd18d18 100644 --- a/src/test/ui/issue-44078.stderr +++ b/src/test/ui/issue-44078.stderr @@ -1,8 +1,8 @@ error: unterminated double quote string --> $DIR/issue-44078.rs:12:8 | -12 | "😊""; - | ________^ +12 | "😊""; //~ ERROR unterminated double quote + | _________^ 13 | | } | |__^ diff --git a/src/test/ui/issue-44406.rs b/src/test/ui/issue-44406.rs new file mode 100644 index 0000000000000..8e99caff4efa2 --- /dev/null +++ b/src/test/ui/issue-44406.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! foo { + ($rest: tt) => { + bar(baz: $rest) + } +} + +fn main() { + foo!(true); //~ ERROR expected type, found keyword + //~^ ERROR expected identifier, found keyword +} diff --git a/src/test/ui/issue-44406.stderr b/src/test/ui/issue-44406.stderr new file mode 100644 index 0000000000000..2e71b001d7ac0 --- /dev/null +++ b/src/test/ui/issue-44406.stderr @@ -0,0 +1,17 @@ +error: expected identifier, found keyword `true` + --> $DIR/issue-44406.rs:18:10 + | +18 | foo!(true); //~ ERROR expected type, found keyword + | ^^^^ + +error: expected type, found keyword `true` + --> $DIR/issue-44406.rs:18:10 + | +13 | bar(baz: $rest) + | - help: did you mean to use `;` here? +... +18 | foo!(true); //~ ERROR expected type, found keyword + | ^^^^ expecting a type here because of type ascription + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.rs b/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.rs new file mode 100644 index 0000000000000..2fce8d723d39c --- /dev/null +++ b/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deny(unused_unsafe)] +fn main() { + let mut v = Vec::::with_capacity(24); + + unsafe { + let f = |v: &mut Vec<_>| { + unsafe { //~ ERROR unnecessary `unsafe` + v.set_len(24); + |w: &mut Vec| { unsafe { //~ ERROR unnecessary `unsafe` + w.set_len(32); + } }; + } + |x: &mut Vec| { unsafe { //~ ERROR unnecessary `unsafe` + x.set_len(40); + } }; + }; + + v.set_len(0); + f(&mut v); + } + + |y: &mut Vec| { unsafe { + y.set_len(48); + } }; +} diff --git a/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.stderr b/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.stderr new file mode 100644 index 0000000000000..abd875c48083d --- /dev/null +++ b/src/test/ui/issue-45107-unnecessary-unsafe-in-closure.stderr @@ -0,0 +1,72 @@ +error: unnecessary `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:17:13 + | +17 | / unsafe { //~ ERROR unnecessary `unsafe` +18 | | v.set_len(24); +19 | | |w: &mut Vec| { unsafe { //~ ERROR unnecessary `unsafe` +20 | | w.set_len(32); +21 | | } }; +22 | | } + | |_____________^ unnecessary `unsafe` block + | +note: lint level defined here + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:11:8 + | +11 | #[deny(unused_unsafe)] + | ^^^^^^^^^^^^^ +note: because it's nested under this `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:15:5 + | +15 | / unsafe { +16 | | let f = |v: &mut Vec<_>| { +17 | | unsafe { //~ ERROR unnecessary `unsafe` +18 | | v.set_len(24); +... | +29 | | f(&mut v); +30 | | } + | |_____^ + +error: unnecessary `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:19:38 + | +19 | |w: &mut Vec| { unsafe { //~ ERROR unnecessary `unsafe` + | ______________________________________^ +20 | | w.set_len(32); +21 | | } }; + | |_________________^ unnecessary `unsafe` block + | +note: because it's nested under this `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:15:5 + | +15 | / unsafe { +16 | | let f = |v: &mut Vec<_>| { +17 | | unsafe { //~ ERROR unnecessary `unsafe` +18 | | v.set_len(24); +... | +29 | | f(&mut v); +30 | | } + | |_____^ + +error: unnecessary `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:23:34 + | +23 | |x: &mut Vec| { unsafe { //~ ERROR unnecessary `unsafe` + | __________________________________^ +24 | | x.set_len(40); +25 | | } }; + | |_____________^ unnecessary `unsafe` block + | +note: because it's nested under this `unsafe` block + --> $DIR/issue-45107-unnecessary-unsafe-in-closure.rs:15:5 + | +15 | / unsafe { +16 | | let f = |v: &mut Vec<_>| { +17 | | unsafe { //~ ERROR unnecessary `unsafe` +18 | | v.set_len(24); +... | +29 | | f(&mut v); +30 | | } + | |_____^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/issue-45296.rs b/src/test/ui/issue-45296.rs new file mode 100644 index 0000000000000..965747cfa05ca --- /dev/null +++ b/src/test/ui/issue-45296.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let unused = (); + + #![allow(unused_variables)] //~ ERROR not permitted in this context +} diff --git a/src/test/ui/issue-45296.stderr b/src/test/ui/issue-45296.stderr new file mode 100644 index 0000000000000..45a80750de716 --- /dev/null +++ b/src/test/ui/issue-45296.stderr @@ -0,0 +1,10 @@ +error: an inner attribute is not permitted in this context + --> $DIR/issue-45296.rs:14:7 + | +14 | #![allow(unused_variables)] //~ ERROR not permitted in this context + | ^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: aborting due to previous error + diff --git a/src/test/ui/issue-45730.rs b/src/test/ui/issue-45730.rs new file mode 100644 index 0000000000000..d733c8e6de26f --- /dev/null +++ b/src/test/ui/issue-45730.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +fn main() { + let x: *const _ = 0 as _; //~ ERROR cannot cast + + let x: *const _ = 0 as *const _; //~ ERROR cannot cast + let y: Option<*const fmt::Debug> = Some(x) as _; + + let x = 0 as *const i32 as *const _ as *mut _; //~ ERROR cannot cast +} diff --git a/src/test/ui/issue-45730.stderr b/src/test/ui/issue-45730.stderr new file mode 100644 index 0000000000000..94d39239117ad --- /dev/null +++ b/src/test/ui/issue-45730.stderr @@ -0,0 +1,32 @@ +error[E0641]: cannot cast to a pointer of an unknown kind + --> $DIR/issue-45730.rs:13:23 + | +13 | let x: *const _ = 0 as _; //~ ERROR cannot cast + | ^^^^^- + | | + | help: consider giving more type information + | + = note: The type information given here is insufficient to check whether the pointer cast is valid + +error[E0641]: cannot cast to a pointer of an unknown kind + --> $DIR/issue-45730.rs:15:23 + | +15 | let x: *const _ = 0 as *const _; //~ ERROR cannot cast + | ^^^^^-------- + | | + | help: consider giving more type information + | + = note: The type information given here is insufficient to check whether the pointer cast is valid + +error[E0641]: cannot cast to a pointer of an unknown kind + --> $DIR/issue-45730.rs:18:13 + | +18 | let x = 0 as *const i32 as *const _ as *mut _; //~ ERROR cannot cast + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ + | | + | help: consider giving more type information + | + = note: The type information given here is insufficient to check whether the pointer cast is valid + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/issue-46186.rs b/src/test/ui/issue-46186.rs new file mode 100644 index 0000000000000..1440b9e8cdc5f --- /dev/null +++ b/src/test/ui/issue-46186.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Struct { + a: usize, +}; //~ ERROR expected item, found `;` + +fn main() {} diff --git a/src/test/ui/issue-46186.stderr b/src/test/ui/issue-46186.stderr new file mode 100644 index 0000000000000..3cc9531bb5b86 --- /dev/null +++ b/src/test/ui/issue-46186.stderr @@ -0,0 +1,8 @@ +error: expected item, found `;` + --> $DIR/issue-46186.rs:13:2 + | +13 | }; //~ ERROR expected item, found `;` + | ^ help: consider removing this semicolon + +error: aborting due to previous error + diff --git a/src/test/ui/issue-46332.rs b/src/test/ui/issue-46332.rs new file mode 100644 index 0000000000000..d094497e246d0 --- /dev/null +++ b/src/test/ui/issue-46332.rs @@ -0,0 +1,21 @@ +// 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. + +// Original Levenshtein distance for both of this is 1. We improved accuracy with +// additional case insensitive comparison. + +struct TyUint {} + +struct TyInt {} + +fn main() { + TyUInt {}; + //~^ ERROR cannot find struct, variant or union type `TyUInt` in this scope +} diff --git a/src/test/ui/issue-46332.stderr b/src/test/ui/issue-46332.stderr new file mode 100644 index 0000000000000..6aef84568353c --- /dev/null +++ b/src/test/ui/issue-46332.stderr @@ -0,0 +1,8 @@ +error[E0422]: cannot find struct, variant or union type `TyUInt` in this scope + --> $DIR/issue-46332.rs:19:5 + | +19 | TyUInt {}; + | ^^^^^^ did you mean `TyUint`? + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.rs b/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.rs new file mode 100644 index 0000000000000..5ded42e7c972c --- /dev/null +++ b/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + field: i32, +} + +fn foo2<'a>(a: &'a Foo, x: &i32) -> &'a i32 { + if true { + let p: &i32 = &a.field; + &*p + } else { + &*x //~ ERROR explicit lifetime + } +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.stderr b/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.stderr new file mode 100644 index 0000000000000..9bfa72c2f36ce --- /dev/null +++ b/src/test/ui/lifetime-errors/42701_one_named_and_one_anonymous.stderr @@ -0,0 +1,11 @@ +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/42701_one_named_and_one_anonymous.rs:20:9 + | +15 | fn foo2<'a>(a: &'a Foo, x: &i32) -> &'a i32 { + | - consider changing the type of `x` to `&'a i32` +... +20 | &*x //~ ERROR explicit lifetime + | ^^^ lifetime `'a` required + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.rs new file mode 100644 index 0000000000000..1705767834fb2 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Clone)] +enum Foo<'a> { + Bar(&'a str), +} + +impl<'a> Foo<'a> { + fn bar(&self, other: Foo) -> Foo<'a> { + match *self { + Foo::Bar(s) => { + if s == "test" { + other //~ ERROR explicit lifetime + } else { + self.clone() + } + } + } + } +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.stderr new file mode 100644 index 0000000000000..4c5e37b8f10fb --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-early-bound-in-struct.stderr @@ -0,0 +1,11 @@ +error[E0621]: explicit lifetime required in the type of `other` + --> $DIR/ex1-return-one-existing-name-early-bound-in-struct.rs:21:21 + | +17 | fn bar(&self, other: Foo) -> Foo<'a> { + | ----- consider changing the type of `other` to `Foo<'a>` +... +21 | other //~ ERROR explicit lifetime + | ^^^^^ lifetime `'a` required + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs index a1716c4e79792..964f2f1c003ec 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { - if x > y { x } else { y } + if x > y { x } else { y } //~ ERROR explicit lifetime } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr index 83716b7791d83..457e347faaa4d 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in the type of `x` | 11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { | - consider changing the type of `x` to `&'a i32` -12 | if x > y { x } else { y } +12 | if x > y { x } else { y } //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs index 7bd32d8761705..96d5c5bb16100 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 { - if x > y { x } else { y } + if x > y { x } else { y } //~ ERROR explicit lifetime } fn main () { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr index 6d5e94a5e78ad..8c3592379ef12 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in parameter type | 11 | fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 { | ------ consider changing type to `(&'a i32, &'a i32)` -12 | if x > y { x } else { y } +12 | if x > y { x } else { y } //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.rs index 8849f7084b3cd..5cf52fe79f014 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.rs @@ -11,7 +11,7 @@ trait Foo { fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { - if x > y { x } else { y } + if x > y { x } else { y } //~ ERROR explicit lifetime } } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.stderr index 4288fdf89a417..d5d1d16a4245f 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-2.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in the type of `x` | 13 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { | - consider changing the type of `x` to `&'a i32` -14 | if x > y { x } else { y } +14 | if x > y { x } else { y } //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs index 362290ff3fa7d..3727ddf91298e 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs @@ -15,7 +15,7 @@ struct Foo { impl Foo { fn foo<'a>(&'a self, x: &i32) -> &i32 { - if true { &self.field } else { x } + if true { &self.field } else { x } //~ ERROR explicit lifetime } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr index 95076bfbdc7da..23b9c0cf2506f 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr @@ -4,7 +4,7 @@ error[E0621]: explicit lifetime required in the type of `x` 16 | fn foo<'a>(&'a self, x: &i32) -> &i32 { | - consider changing the type of `x` to `&'a i32` 17 | -18 | if true { &self.field } else { x } +18 | if true { &self.field } else { x } //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.rs index 36d956a39966f..cec73d79ec21b 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.rs @@ -18,7 +18,7 @@ impl Foo for () { fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { - if x > y { x } else { y } + if x > y { x } else { y } //~ ERROR lifetime mismatch } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr index 9e4f6c421790f..f418e1c01f2ae 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr @@ -1,27 +1,13 @@ -error[E0312]: lifetime of reference outlives lifetime of borrowed content... +error[E0623]: lifetime mismatch --> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:21:20 | -21 | if x > y { x } else { y } - | ^ - | -note: ...the reference is valid for the lifetime 'a as defined on the method body at 19:5... - --> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:19:5 - | -19 | / fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { -20 | | -21 | | if x > y { x } else { y } -22 | | -23 | | } - | |_____^ -note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 19:5 - --> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:19:5 - | -19 | / fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { -20 | | -21 | | if x > y { x } else { y } -22 | | -23 | | } - | |_____^ +19 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { + | ---- ------- + | | + | this parameter and the return type are declared with different lifetimes... +20 | +21 | if x > y { x } else { y } //~ ERROR lifetime mismatch + | ^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs index 30239f4c0946c..5ee2663317e96 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { - if x > y { x } else { y } + if x > y { x } else { y } //~ ERROR explicit lifetime } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr index 5d1336c7c3a22..b28f102cd5ac1 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in the type of `y` | 11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { | - consider changing the type of `y` to `&'a i32` -12 | if x > y { x } else { y } +12 | if x > y { x } else { y } //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs index 96b733be9b4eb..4d57c61ba9e2e 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs @@ -15,7 +15,7 @@ struct Foo { impl Foo { fn foo<'a>(&self, x: &'a i32) -> &i32 { - x + x //~ ERROR lifetime mismatch } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr index e3fd0192053b9..d26cb6be709b1 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr @@ -1,27 +1,13 @@ -error[E0312]: lifetime of reference outlives lifetime of borrowed content... +error[E0623]: lifetime mismatch --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:18:5 | -18 | x - | ^ - | -note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:3... - --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3 - | -16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 { -17 | | -18 | | x -19 | | -20 | | } - | |___^ -note: ...but the borrowed content is only valid for the lifetime 'a as defined on the method body at 16:3 - --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3 - | -16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 { -17 | | -18 | | x -19 | | -20 | | } - | |___^ +16 | fn foo<'a>(&self, x: &'a i32) -> &i32 { + | ------- ---- + | | + | this parameter and the return type are declared with different lifetimes... +17 | +18 | x //~ ERROR lifetime mismatch + | ^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.rs index a8ce60c47b6f5..a6ccf4a53d155 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.rs @@ -15,7 +15,7 @@ struct Foo { impl Foo { fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { - if true { x } else { self } + if true { x } else { self } //~ ERROR lifetime mismatch } } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr index 8551f015db527..0430e4c271507 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr @@ -1,27 +1,13 @@ -error[E0312]: lifetime of reference outlives lifetime of borrowed content... +error[E0623]: lifetime mismatch --> $DIR/ex1-return-one-existing-name-self-is-anon.rs:18:30 | -18 | if true { x } else { self } - | ^^^^ - | -note: ...the reference is valid for the lifetime 'a as defined on the method body at 16:5... - --> $DIR/ex1-return-one-existing-name-self-is-anon.rs:16:5 - | -16 | / fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { -17 | | -18 | | if true { x } else { self } -19 | | -20 | | } - | |_____^ -note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 16:5 - --> $DIR/ex1-return-one-existing-name-self-is-anon.rs:16:5 - | -16 | / fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { -17 | | -18 | | if true { x } else { self } -19 | | -20 | | } - | |_____^ +16 | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { + | ----- ------- + | | + | this parameter and the return type are declared with different lifetimes... +17 | +18 | if true { x } else { self } //~ ERROR lifetime mismatch + | ^^^^ ...but data from `self` is returned here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs index 098950e13b315..7f5b23728fd31 100644 --- a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs +++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(x: &i32, y: &i32) -> &i32 { +fn foo(x: &i32, y: &i32) -> &i32 { //~ ERROR missing lifetime if x > y { x } else { y } } diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr index fccc44caac81a..7cd5ca65981b7 100644 --- a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr +++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr @@ -1,7 +1,7 @@ error[E0106]: missing lifetime specifier --> $DIR/ex1b-return-no-names-if-else.rs:11:29 | -11 | fn foo(x: &i32, y: &i32) -> &i32 { +11 | fn foo(x: &i32, y: &i32) -> &i32 { //~ ERROR missing lifetime | ^ expected lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs index dd34e1aa6d9d2..f35a7555d708b 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs @@ -13,7 +13,7 @@ struct Ref<'a, T: 'a> { } fn foo<'a>(x: Ref, y: &mut Vec>) { - y.push(x); + y.push(x); //~ ERROR explicit lifetime } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr index 8dba0c33f201a..7abc093512b46 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in the type of `x` | 15 | fn foo<'a>(x: Ref, y: &mut Vec>) { | - consider changing the type of `x` to `Ref<'a, i32>` -16 | y.push(x); +16 | y.push(x); //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.rs similarity index 92% rename from src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.rs rename to src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.rs index 5d1820082093e..18a720f345d7f 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.rs +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.rs @@ -14,7 +14,7 @@ fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T) where i32: Foo<'a>, u32: Foo<'b> { - x.push(y); + x.push(y); //~ ERROR explicit lifetime required } fn main() { let x = baz; diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.stderr new file mode 100644 index 0000000000000..ca522596fbff8 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-early-bound.stderr @@ -0,0 +1,11 @@ +error[E0621]: explicit lifetime required in the type of `y` + --> $DIR/ex2a-push-one-existing-name-early-bound.rs:17:12 + | +13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T) + | - consider changing the type of `y` to `&'a T` +... +17 | x.push(y); //~ ERROR explicit lifetime required + | ^ lifetime `'a` required + +error: aborting due to previous error + diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs index 71a1c865e0995..1834395bd3b82 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs @@ -13,7 +13,7 @@ struct Ref<'a, T: 'a> { } fn foo<'a>(x: &mut Vec>, y: Ref) { - x.push(y); + x.push(y); //~ ERROR explicit lifetime } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr index e529d6ffe46b4..5d8f2c1decb2c 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr @@ -3,7 +3,7 @@ error[E0621]: explicit lifetime required in the type of `y` | 15 | fn foo<'a>(x: &mut Vec>, y: Ref) { | - consider changing the type of `y` to `Ref<'a, i32>` -16 | x.push(y); +16 | x.push(y); //~ ERROR explicit lifetime | ^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs index 09038d8ce9027..6cf626adf82e3 100644 --- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs @@ -13,7 +13,7 @@ struct Ref<'a, T: 'a> { } fn foo(x: &mut Vec>, y: Ref) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr index 1ee0099799760..69ff29db3570a 100644 --- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 15 | fn foo(x: &mut Vec>, y: Ref) { | -------- -------- these two types are declared with different lifetimes... -16 | x.push(y); +16 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs index cb083f778deeb..36bd1c32286e1 100644 --- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs @@ -14,7 +14,7 @@ struct Ref<'a, T: 'a> { fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { let z = Ref { data: y.data }; - x.push(z); + x.push(z); //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr index 7356fc11862f6..dacb0708b0580 100644 --- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr @@ -1,35 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements - --> $DIR/ex2c-push-inference-variable.rs:16:13 - | -16 | let z = Ref { data: y.data }; - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime 'c as defined on the function body at 15:1... - --> $DIR/ex2c-push-inference-variable.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let z = Ref { data: y.data }; -17 | | x.push(z); -18 | | } - | |_^ -note: ...so that reference does not outlive borrowed content - --> $DIR/ex2c-push-inference-variable.rs:16:25 - | -16 | let z = Ref { data: y.data }; - | ^^^^^^ -note: but, the lifetime must be valid for the lifetime 'b as defined on the function body at 15:1... - --> $DIR/ex2c-push-inference-variable.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let z = Ref { data: y.data }; -17 | | x.push(z); -18 | | } - | |_^ -note: ...so that expression is assignable (expected Ref<'b, _>, found Ref<'_, _>) +error[E0623]: lifetime mismatch --> $DIR/ex2c-push-inference-variable.rs:17:12 | -17 | x.push(z); - | ^ +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ------------ ------------ these two types are declared with different lifetimes... +16 | let z = Ref { data: y.data }; +17 | x.push(z); //~ ERROR lifetime mismatch + | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs index bcb7583beefcf..96316819e937f 100644 --- a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs @@ -13,7 +13,7 @@ struct Ref<'a, T: 'a> { } fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - let a: &mut Vec> = x; + let a: &mut Vec> = x; //~ ERROR lifetime mismatch let b = Ref { data: y.data }; a.push(b); } diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr index 38b0acf9339e0..e30355891ee78 100644 --- a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr @@ -1,37 +1,10 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements - --> $DIR/ex2d-push-inference-variable-2.rs:17:13 - | -17 | let b = Ref { data: y.data }; - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime 'c as defined on the function body at 15:1... - --> $DIR/ex2d-push-inference-variable-2.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let a: &mut Vec> = x; -17 | | let b = Ref { data: y.data }; -18 | | a.push(b); -19 | | } - | |_^ -note: ...so that reference does not outlive borrowed content - --> $DIR/ex2d-push-inference-variable-2.rs:17:25 - | -17 | let b = Ref { data: y.data }; - | ^^^^^^ -note: but, the lifetime must be valid for the lifetime 'b as defined on the function body at 15:1... - --> $DIR/ex2d-push-inference-variable-2.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let a: &mut Vec> = x; -17 | | let b = Ref { data: y.data }; -18 | | a.push(b); -19 | | } - | |_^ -note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) +error[E0623]: lifetime mismatch --> $DIR/ex2d-push-inference-variable-2.rs:16:33 | -16 | let a: &mut Vec> = x; - | ^ +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ------------ ------------ these two types are declared with different lifetimes... +16 | let a: &mut Vec> = x; //~ ERROR lifetime mismatch + | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs index 2d05adb7ecd37..9352ebc77f5b2 100644 --- a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs @@ -13,7 +13,7 @@ struct Ref<'a, T: 'a> { } fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { - let a: &mut Vec> = x; + let a: &mut Vec> = x; //~ ERROR lifetime mismatch let b = Ref { data: y.data }; Vec::push(a, b); } diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr index 035e516e8628e..841555c1fcb38 100644 --- a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr @@ -1,37 +1,10 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements - --> $DIR/ex2e-push-inference-variable-3.rs:17:13 - | -17 | let b = Ref { data: y.data }; - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime 'c as defined on the function body at 15:1... - --> $DIR/ex2e-push-inference-variable-3.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let a: &mut Vec> = x; -17 | | let b = Ref { data: y.data }; -18 | | Vec::push(a, b); -19 | | } - | |_^ -note: ...so that reference does not outlive borrowed content - --> $DIR/ex2e-push-inference-variable-3.rs:17:25 - | -17 | let b = Ref { data: y.data }; - | ^^^^^^ -note: but, the lifetime must be valid for the lifetime 'b as defined on the function body at 15:1... - --> $DIR/ex2e-push-inference-variable-3.rs:15:1 - | -15 | / fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { -16 | | let a: &mut Vec> = x; -17 | | let b = Ref { data: y.data }; -18 | | Vec::push(a, b); -19 | | } - | |_^ -note: ...so that expression is assignable (expected &mut std::vec::Vec>, found &mut std::vec::Vec>) +error[E0623]: lifetime mismatch --> $DIR/ex2e-push-inference-variable-3.rs:16:33 | -16 | let a: &mut Vec> = x; - | ^ +15 | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { + | ------------ ------------ these two types are declared with different lifetimes... +16 | let a: &mut Vec> = x; //~ ERROR lifetime mismatch + | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs index 905eae18d1807..5d490824d02f9 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo((v, w): (&u8, &u8), x: &u8) { - v = x; + v = x; //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr index 74a40c87c2fb1..5e1a4593ae459 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 11 | fn foo((v, w): (&u8, &u8), x: &u8) { | --- --- these two types are declared with different lifetimes... -12 | v = x; +12 | v = x; //~ ERROR lifetime mismatch | ^ ...but data from `x` flows here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs index 51271243bdfdc..fe6b40c05a603 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs @@ -9,7 +9,8 @@ // except according to those terms. fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { - z.push((x,y)); + z.push((x,y)); //~ ERROR lifetime mismatch + //~^ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr index 898866c75f214..b5b90c077d064 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { | --- --- these two types are declared with different lifetimes... -12 | z.push((x,y)); +12 | z.push((x,y)); //~ ERROR lifetime mismatch | ^ ...but data flows into `z` here error[E0623]: lifetime mismatch @@ -11,7 +11,7 @@ error[E0623]: lifetime mismatch | 11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { | --- --- these two types are declared with different lifetimes... -12 | z.push((x,y)); +12 | z.push((x,y)); //~ ERROR lifetime mismatch | ^ ...but data flows into `z` here error: aborting due to 2 previous errors diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.rs index 2fbf31aead5ed..f16120ddc22a0 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.rs @@ -13,7 +13,7 @@ struct Ref<'a, 'b> { } fn foo(mut x: Ref, y: Ref) { - x.b = y.b; + x.b = y.b; //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr index 26f31defc9eb1..e7317e63ab472 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 15 | fn foo(mut x: Ref, y: Ref) { | --- --- these two types are declared with different lifetimes... -16 | x.b = y.b; +16 | x.b = y.b; //~ ERROR lifetime mismatch | ^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.rs index 120a7ca74aeee..78e6dc2d3e75f 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.rs @@ -13,7 +13,7 @@ struct Ref<'a, 'b> { } fn foo(mut x: Ref) { - x.a = x.b; + x.a = x.b; //~ ERROR lifetime mismatch } -fn main() {} \ No newline at end of file +fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr index 1b5ac7c7b57ee..71eef13a67db7 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr @@ -4,8 +4,8 @@ error[E0623]: lifetime mismatch 15 | fn foo(mut x: Ref) { | --- | | - | this type was declared with multiple lifetimes... -16 | x.a = x.b; + | this type is declared with multiple lifetimes... +16 | x.a = x.b; //~ ERROR lifetime mismatch | ^^^ ...but data with one lifetime flows into the other here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.rs index 606e611865fc7..78e6dc2d3e75f 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.rs @@ -13,7 +13,7 @@ struct Ref<'a, 'b> { } fn foo(mut x: Ref) { - x.a = x.b; + x.a = x.b; //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.stderr index 689a1ac292b33..61b59b8f121c1 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-4.stderr @@ -4,8 +4,8 @@ error[E0623]: lifetime mismatch 15 | fn foo(mut x: Ref) { | --- | | - | this type was declared with multiple lifetimes... -16 | x.a = x.b; + | this type is declared with multiple lifetimes... +16 | x.a = x.b; //~ ERROR lifetime mismatch | ^^^ ...but data with one lifetime flows into the other here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.rs index 0fef709ae5363..ffec0e8d5bbb6 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.rs @@ -15,7 +15,7 @@ fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) where &'a (): Sized, &'b u32: Sized { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr index 59bf5d17222b7..0b1b01d86b8ea 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr @@ -4,7 +4,7 @@ error[E0623]: lifetime mismatch 14 | fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) | ------- ------- these two types are declared with different lifetimes... ... -18 | x.push(y); +18 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.rs index a91d0b55dc7ab..16d18f309516c 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.rs @@ -12,7 +12,7 @@ struct Ref<'a> { } fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr index 878351210681b..36885b7e076e4 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 14 | fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) { | ------- ------- these two types are declared with different lifetimes... -15 | x.push(y); +15 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.rs index 67ba8ee532ad0..3b90b3474a140 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.rs @@ -12,7 +12,7 @@ struct Ref<'a> { } fn foo(mut x: Vec, y: Ref) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr index 6ad795400b334..961b8e310fe16 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 14 | fn foo(mut x: Vec, y: Ref) { | --- --- these two types are declared with different lifetimes... -15 | x.push(y); +15 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.stderr deleted file mode 100644 index 58f2cb94cec1d..0000000000000 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-earlybound-regions.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0623]: lifetime mismatch - --> $DIR/ex3-both-anon-regions-earlybound-regions.rs:17:12 - | -13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T) - | ----- -- these two types are declared with different lifetimes... -... -17 | x.push(y); - | ^ ...but data from `y` flows into `x` here - -error: aborting due to previous error - diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.rs index 5abfc983f883a..966b4f0b6c329 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } -fn main() { } \ No newline at end of file +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr index be628f226d3dd..b70d26a99d732 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 11 | fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) { | ------ ------ these two types are declared with different lifetimes... -12 | x.push(y); +12 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.rs index a8b1f53fc98d6..055c3f804685a 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.rs @@ -11,7 +11,7 @@ struct Ref<'a, 'b> { a: &'a u32, b: &'b u32 } fn foo(mut x: Ref, y: &u32) { - y = x.b; + y = x.b; //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr index 31c7ebf6504cc..7a50371306369 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr @@ -5,7 +5,7 @@ error[E0623]: lifetime mismatch | --- ---- | | | these two types are declared with different lifetimes... -14 | y = x.b; +14 | y = x.b; //~ ERROR lifetime mismatch | ^^^ ...but data from `x` flows into `y` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.rs index 4933dbb7e7a7a..474da4a7d1613 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.rs @@ -11,7 +11,7 @@ struct Ref<'a, 'b> { a: &'a u32, b: &'b u32 } fn foo(mut y: Ref, x: &u32) { - y.b = x; + y.b = x; //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr index d54b526aef97f..66155bec0bb9a 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 13 | fn foo(mut y: Ref, x: &u32) { | --- ---- these two types are declared with different lifetimes... -14 | y.b = x; +14 | y.b = x; //~ ERROR lifetime mismatch | ^ ...but data from `x` flows into `y` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.rs index 4933dbb7e7a7a..474da4a7d1613 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.rs @@ -11,7 +11,7 @@ struct Ref<'a, 'b> { a: &'a u32, b: &'b u32 } fn foo(mut y: Ref, x: &u32) { - y.b = x; + y.b = x; //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr index 40f026bcb1b58..d47cffbc6222f 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 13 | fn foo(mut y: Ref, x: &u32) { | --- ---- these two types are declared with different lifetimes... -14 | y.b = x; +14 | y.b = x; //~ ERROR lifetime mismatch | ^ ...but data from `x` flows into `y` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.rs index e1594b1a277cd..1ffaec7ba00d3 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.rs @@ -14,7 +14,7 @@ struct Ref<'a, 'b> { } fn foo(mut x: Ref, y: &u32) { - x.b = y; + x.b = y; //~ ERROR lifetime mismatch } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr index bb7b9ea68436c..43c85e43e7738 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 16 | fn foo(mut x: Ref, y: &u32) { | --- ---- these two types are declared with different lifetimes... -17 | x.b = y; +17 | x.b = y; //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs index 0dc257ac0921e..97af35980105d 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs @@ -14,7 +14,7 @@ struct Foo { impl Foo { fn foo<'a>(&self, x: &i32) -> &i32 { - x + x //~ ERROR lifetime mismatch } } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr index 890f9b311e7d2..73927f0c1d31f 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr @@ -1,23 +1,12 @@ -error[E0312]: lifetime of reference outlives lifetime of borrowed content... +error[E0623]: lifetime mismatch --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:17:5 | -17 | x - | ^ - | -note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:3... - --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:16:3 - | -16 | / fn foo<'a>(&self, x: &i32) -> &i32 { -17 | | x -18 | | } - | |___^ -note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the method body at 16:3 - --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:16:3 - | -16 | / fn foo<'a>(&self, x: &i32) -> &i32 { -17 | | x -18 | | } - | |___^ +16 | fn foo<'a>(&self, x: &i32) -> &i32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +17 | x //~ ERROR lifetime mismatch + | ^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.rs index 0940ce15d1e76..e6f4f0966ca62 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.rs @@ -14,7 +14,7 @@ struct Foo { impl Foo { fn foo<'a>(&self, x: &Foo) -> &Foo { - if true { x } else { self } + if true { x } else { self } //~ ERROR lifetime mismatch } } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr index 43f00c32c6285..edb7ce2d6e9f7 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr @@ -1,23 +1,12 @@ -error[E0312]: lifetime of reference outlives lifetime of borrowed content... +error[E0623]: lifetime mismatch --> $DIR/ex3-both-anon-regions-self-is-anon.rs:17:19 | -17 | if true { x } else { self } - | ^ - | -note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:5... - --> $DIR/ex3-both-anon-regions-self-is-anon.rs:16:5 - | -16 | / fn foo<'a>(&self, x: &Foo) -> &Foo { -17 | | if true { x } else { self } -18 | | } - | |_____^ -note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the method body at 16:5 - --> $DIR/ex3-both-anon-regions-self-is-anon.rs:16:5 - | -16 | / fn foo<'a>(&self, x: &Foo) -> &Foo { -17 | | if true { x } else { self } -18 | | } - | |_____^ +16 | fn foo<'a>(&self, x: &Foo) -> &Foo { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +17 | if true { x } else { self } //~ ERROR lifetime mismatch + | ^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.rs index 9220c34489fab..db53acf5afceb 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) { - y.push(z); + y.push(z); //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr index adfc4dc0c276e..065b669e6929b 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 10 | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) { | --- --- these two types are declared with different lifetimes... -11 | y.push(z); +11 | y.push(z); //~ ERROR lifetime mismatch | ^ ...but data from `z` flows into `y` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.rs index 3a7ba415c0ded..b3ef06f189897 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.rs @@ -12,7 +12,7 @@ trait Foo { } impl Foo for () { fn foo(x: &mut Vec<&u8>, y: &u8) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } } fn main() {} diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr index 9591df8e8aad2..20badfccd8e14 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 14 | fn foo(x: &mut Vec<&u8>, y: &u8) { | --- --- these two types are declared with different lifetimes... -15 | x.push(y); +15 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.rs index 78a6ad54eae4f..ebde6a3b53ff6 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. fn foo(x:Box , y: Vec<&u8>, z: &u8) { - y.push(z); + y.push(z); //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr index ce766b2e406a2..b8a4d9ed24ec9 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 10 | fn foo(x:Box , y: Vec<&u8>, z: &u8) { | --- --- these two types are declared with different lifetimes... -11 | y.push(z); +11 | y.push(z); //~ ERROR lifetime mismatch | ^ ...but data from `z` flows into `y` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs index be48d07b94e06..f88eca494eb5a 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo(x: &mut Vec<&u8>, y: &u8) { - x.push(y); + x.push(y); //~ ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr index d3291063859ca..2a30172c43a18 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr @@ -3,7 +3,7 @@ error[E0623]: lifetime mismatch | 11 | fn foo(x: &mut Vec<&u8>, y: &u8) { | --- --- these two types are declared with different lifetimes... -12 | x.push(y); +12 | x.push(y); //~ ERROR lifetime mismatch | ^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.rs b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.rs new file mode 100644 index 0000000000000..d4ef87cdd7681 --- /dev/null +++ b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.rs @@ -0,0 +1,54 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME: Change to UI Test +// Check notes are placed on an assignment that can actually preceed the current assigmnent +// Don't emmit a first assignment for assignment in a loop. + +// compile-flags: -Zborrowck=compare + +fn test() { + let x; + if true { + x = 1; + } else { + x = 2; + x = 3; //~ ERROR (Ast) [E0384] + //~^ ERROR (Mir) [E0384] + } +} + +fn test_in_loop() { + loop { + let x; + if true { + x = 1; + } else { + x = 2; + x = 3; //~ ERROR (Ast) [E0384] + //~^ ERROR (Mir) [E0384] + } + } +} + +fn test_using_loop() { + let x; + loop { + if true { + x = 1; //~ ERROR (Ast) [E0384] + //~^ ERROR (Mir) [E0384] + } else { + x = 2; //~ ERROR (Ast) [E0384] + //~^ ERROR (Mir) [E0384] + } + } +} + +fn main() {} diff --git a/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr new file mode 100644 index 0000000000000..b8f738e445e06 --- /dev/null +++ b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr @@ -0,0 +1,64 @@ +error[E0384]: cannot assign twice to immutable variable `x` (Ast) + --> $DIR/liveness-assign-imm-local-notes.rs:23:9 + | +22 | x = 2; + | ----- first assignment to `x` +23 | x = 3; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Ast) + --> $DIR/liveness-assign-imm-local-notes.rs:35:13 + | +34 | x = 2; + | ----- first assignment to `x` +35 | x = 3; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Ast) + --> $DIR/liveness-assign-imm-local-notes.rs:45:13 + | +45 | x = 1; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Ast) + --> $DIR/liveness-assign-imm-local-notes.rs:48:13 + | +45 | x = 1; //~ ERROR (Ast) [E0384] + | ----- first assignment to `x` +... +48 | x = 2; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Mir) + --> $DIR/liveness-assign-imm-local-notes.rs:23:9 + | +22 | x = 2; + | ----- first assignment to `x` +23 | x = 3; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Mir) + --> $DIR/liveness-assign-imm-local-notes.rs:35:13 + | +34 | x = 2; + | ----- first assignment to `x` +35 | x = 3; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Mir) + --> $DIR/liveness-assign-imm-local-notes.rs:45:13 + | +45 | x = 1; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error[E0384]: cannot assign twice to immutable variable `x` (Mir) + --> $DIR/liveness-assign-imm-local-notes.rs:48:13 + | +45 | x = 1; //~ ERROR (Ast) [E0384] + | ----- first assignment to `x` +... +48 | x = 2; //~ ERROR (Ast) [E0384] + | ^^^^^ cannot assign twice to immutable variable + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/lifetimes/borrowck-let-suggestion.rs b/src/test/ui/lifetimes/borrowck-let-suggestion.rs index 1c904648f9e7b..7bf0ed34cbfd2 100644 --- a/src/test/ui/lifetimes/borrowck-let-suggestion.rs +++ b/src/test/ui/lifetimes/borrowck-let-suggestion.rs @@ -9,7 +9,7 @@ // except according to those terms. fn f() { - let x = vec![1].iter(); + let x = vec![1].iter(); //~ ERROR does not live long enough } fn main() { diff --git a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr index 6316c06666003..675974d617cb2 100644 --- a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr @@ -1,7 +1,7 @@ error[E0597]: borrowed value does not live long enough --> $DIR/borrowck-let-suggestion.rs:12:27 | -12 | let x = vec![1].iter(); +12 | let x = vec![1].iter(); //~ ERROR does not live long enough | ------- ^ temporary value dropped here while still borrowed | | | temporary value created here @@ -9,7 +9,7 @@ error[E0597]: borrowed value does not live long enough | - temporary value needs to live until here | = note: consider using a `let` binding to increase its lifetime - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs index 465b42710352d..58c33af0ddd27 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs @@ -16,6 +16,7 @@ trait Collection { fn len(&self) -> usize; } struct List<'a, T: ListItem<'a>> { slice: &'a [T] + //~^ ERROR may not live long enough } impl<'a, T: ListItem<'a>> Collection for List<'a, T> { @@ -26,6 +27,25 @@ impl<'a, T: ListItem<'a>> Collection for List<'a, T> { struct Foo { foo: &'static T + //~^ ERROR may not live long enough +} + +trait X: Sized { + fn foo<'a, L: X<&'a Nested>>(); + //~^ ERROR may not live long enough + // check that we give a sane error for `Self` + fn bar<'a, L: X<&'a Nested>>(); + //~^ ERROR may not live long enough +} + +struct Nested(K); +impl Nested { + fn generic_in_parent<'a, L: X<&'a Nested>>() { + //~^ ERROR may not live long enough + } + fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { + //~^ ERROR may not live long enough + } } fn main() {} diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr index e17a660c59170..342c6ab8f1624 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr @@ -13,18 +13,82 @@ note: ...so that the reference type `&'a [T]` does not outlive the data it point | ^^^^^^^^^^^^^^ error[E0310]: the parameter type `T` may not live long enough - --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:29:5 | -27 | struct Foo { +28 | struct Foo { | - help: consider adding an explicit lifetime bound `T: 'static`... -28 | foo: &'static T +29 | foo: &'static T | ^^^^^^^^^^^^^^^ | note: ...so that the reference type `&'static T` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:29:5 | -28 | foo: &'static T +29 | foo: &'static T | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:34:5 + | +33 | trait X: Sized { + | - help: consider adding an explicit lifetime bound `K: 'a`... +34 | fn foo<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:34:5 + | +34 | fn foo<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0309]: the parameter type `Self` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + | +37 | fn bar<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `Self: 'a`... +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + | +37 | fn bar<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:43:5 + | +42 | impl Nested { + | - help: consider adding an explicit lifetime bound `K: 'a`... +43 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +44 | | //~^ ERROR may not live long enough +45 | | } + | |_____^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:43:5 + | +43 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +44 | | //~^ ERROR may not live long enough +45 | | } + | |_____^ + +error[E0309]: the parameter type `M` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:46:5 + | +46 | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { + | ^ -- help: consider adding an explicit lifetime bound `M: 'a`... + | _____| + | | +47 | | //~^ ERROR may not live long enough +48 | | } + | |_____^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:46:5 + | +46 | / fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { +47 | | //~^ ERROR may not live long enough +48 | | } + | |_____^ + +error: aborting due to 6 previous errors diff --git a/src/test/ui/lint/command-line-lint-group-deny.rs b/src/test/ui/lint/command-line-lint-group-deny.rs index 1248601c1e44a..6ffc9b5aa1701 100644 --- a/src/test/ui/lint/command-line-lint-group-deny.rs +++ b/src/test/ui/lint/command-line-lint-group-deny.rs @@ -11,5 +11,5 @@ // compile-flags: -D bad-style fn main() { - let _InappropriateCamelCasing = true; + let _InappropriateCamelCasing = true; //~ ERROR should have a snake } diff --git a/src/test/ui/lint/command-line-lint-group-deny.stderr b/src/test/ui/lint/command-line-lint-group-deny.stderr index 23fac66cc6c98..a6182de0a7587 100644 --- a/src/test/ui/lint/command-line-lint-group-deny.stderr +++ b/src/test/ui/lint/command-line-lint-group-deny.stderr @@ -1,7 +1,7 @@ error: variable `_InappropriateCamelCasing` should have a snake case name such as `_inappropriate_camel_casing` --> $DIR/command-line-lint-group-deny.rs:14:9 | -14 | let _InappropriateCamelCasing = true; +14 | let _InappropriateCamelCasing = true; //~ ERROR should have a snake | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D non-snake-case` implied by `-D bad-style` diff --git a/src/test/ui/lint/command-line-lint-group-forbid.rs b/src/test/ui/lint/command-line-lint-group-forbid.rs index ae16db44864c9..eb4645a4fc8dd 100644 --- a/src/test/ui/lint/command-line-lint-group-forbid.rs +++ b/src/test/ui/lint/command-line-lint-group-forbid.rs @@ -11,5 +11,5 @@ // compile-flags: -F bad-style fn main() { - let _InappropriateCamelCasing = true; + let _InappropriateCamelCasing = true; //~ ERROR should have a snake } diff --git a/src/test/ui/lint/command-line-lint-group-forbid.stderr b/src/test/ui/lint/command-line-lint-group-forbid.stderr index 0babd7f6fe47a..7ae6734c8a397 100644 --- a/src/test/ui/lint/command-line-lint-group-forbid.stderr +++ b/src/test/ui/lint/command-line-lint-group-forbid.stderr @@ -1,7 +1,7 @@ error: variable `_InappropriateCamelCasing` should have a snake case name such as `_inappropriate_camel_casing` --> $DIR/command-line-lint-group-forbid.rs:14:9 | -14 | let _InappropriateCamelCasing = true; +14 | let _InappropriateCamelCasing = true; //~ ERROR should have a snake | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-F non-snake-case` implied by `-F bad-style` diff --git a/src/test/ui/lint/lint-group-style.rs b/src/test/ui/lint/lint-group-style.rs index 2bd760e417a89..9f33f57f48a28 100644 --- a/src/test/ui/lint/lint-group-style.rs +++ b/src/test/ui/lint/lint-group-style.rs @@ -11,7 +11,7 @@ #![deny(bad_style)] #![allow(dead_code)] -fn CamelCase() {} +fn CamelCase() {} //~ ERROR should have a snake #[allow(bad_style)] mod test { @@ -19,17 +19,17 @@ mod test { #[forbid(bad_style)] mod bad { - fn CamelCase() {} + fn CamelCase() {} //~ ERROR should have a snake - static bad: isize = 1; + static bad: isize = 1; //~ ERROR should have an upper } mod warn { #![warn(bad_style)] - fn CamelCase() {} + fn CamelCase() {} //~ WARN should have a snake - struct snake_case; + struct snake_case; //~ WARN should have a camel } } diff --git a/src/test/ui/lint/lint-group-style.stderr b/src/test/ui/lint/lint-group-style.stderr index 862e94b873a0c..3dfe2cee991a0 100644 --- a/src/test/ui/lint/lint-group-style.stderr +++ b/src/test/ui/lint/lint-group-style.stderr @@ -1,7 +1,7 @@ error: function `CamelCase` should have a snake case name such as `camel_case` --> $DIR/lint-group-style.rs:14:1 | -14 | fn CamelCase() {} +14 | fn CamelCase() {} //~ ERROR should have a snake | ^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -14,7 +14,7 @@ note: lint level defined here error: function `CamelCase` should have a snake case name such as `camel_case` --> $DIR/lint-group-style.rs:22:9 | -22 | fn CamelCase() {} +22 | fn CamelCase() {} //~ ERROR should have a snake | ^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -27,7 +27,7 @@ note: lint level defined here error: static variable `bad` should have an upper case name such as `BAD` --> $DIR/lint-group-style.rs:24:9 | -24 | static bad: isize = 1; +24 | static bad: isize = 1; //~ ERROR should have an upper | ^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -40,7 +40,7 @@ note: lint level defined here warning: function `CamelCase` should have a snake case name such as `camel_case` --> $DIR/lint-group-style.rs:30:9 | -30 | fn CamelCase() {} +30 | fn CamelCase() {} //~ WARN should have a snake | ^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -53,7 +53,7 @@ note: lint level defined here warning: type `snake_case` should have a camel case name such as `SnakeCase` --> $DIR/lint-group-style.rs:32:9 | -32 | struct snake_case; +32 | struct snake_case; //~ WARN should have a camel | ^^^^^^^^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/lint/outer-forbid.rs b/src/test/ui/lint/outer-forbid.rs index a79dacbc1c959..d72f307b46124 100644 --- a/src/test/ui/lint/outer-forbid.rs +++ b/src/test/ui/lint/outer-forbid.rs @@ -16,13 +16,13 @@ #![forbid(unused, non_snake_case)] -#[allow(unused_variables)] +#[allow(unused_variables)] //~ ERROR overruled fn foo() {} -#[allow(unused)] +#[allow(unused)] //~ ERROR overruled fn bar() {} -#[allow(bad_style)] +#[allow(bad_style)] //~ ERROR overruled fn main() { println!("hello forbidden world") } diff --git a/src/test/ui/lint/outer-forbid.stderr b/src/test/ui/lint/outer-forbid.stderr index 67a1f4f88adc6..0bc4e4dcf5fd6 100644 --- a/src/test/ui/lint/outer-forbid.stderr +++ b/src/test/ui/lint/outer-forbid.stderr @@ -4,7 +4,7 @@ error[E0453]: allow(unused_variables) overruled by outer forbid(unused) 17 | #![forbid(unused, non_snake_case)] | ------ `forbid` level set here 18 | -19 | #[allow(unused_variables)] +19 | #[allow(unused_variables)] //~ ERROR overruled | ^^^^^^^^^^^^^^^^ overruled by previous forbid error[E0453]: allow(unused) overruled by outer forbid(unused) @@ -13,7 +13,7 @@ error[E0453]: allow(unused) overruled by outer forbid(unused) 17 | #![forbid(unused, non_snake_case)] | ------ `forbid` level set here ... -22 | #[allow(unused)] +22 | #[allow(unused)] //~ ERROR overruled | ^^^^^^ overruled by previous forbid error[E0453]: allow(bad_style) overruled by outer forbid(non_snake_case) @@ -22,7 +22,7 @@ error[E0453]: allow(bad_style) overruled by outer forbid(non_snake_case) 17 | #![forbid(unused, non_snake_case)] | -------------- `forbid` level set here ... -25 | #[allow(bad_style)] +25 | #[allow(bad_style)] //~ ERROR overruled | ^^^^^^^^^ overruled by previous forbid error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/suggestions.rs b/src/test/ui/lint/suggestions.rs new file mode 100644 index 0000000000000..3789b6dfc8b3a --- /dev/null +++ b/src/test/ui/lint/suggestions.rs @@ -0,0 +1,46 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issue #43896 +#![feature(no_debug)] + +#[no_mangle] static SHENZHOU: usize = 1; // should suggest `pub` +//~^ WARN static is marked #[no_mangle] +#[no_mangle] const DISCOVERY: usize = 1; // should suggest `pub static` rather than `const` +//~^ ERROR const items should never be #[no_mangle] + +#[no_mangle] // should suggest removal (generics can't be no-mangle) +pub fn defiant(_t: T) {} +//~^ WARN functions generic over types must be mangled + +#[no_mangle] +fn rio_grande() {} // should suggest `pub` +//~^ WARN function is marked + +struct Equinox { + warp_factor: f32, +} + +#[no_debug] // should suggest removal of deprecated attribute +//~^ WARN deprecated +fn main() { + while true { // should suggest `loop` + //~^ WARN denote infinite loops + let mut a = (1); // should suggest no `mut`, no parens + //~^ WARN does not need to be mutable + //~| WARN unnecessary parentheses + let d = Equinox { warp_factor: 9.975 }; + match d { + Equinox { warp_factor: warp_factor } => {} // should suggest shorthand + //~^ WARN this pattern is redundant + } + println!("{}", a); + } +} diff --git a/src/test/ui/lint/suggestions.stderr b/src/test/ui/lint/suggestions.stderr new file mode 100644 index 0000000000000..7b84cc1f4b490 --- /dev/null +++ b/src/test/ui/lint/suggestions.stderr @@ -0,0 +1,104 @@ +warning: unnecessary parentheses around assigned value + --> $DIR/suggestions.rs:36:21 + | +36 | let mut a = (1); // should suggest no `mut`, no parens + | ^^^ help: remove these parentheses + | +note: lint level defined here + --> $DIR/suggestions.rs:11:21 + | +11 | #![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issue #43896 + | ^^^^^^^^^^^^^ + +warning: use of deprecated attribute `no_debug`: the `#[no_debug]` attribute was an experimental feature that has been deprecated due to lack of demand. See https://github.com/rust-lang/rust/issues/29721 + --> $DIR/suggestions.rs:31:1 + | +31 | #[no_debug] // should suggest removal of deprecated attribute + | ^^^^^^^^^^^ help: remove this attribute + | + = note: #[warn(deprecated)] on by default + +warning: variable does not need to be mutable + --> $DIR/suggestions.rs:36:13 + | +36 | let mut a = (1); // should suggest no `mut`, no parens + | ---^^ + | | + | help: remove this `mut` + | +note: lint level defined here + --> $DIR/suggestions.rs:11:9 + | +11 | #![warn(unused_mut, unused_parens)] // UI tests pass `-A unused`—see Issue #43896 + | ^^^^^^^^^^ + +warning: static is marked #[no_mangle], but not exported + --> $DIR/suggestions.rs:14:14 + | +14 | #[no_mangle] static SHENZHOU: usize = 1; // should suggest `pub` + | -^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: try making it public: `pub ` + | + = note: #[warn(private_no_mangle_statics)] on by default + +error: const items should never be #[no_mangle] + --> $DIR/suggestions.rs:16:14 + | +16 | #[no_mangle] const DISCOVERY: usize = 1; // should suggest `pub static` rather than `const` + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: try a static value: `pub static` + | + = note: #[deny(no_mangle_const_items)] on by default + +warning: functions generic over types must be mangled + --> $DIR/suggestions.rs:20:1 + | +19 | #[no_mangle] // should suggest removal (generics can't be no-mangle) + | ------------ help: remove this attribute +20 | pub fn defiant(_t: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(no_mangle_generic_items)] on by default + +warning: function is marked #[no_mangle], but not exported + --> $DIR/suggestions.rs:24:1 + | +24 | fn rio_grande() {} // should suggest `pub` + | -^^^^^^^^^^^^^^^^^ + | | + | help: try making it public: `pub ` + | + = note: #[warn(private_no_mangle_fns)] on by default + +warning: denote infinite loops with `loop { ... }` + --> $DIR/suggestions.rs:34:5 + | +34 | while true { // should suggest `loop` + | ^--------- + | | + | _____help: use `loop` + | | +35 | | //~^ WARN denote infinite loops +36 | | let mut a = (1); // should suggest no `mut`, no parens +37 | | //~^ WARN does not need to be mutable +... | +44 | | println!("{}", a); +45 | | } + | |_____^ + | + = note: #[warn(while_true)] on by default + +warning: the `warp_factor:` in this pattern is redundant + --> $DIR/suggestions.rs:41:23 + | +41 | Equinox { warp_factor: warp_factor } => {} // should suggest shorthand + | ------------^^^^^^^^^^^^ + | | + | help: remove this + | + = note: #[warn(non_shorthand_field_patterns)] on by default + +error: aborting due to previous error + diff --git a/src/test/ui/lint/unreachable_pub-pub_crate.rs b/src/test/ui/lint/unreachable_pub-pub_crate.rs new file mode 100644 index 0000000000000..b794f6c9517e8 --- /dev/null +++ b/src/test/ui/lint/unreachable_pub-pub_crate.rs @@ -0,0 +1,74 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is just like unreachable_pub.rs, but without the +// `crate_visibility_modifier` feature (so that we can test the suggestions to +// use `pub(crate)` that are given when that feature is off, as opposed to the +// suggestions to use `crate` given when it is on). When that feature becomes +// stable, this test can be deleted. + +#![feature(macro_vis_matcher)] + +#![allow(unused)] +#![warn(unreachable_pub)] + +mod private_mod { + // non-leaked `pub` items in private module should be linted + pub use std::fmt; + + pub struct Hydrogen { + // `pub` struct fields, too + pub neutrons: usize, + // (... but not more-restricted fields) + pub(crate) electrons: usize + } + impl Hydrogen { + // impls, too + pub fn count_neutrons(&self) -> usize { self.neutrons } + pub(crate) fn count_electrons(&self) -> usize { self.electrons } + } + + pub enum Helium {} + pub union Lithium { c1: usize, c2: u8 } + pub fn beryllium() {} + pub trait Boron {} + pub const CARBON: usize = 1; + pub static NITROGEN: usize = 2; + pub type Oxygen = bool; + + macro_rules! define_empty_struct_with_visibility { + ($visibility: vis, $name: ident) => { $visibility struct $name {} } + } + define_empty_struct_with_visibility!(pub, Fluorine); + + extern { + pub fn catalyze() -> bool; + } + + // items leaked through signatures (see `get_neon` below) are OK + pub struct Neon {} + + // crate-visible items are OK + pub(crate) struct Sodium {} +} + +pub mod public_mod { + // module is public: these are OK, too + pub struct Magnesium {} + pub(crate) struct Aluminum {} +} + +pub fn get_neon() -> private_mod::Neon { + private_mod::Neon {} +} + +fn main() { + let _ = get_neon(); +} diff --git a/src/test/ui/lint/unreachable_pub-pub_crate.stderr b/src/test/ui/lint/unreachable_pub-pub_crate.stderr new file mode 100644 index 0000000000000..84cbf87c1a1c4 --- /dev/null +++ b/src/test/ui/lint/unreachable_pub-pub_crate.stderr @@ -0,0 +1,134 @@ +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:24:5 + | +24 | pub use std::fmt; + | ---^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | +note: lint level defined here + --> $DIR/unreachable_pub-pub_crate.rs:20:9 + | +20 | #![warn(unreachable_pub)] + | ^^^^^^^^^^^^^^^ + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:26:5 + | +26 | pub struct Hydrogen { + | ---^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` field + --> $DIR/unreachable_pub-pub_crate.rs:28:9 + | +28 | pub neutrons: usize, + | ---^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:34:9 + | +34 | pub fn count_neutrons(&self) -> usize { self.neutrons } + | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:38:5 + | +38 | pub enum Helium {} + | ---^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:39:5 + | +39 | pub union Lithium { c1: usize, c2: u8 } + | ---^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:40:5 + | +40 | pub fn beryllium() {} + | ---^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:41:5 + | +41 | pub trait Boron {} + | ---^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:42:5 + | +42 | pub const CARBON: usize = 1; + | ---^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:43:5 + | +43 | pub static NITROGEN: usize = 2; + | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:44:5 + | +44 | pub type Oxygen = bool; + | ---^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:47:47 + | +47 | ($visibility: vis, $name: ident) => { $visibility struct $name {} } + | -----------^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` +48 | } +49 | define_empty_struct_with_visibility!(pub, Fluorine); + | ---------------------------------------------------- in this macro invocation + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub-pub_crate.rs:52:9 + | +52 | pub fn catalyze() -> bool; + | ---^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates + diff --git a/src/test/ui/lint/unreachable_pub.rs b/src/test/ui/lint/unreachable_pub.rs new file mode 100644 index 0000000000000..5812061dfdb1e --- /dev/null +++ b/src/test/ui/lint/unreachable_pub.rs @@ -0,0 +1,69 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(crate_visibility_modifier)] +#![feature(macro_vis_matcher)] + +#![allow(unused)] +#![warn(unreachable_pub)] + +mod private_mod { + // non-leaked `pub` items in private module should be linted + pub use std::fmt; + + pub struct Hydrogen { + // `pub` struct fields, too + pub neutrons: usize, + // (... but not more-restricted fields) + crate electrons: usize + } + impl Hydrogen { + // impls, too + pub fn count_neutrons(&self) -> usize { self.neutrons } + crate fn count_electrons(&self) -> usize { self.electrons } + } + + pub enum Helium {} + pub union Lithium { c1: usize, c2: u8 } + pub fn beryllium() {} + pub trait Boron {} + pub const CARBON: usize = 1; + pub static NITROGEN: usize = 2; + pub type Oxygen = bool; + + macro_rules! define_empty_struct_with_visibility { + ($visibility: vis, $name: ident) => { $visibility struct $name {} } + } + define_empty_struct_with_visibility!(pub, Fluorine); + + extern { + pub fn catalyze() -> bool; + } + + // items leaked through signatures (see `get_neon` below) are OK + pub struct Neon {} + + // crate-visible items are OK + crate struct Sodium {} +} + +pub mod public_mod { + // module is public: these are OK, too + pub struct Magnesium {} + crate struct Aluminum {} +} + +pub fn get_neon() -> private_mod::Neon { + private_mod::Neon {} +} + +fn main() { + let _ = get_neon(); +} diff --git a/src/test/ui/lint/unreachable_pub.stderr b/src/test/ui/lint/unreachable_pub.stderr new file mode 100644 index 0000000000000..bdd016ff2df20 --- /dev/null +++ b/src/test/ui/lint/unreachable_pub.stderr @@ -0,0 +1,134 @@ +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:19:5 + | +19 | pub use std::fmt; + | ---^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | +note: lint level defined here + --> $DIR/unreachable_pub.rs:15:9 + | +15 | #![warn(unreachable_pub)] + | ^^^^^^^^^^^^^^^ + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:21:5 + | +21 | pub struct Hydrogen { + | ---^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` field + --> $DIR/unreachable_pub.rs:23:9 + | +23 | pub neutrons: usize, + | ---^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:29:9 + | +29 | pub fn count_neutrons(&self) -> usize { self.neutrons } + | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:33:5 + | +33 | pub enum Helium {} + | ---^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:34:5 + | +34 | pub union Lithium { c1: usize, c2: u8 } + | ---^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:35:5 + | +35 | pub fn beryllium() {} + | ---^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:36:5 + | +36 | pub trait Boron {} + | ---^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:37:5 + | +37 | pub const CARBON: usize = 1; + | ---^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:38:5 + | +38 | pub static NITROGEN: usize = 2; + | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:39:5 + | +39 | pub type Oxygen = bool; + | ---^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:42:47 + | +42 | ($visibility: vis, $name: ident) => { $visibility struct $name {} } + | -----------^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` +43 | } +44 | define_empty_struct_with_visibility!(pub, Fluorine); + | ---------------------------------------------------- in this macro invocation + | + = help: or consider exporting it for use by other crates + +warning: unreachable `pub` item + --> $DIR/unreachable_pub.rs:47:9 + | +47 | pub fn catalyze() -> bool; + | ---^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider restricting its visibility: `crate` + | + = help: or consider exporting it for use by other crates + diff --git a/src/test/ui/lint/unused_parens_json_suggestion.rs b/src/test/ui/lint/unused_parens_json_suggestion.rs new file mode 100644 index 0000000000000..ad501e668095a --- /dev/null +++ b/src/test/ui/lint/unused_parens_json_suggestion.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --error-format pretty-json -Zunstable-options + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![warn(unused_parens)] + +fn main() { + // We want to suggest the properly-balanced expression `1 / (2 + 3)`, not + // the malformed `1 / (2 + 3` + let _a = (1 / (2 + 3)); +} diff --git a/src/test/ui/lint/unused_parens_json_suggestion.stderr b/src/test/ui/lint/unused_parens_json_suggestion.stderr new file mode 100644 index 0000000000000..fe113eda3dd22 --- /dev/null +++ b/src/test/ui/lint/unused_parens_json_suggestion.stderr @@ -0,0 +1,103 @@ +{ + "message": "unnecessary parentheses around assigned value", + "code": { + "code": "unused_parens", + "explanation": null + }, + "level": "warning", + "spans": [ + { + "file_name": "$DIR/unused_parens_json_suggestion.rs", + "byte_start": 1027, + "byte_end": 1040, + "line_start": 24, + "line_end": 24, + "column_start": 14, + "column_end": 27, + "is_primary": true, + "text": [ + { + "text": " let _a = (1 / (2 + 3));", + "highlight_start": 14, + "highlight_end": 27 + } + ], + "label": null, + "suggested_replacement": null, + "expansion": null + } + ], + "children": [ + { + "message": "lint level defined here", + "code": null, + "level": "note", + "spans": [ + { + "file_name": "$DIR/unused_parens_json_suggestion.rs", + "byte_start": 873, + "byte_end": 886, + "line_start": 19, + "line_end": 19, + "column_start": 9, + "column_end": 22, + "is_primary": true, + "text": [ + { + "text": "#![warn(unused_parens)]", + "highlight_start": 9, + "highlight_end": 22 + } + ], + "label": null, + "suggested_replacement": null, + "expansion": null + } + ], + "children": [], + "rendered": null + }, + { + "message": "remove these parentheses", + "code": null, + "level": "help", + "spans": [ + { + "file_name": "$DIR/unused_parens_json_suggestion.rs", + "byte_start": 1027, + "byte_end": 1040, + "line_start": 24, + "line_end": 24, + "column_start": 14, + "column_end": 27, + "is_primary": true, + "text": [ + { + "text": " let _a = (1 / (2 + 3));", + "highlight_start": 14, + "highlight_end": 27 + } + ], + "label": null, + "suggested_replacement": "1 / (2 + 3)", + "expansion": null + } + ], + "children": [], + "rendered": null + } + ], + "rendered": "warning: unnecessary parentheses around assigned value + --> $DIR/unused_parens_json_suggestion.rs:24:14 + | +24 | let _a = (1 / (2 + 3)); + | ^^^^^^^^^^^^^ help: remove these parentheses + | +note: lint level defined here + --> $DIR/unused_parens_json_suggestion.rs:19:9 + | +19 | #![warn(unused_parens)] + | ^^^^^^^^^^^^^ + +" +} diff --git a/src/test/ui/lint/use_suggestion_json.rs b/src/test/ui/lint/use_suggestion_json.rs new file mode 100644 index 0000000000000..27232c4fec4ad --- /dev/null +++ b/src/test/ui/lint/use_suggestion_json.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --error-format pretty-json -Zunstable-options + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +fn main() { + let x: Iter; +} diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr new file mode 100644 index 0000000000000..846d7df445d58 --- /dev/null +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -0,0 +1,398 @@ +{ + "message": "cannot find type `Iter` in this scope", + "code": { + "code": "E0412", + "explanation": " +The type name used is not in scope. + +Erroneous code examples: + +```compile_fail,E0412 +impl Something {} // error: type name `Something` is not in scope + +// or: + +trait Foo { + fn bar(N); // error: type name `N` is not in scope +} + +// or: + +fn foo(x: T) {} // type name `T` is not in scope +``` + +To fix this error, please verify you didn't misspell the type name, you did +declare it or imported it into the scope. Examples: + +``` +struct Something; + +impl Something {} // ok! + +// or: + +trait Foo { + type N; + + fn bar(_: Self::N); // ok! +} + +// or: + +fn foo(x: T) {} // ok! +``` + +Another case that causes this error is when a type is imported into a parent +module. To fix this, you can follow the suggestion and use File directly or +`use super::File;` which will import the types from the parent namespace. An +example that causes this error is below: + +```compile_fail,E0412 +use std::fs::File; + +mod foo { + fn some_function(f: File) {} +} +``` + +``` +use std::fs::File; + +mod foo { + // either + use super::File; + // or + // use std::fs::File; + fn foo(f: File) {} +} +# fn main() {} // don't insert it for us; that'll break imports +``` +" + }, + "level": "error", + "spans": [ + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 888, + "byte_end": 892, + "line_start": 20, + "line_end": 20, + "column_start": 12, + "column_end": 16, + "is_primary": true, + "text": [ + { + "text": " let x: Iter;", + "highlight_start": 12, + "highlight_end": 16 + } + ], + "label": "not found in this scope", + "suggested_replacement": null, + "expansion": null + } + ], + "children": [ + { + "message": "possible candidates are found in other modules, you can import them into scope", + "code": null, + "level": "help", + "spans": [ + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::binary_heap::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::btree_map::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::btree_set::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::hash_map::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::hash_set::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::linked_list::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::collections::vec_deque::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::option::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::path::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::result::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::slice::Iter; + +", + "expansion": null + }, + { + "file_name": "$DIR/use_suggestion_json.rs", + "byte_start": 865, + "byte_end": 865, + "line_start": 19, + "line_end": 19, + "column_start": 1, + "column_end": 1, + "is_primary": true, + "text": [ + { + "text": "fn main() {", + "highlight_start": 1, + "highlight_end": 1 + } + ], + "label": null, + "suggested_replacement": "use std::sync::mpsc::Iter; + +", + "expansion": null + } + ], + "children": [], + "rendered": null + } + ], + "rendered": "error[E0412]: cannot find type `Iter` in this scope + --> $DIR/use_suggestion_json.rs:20:12 + | +20 | let x: Iter; + | ^^^^ not found in this scope +help: possible candidates are found in other modules, you can import them into scope + | +19 | use std::collections::binary_heap::Iter; + | +19 | use std::collections::btree_map::Iter; + | +19 | use std::collections::btree_set::Iter; + | +19 | use std::collections::hash_map::Iter; + | +and 8 other candidates + +" +} +{ + "message": "aborting due to previous error", + "code": null, + "level": "error", + "spans": [], + "children": [], + "rendered": "error: aborting due to previous error + +" +} diff --git a/src/test/ui/loop-break-value-no-repeat.rs b/src/test/ui/loop-break-value-no-repeat.rs index b52d540fd7511..f24840eca5441 100644 --- a/src/test/ui/loop-break-value-no-repeat.rs +++ b/src/test/ui/loop-break-value-no-repeat.rs @@ -19,6 +19,6 @@ use std::ptr; fn main() { for _ in &[1,2,3] { - break 22 + break 22 //~ ERROR `break` with value from a `for` loop } } diff --git a/src/test/ui/loop-break-value-no-repeat.stderr b/src/test/ui/loop-break-value-no-repeat.stderr index c154ea6f8c2d6..296b3b191e319 100644 --- a/src/test/ui/loop-break-value-no-repeat.stderr +++ b/src/test/ui/loop-break-value-no-repeat.stderr @@ -1,7 +1,7 @@ error[E0571]: `break` with value from a `for` loop --> $DIR/loop-break-value-no-repeat.rs:22:9 | -22 | break 22 +22 | break 22 //~ ERROR `break` with value from a `for` loop | ^^^^^^^^ can only break with a value inside `loop` error: aborting due to previous error diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.rs b/src/test/ui/lub-glb/old-lub-glb-hr.rs new file mode 100644 index 0000000000000..7526b2f946c12 --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we give a note when the old LUB/GLB algorithm would have +// succeeded but the new code (which is stricter) gives an error. + +fn foo( + x: fn(&u8, &u8), + y: for<'a> fn(&'a u8, &'a u8), +) { + let z = match 22 { //~ ERROR incompatible types + 0 => x, + _ => y, + }; +} + +fn bar( + x: fn(&u8, &u8), + y: for<'a> fn(&'a u8, &'a u8), +) { + let z = match 22 { + // No error with an explicit cast: + 0 => x as for<'a> fn(&'a u8, &'a u8), + _ => y, + }; +} + +fn main() { +} diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.stderr b/src/test/ui/lub-glb/old-lub-glb-hr.stderr new file mode 100644 index 0000000000000..72d9787b93aed --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr.stderr @@ -0,0 +1,22 @@ +error[E0308]: match arms have incompatible types + --> $DIR/old-lub-glb-hr.rs:18:13 + | +18 | let z = match 22 { //~ ERROR incompatible types + | _____________^ +19 | | 0 => x, +20 | | _ => y, +21 | | }; + | |_____^ expected bound lifetime parameter, found concrete lifetime + | + = note: expected type `for<'r, 's> fn(&'r u8, &'s u8)` + found type `for<'a> fn(&'a u8, &'a u8)` + = note: this was previously accepted by the compiler but has been phased out + = note: for more information, see https://github.com/rust-lang/rust/issues/45852 +note: match arm with an incompatible type + --> $DIR/old-lub-glb-hr.rs:20:14 + | +20 | _ => y, + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/lub-glb/old-lub-glb-object.rs b/src/test/ui/lub-glb/old-lub-glb-object.rs new file mode 100644 index 0000000000000..63dcfa3fc1e91 --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-object.rs @@ -0,0 +1,38 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we give a note when the old LUB/GLB algorithm would have +// succeeded but the new code (which is stricter) gives an error. + +trait Foo { } + +fn foo( + x: &for<'a, 'b> Foo<&'a u8, &'b u8>, + y: &for<'a> Foo<&'a u8, &'a u8>, +) { + let z = match 22 { //~ ERROR incompatible types + 0 => x, + _ => y, + }; +} + +fn bar( + x: &for<'a, 'b> Foo<&'a u8, &'b u8>, + y: &for<'a> Foo<&'a u8, &'a u8>, +) { + // Accepted with explicit case: + let z = match 22 { + 0 => x as &for<'a> Foo<&'a u8, &'a u8>, + _ => y, + }; +} + +fn main() { +} diff --git a/src/test/ui/lub-glb/old-lub-glb-object.stderr b/src/test/ui/lub-glb/old-lub-glb-object.stderr new file mode 100644 index 0000000000000..852f74b4e7542 --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-object.stderr @@ -0,0 +1,22 @@ +error[E0308]: match arms have incompatible types + --> $DIR/old-lub-glb-object.rs:20:13 + | +20 | let z = match 22 { //~ ERROR incompatible types + | _____________^ +21 | | 0 => x, +22 | | _ => y, +23 | | }; + | |_____^ expected bound lifetime parameter 'a, found concrete lifetime + | + = note: expected type `&for<'a, 'b> Foo<&'a u8, &'b u8>` + found type `&for<'a> Foo<&'a u8, &'a u8>` + = note: this was previously accepted by the compiler but has been phased out + = note: for more information, see https://github.com/rust-lang/rust/issues/45852 +note: match arm with an incompatible type + --> $DIR/old-lub-glb-object.rs:22:14 + | +22 | _ => y, + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/macro_backtrace/auxiliary/ping.rs b/src/test/ui/macro_backtrace/auxiliary/ping.rs new file mode 100644 index 0000000000000..eeed0d78158c8 --- /dev/null +++ b/src/test/ui/macro_backtrace/auxiliary/ping.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the macro backtrace facility works (supporting file) + +// a non-local macro +#[macro_export] +macro_rules! ping { + () => { + pong!(); + } +} + diff --git a/src/test/ui/macro_backtrace/main.rs b/src/test/ui/macro_backtrace/main.rs new file mode 100644 index 0000000000000..ec9218e3ec001 --- /dev/null +++ b/src/test/ui/macro_backtrace/main.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the macro backtrace facility works +// aux-build:ping.rs +// compile-flags: -Z external-macro-backtrace + +#[macro_use] extern crate ping; + +// a local macro +macro_rules! pong { + () => { syntax error }; //~ ERROR expected one of + //~^ ERROR expected one of +} + +fn main() { + pong!(); + ping!(); +} diff --git a/src/test/ui/macro_backtrace/main.stderr b/src/test/ui/macro_backtrace/main.stderr new file mode 100644 index 0000000000000..4b10e5e19a3f1 --- /dev/null +++ b/src/test/ui/macro_backtrace/main.stderr @@ -0,0 +1,17 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error` + --> $DIR/main.rs:19:20 + | +19 | () => { syntax error }; //~ ERROR expected one of + | ^^^^^ expected one of 8 possible tokens here +$DIR/main.rs:24:5: 24:13 note: in this expansion of pong! (defined in $DIR/main.rs) + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error` + --> $DIR/main.rs:19:20 + | +19 | () => { syntax error }; //~ ERROR expected one of + | ^^^^^ expected one of 8 possible tokens here +$DIR/main.rs:25:5: 25:13 note: in this expansion of ping! (defined in ) +:1:11: 1:24 note: in this expansion of pong! (defined in $DIR/main.rs) + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/macros/assert_eq_trailing_comma.stderr b/src/test/ui/macros/assert_eq_trailing_comma.stderr deleted file mode 100644 index 1b46e94584e6b..0000000000000 --- a/src/test/ui/macros/assert_eq_trailing_comma.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: unexpected end of macro invocation - --> $DIR/assert_eq_trailing_comma.rs:12:20 - | -12 | assert_eq!(1, 1,); - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/macros/assert_ne_trailing_comma.stderr b/src/test/ui/macros/assert_ne_trailing_comma.stderr deleted file mode 100644 index 33d2cb0ed8242..0000000000000 --- a/src/test/ui/macros/assert_ne_trailing_comma.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: unexpected end of macro invocation - --> $DIR/assert_ne_trailing_comma.rs:12:20 - | -12 | assert_ne!(1, 2,); - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/macros/bad_hello.rs b/src/test/ui/macros/bad_hello.rs index a18771deacee7..174dcc9b6cd3f 100644 --- a/src/test/ui/macros/bad_hello.rs +++ b/src/test/ui/macros/bad_hello.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - println!(3 + 4); + println!(3 + 4); //~ ERROR expected a literal } diff --git a/src/test/ui/macros/bad_hello.stderr b/src/test/ui/macros/bad_hello.stderr index bffb33f468fc8..825aa64e40f52 100644 --- a/src/test/ui/macros/bad_hello.stderr +++ b/src/test/ui/macros/bad_hello.stderr @@ -1,7 +1,7 @@ error: expected a literal --> $DIR/bad_hello.rs:12:14 | -12 | println!(3 + 4); +12 | println!(3 + 4); //~ ERROR expected a literal | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/macros/format-foreign.rs b/src/test/ui/macros/format-foreign.rs index cca45ca9ecdd9..91ca8f5ff7602 100644 --- a/src/test/ui/macros/format-foreign.rs +++ b/src/test/ui/macros/format-foreign.rs @@ -10,11 +10,11 @@ fn main() { println!("%.*3$s %s!\n", "Hello,", "World", 4); - println!("%1$*2$.*3$f", 123.456); + println!("%1$*2$.*3$f", 123.456); //~ ERROR never used // This should *not* produce hints, on the basis that there's equally as // many "correct" format specifiers. It's *probably* just an actual typo. - println!("{} %f", "one", 2.0); + println!("{} %f", "one", 2.0); //~ ERROR never used - println!("Hi there, $NAME.", NAME="Tim"); + println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used } diff --git a/src/test/ui/macros/format-foreign.stderr b/src/test/ui/macros/format-foreign.stderr index 00469b5f7998c..d0229957b682e 100644 --- a/src/test/ui/macros/format-foreign.stderr +++ b/src/test/ui/macros/format-foreign.stderr @@ -11,12 +11,12 @@ error: multiple unused formatting arguments = help: `%.*3$s` should be written as `{:.2$}` = help: `%s` should be written as `{}` = note: printf formatting not supported; see the documentation for `std::fmt` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: argument never used --> $DIR/format-foreign.rs:13:29 | -13 | println!("%1$*2$.*3$f", 123.456); +13 | println!("%1$*2$.*3$f", 123.456); //~ ERROR never used | ^^^^^^^ | = help: `%1$*2$.*3$f` should be written as `{0:1$.2$}` @@ -25,13 +25,13 @@ error: argument never used error: argument never used --> $DIR/format-foreign.rs:17:30 | -17 | println!("{} %f", "one", 2.0); +17 | println!("{} %f", "one", 2.0); //~ ERROR never used | ^^^ error: named argument never used --> $DIR/format-foreign.rs:19:39 | -19 | println!("Hi there, $NAME.", NAME="Tim"); +19 | println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used | ^^^^^ | = help: `$NAME` should be written as `{NAME}` diff --git a/src/test/ui/macros/format-unused-lables.rs b/src/test/ui/macros/format-unused-lables.rs index f1e349ea9f431..7a32d932ba386 100644 --- a/src/test/ui/macros/format-unused-lables.rs +++ b/src/test/ui/macros/format-unused-lables.rs @@ -17,7 +17,7 @@ fn main() { 789 ); - println!("Some stuff", UNUSED="args"); + println!("Some stuff", UNUSED="args"); //~ ERROR named argument never used println!("Some more $STUFF", "woo!", diff --git a/src/test/ui/macros/format-unused-lables.stderr b/src/test/ui/macros/format-unused-lables.stderr index bd6d38ccb0a44..9efdca12dea03 100644 --- a/src/test/ui/macros/format-unused-lables.stderr +++ b/src/test/ui/macros/format-unused-lables.stderr @@ -8,7 +8,7 @@ error: multiple unused formatting arguments | | unused | unused | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:14:5 @@ -23,12 +23,12 @@ error: multiple unused formatting arguments 18 | | ); | |______^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: named argument never used --> $DIR/format-unused-lables.rs:20:35 | -20 | println!("Some stuff", UNUSED="args"); +20 | println!("Some stuff", UNUSED="args"); //~ ERROR named argument never used | ^^^^^^ error: multiple unused formatting arguments @@ -47,7 +47,7 @@ error: multiple unused formatting arguments | = help: `$STUFF` should be written as `{STUFF}` = note: shell formatting not supported; see the documentation for `std::fmt` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/src/test/ui/macros/macro-backtrace-invalid-internals.rs b/src/test/ui/macros/macro-backtrace-invalid-internals.rs index 546e06b6c79f3..037f0d839e2b6 100644 --- a/src/test/ui/macros/macro-backtrace-invalid-internals.rs +++ b/src/test/ui/macros/macro-backtrace-invalid-internals.rs @@ -12,37 +12,37 @@ macro_rules! fake_method_stmt { () => { - 1.fake() + 1.fake() //~ ERROR no method } } macro_rules! fake_field_stmt { () => { - 1.fake + 1.fake //~ ERROR doesn't have fields } } macro_rules! fake_anon_field_stmt { () => { - (1).0 + (1).0 //~ ERROR no field } } macro_rules! fake_method_expr { () => { - 1.fake() + 1.fake() //~ ERROR no method } } macro_rules! fake_field_expr { () => { - 1.fake + 1.fake //~ ERROR doesn't have fields } } macro_rules! fake_anon_field_expr { () => { - (1).0 + (1).0 //~ ERROR no field } } diff --git a/src/test/ui/macros/macro-backtrace-invalid-internals.stderr b/src/test/ui/macros/macro-backtrace-invalid-internals.stderr index c80c0fce35806..42144f63c3710 100644 --- a/src/test/ui/macros/macro-backtrace-invalid-internals.stderr +++ b/src/test/ui/macros/macro-backtrace-invalid-internals.stderr @@ -1,7 +1,7 @@ error[E0599]: no method named `fake` found for type `{integer}` in the current scope --> $DIR/macro-backtrace-invalid-internals.rs:15:13 | -15 | 1.fake() +15 | 1.fake() //~ ERROR no method | ^^^^ ... 50 | fake_method_stmt!(); @@ -10,7 +10,7 @@ error[E0599]: no method named `fake` found for type `{integer}` in the current s error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> $DIR/macro-backtrace-invalid-internals.rs:21:13 | -21 | 1.fake +21 | 1.fake //~ ERROR doesn't have fields | ^^^^ ... 51 | fake_field_stmt!(); @@ -19,7 +19,7 @@ error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields error[E0609]: no field `0` on type `{integer}` --> $DIR/macro-backtrace-invalid-internals.rs:27:11 | -27 | (1).0 +27 | (1).0 //~ ERROR no field | ^^^^^ ... 52 | fake_anon_field_stmt!(); @@ -28,7 +28,7 @@ error[E0609]: no field `0` on type `{integer}` error[E0599]: no method named `fake` found for type `{integer}` in the current scope --> $DIR/macro-backtrace-invalid-internals.rs:33:13 | -33 | 1.fake() +33 | 1.fake() //~ ERROR no method | ^^^^ ... 54 | let _ = fake_method_expr!(); @@ -37,7 +37,7 @@ error[E0599]: no method named `fake` found for type `{integer}` in the current s error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> $DIR/macro-backtrace-invalid-internals.rs:39:13 | -39 | 1.fake +39 | 1.fake //~ ERROR doesn't have fields | ^^^^ ... 55 | let _ = fake_field_expr!(); @@ -46,7 +46,7 @@ error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields error[E0609]: no field `0` on type `{integer}` --> $DIR/macro-backtrace-invalid-internals.rs:45:11 | -45 | (1).0 +45 | (1).0 //~ ERROR no field | ^^^^^ ... 56 | let _ = fake_anon_field_expr!(); diff --git a/src/test/ui/macros/macro-backtrace-nested.rs b/src/test/ui/macros/macro-backtrace-nested.rs index d8bf6222c1c2c..d261633c60c86 100644 --- a/src/test/ui/macros/macro-backtrace-nested.rs +++ b/src/test/ui/macros/macro-backtrace-nested.rs @@ -12,7 +12,8 @@ // we replace the span of the expanded expression with that of the call site. macro_rules! nested_expr { - () => (fake) + () => (fake) //~ ERROR cannot find + //~^ ERROR cannot find } macro_rules! call_nested_expr { diff --git a/src/test/ui/macros/macro-backtrace-nested.stderr b/src/test/ui/macros/macro-backtrace-nested.stderr index 8b69d112d4d42..ee4a38312e289 100644 --- a/src/test/ui/macros/macro-backtrace-nested.stderr +++ b/src/test/ui/macros/macro-backtrace-nested.stderr @@ -1,19 +1,19 @@ error[E0425]: cannot find value `fake` in this scope --> $DIR/macro-backtrace-nested.rs:15:12 | -15 | () => (fake) +15 | () => (fake) //~ ERROR cannot find | ^^^^ not found in this scope ... -27 | 1 + call_nested_expr!(); +28 | 1 + call_nested_expr!(); | ------------------- in this macro invocation error[E0425]: cannot find value `fake` in this scope --> $DIR/macro-backtrace-nested.rs:15:12 | -15 | () => (fake) +15 | () => (fake) //~ ERROR cannot find | ^^^^ not found in this scope ... -28 | call_nested_expr_sum!(); +29 | call_nested_expr_sum!(); | ------------------------ in this macro invocation error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/macro-backtrace-println.rs b/src/test/ui/macros/macro-backtrace-println.rs index baf276919a5e8..6f035bc9d2355 100644 --- a/src/test/ui/macros/macro-backtrace-println.rs +++ b/src/test/ui/macros/macro-backtrace-println.rs @@ -21,7 +21,7 @@ macro_rules! myprint { } macro_rules! myprintln { - ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); + ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR no arguments were given } fn main() { diff --git a/src/test/ui/macros/macro-backtrace-println.stderr b/src/test/ui/macros/macro-backtrace-println.stderr index f21253bb67fb0..c587654d880a0 100644 --- a/src/test/ui/macros/macro-backtrace-println.stderr +++ b/src/test/ui/macros/macro-backtrace-println.stderr @@ -1,7 +1,7 @@ -error: invalid reference to argument `0` (no arguments given) +error: 1 positional argument in format string, but no arguments were given --> $DIR/macro-backtrace-println.rs:24:30 | -24 | ($fmt:expr) => (myprint!(concat!($fmt, "/n"))); +24 | ($fmt:expr) => (myprint!(concat!($fmt, "/n"))); //~ ERROR no arguments were given | ^^^^^^^^^^^^^^^^^^^ ... 28 | myprintln!("{}"); diff --git a/src/test/ui/macros/macro-name-typo.rs b/src/test/ui/macros/macro-name-typo.rs index ec8d27f9138f7..7fadbf2a90bba 100644 --- a/src/test/ui/macros/macro-name-typo.rs +++ b/src/test/ui/macros/macro-name-typo.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - printlx!("oh noes!"); + printlx!("oh noes!"); //~ ERROR cannot find } diff --git a/src/test/ui/macros/macro-name-typo.stderr b/src/test/ui/macros/macro-name-typo.stderr index 7c83250fe8ada..84851749c7074 100644 --- a/src/test/ui/macros/macro-name-typo.stderr +++ b/src/test/ui/macros/macro-name-typo.stderr @@ -1,7 +1,7 @@ error: cannot find macro `printlx!` in this scope --> $DIR/macro-name-typo.rs:12:5 | -12 | printlx!("oh noes!"); +12 | printlx!("oh noes!"); //~ ERROR cannot find | ^^^^^^^ help: you could try the macro: `println!` error: aborting due to previous error diff --git a/src/test/ui/macros/macro_path_as_generic_bound.rs b/src/test/ui/macros/macro_path_as_generic_bound.rs index 781ea30ed8bc3..85cf597489da9 100644 --- a/src/test/ui/macros/macro_path_as_generic_bound.rs +++ b/src/test/ui/macros/macro_path_as_generic_bound.rs @@ -14,6 +14,6 @@ macro_rules! foo(($t:path) => { impl Foo for T {} }); -foo!(m::m2::A); +foo!(m::m2::A); //~ ERROR failed to resolve fn main() {} diff --git a/src/test/ui/macros/macro_path_as_generic_bound.stderr b/src/test/ui/macros/macro_path_as_generic_bound.stderr index 5c3bb66d83a87..d59bcaa316e5d 100644 --- a/src/test/ui/macros/macro_path_as_generic_bound.stderr +++ b/src/test/ui/macros/macro_path_as_generic_bound.stderr @@ -1,7 +1,7 @@ error[E0433]: failed to resolve. Use of undeclared type or module `m` --> $DIR/macro_path_as_generic_bound.rs:17:6 | -17 | foo!(m::m2::A); +17 | foo!(m::m2::A); //~ ERROR failed to resolve | ^ Use of undeclared type or module `m` error: cannot continue compilation due to previous error diff --git a/src/test/ui/macros/macro_undefined.rs b/src/test/ui/macros/macro_undefined.rs index db93ba5e2c41d..c0acbc979ad1f 100644 --- a/src/test/ui/macros/macro_undefined.rs +++ b/src/test/ui/macros/macro_undefined.rs @@ -18,6 +18,6 @@ mod m { } fn main() { - k!(); - kl!(); + k!(); //~ ERROR cannot find + kl!(); //~ ERROR cannot find } diff --git a/src/test/ui/macros/macro_undefined.stderr b/src/test/ui/macros/macro_undefined.stderr index 5c33ae99734e8..6cfb05e786703 100644 --- a/src/test/ui/macros/macro_undefined.stderr +++ b/src/test/ui/macros/macro_undefined.stderr @@ -1,7 +1,7 @@ error: cannot find macro `kl!` in this scope --> $DIR/macro_undefined.rs:22:5 | -22 | kl!(); +22 | kl!(); //~ ERROR cannot find | ^^ | = help: have you added the `#[macro_use]` on the module/import? @@ -9,7 +9,7 @@ error: cannot find macro `kl!` in this scope error: cannot find macro `k!` in this scope --> $DIR/macro_undefined.rs:21:5 | -21 | k!(); +21 | k!(); //~ ERROR cannot find | ^ help: you could try the macro: `kl!` error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/trace_faulty_macros.rs b/src/test/ui/macros/trace_faulty_macros.rs index eb7292b0a652b..ced1a7f68fb53 100644 --- a/src/test/ui/macros/trace_faulty_macros.rs +++ b/src/test/ui/macros/trace_faulty_macros.rs @@ -14,7 +14,7 @@ macro_rules! my_faulty_macro { () => { - my_faulty_macro!(bcd); + my_faulty_macro!(bcd); //~ ERROR no rules }; } @@ -29,7 +29,7 @@ macro_rules! pat_macro { macro_rules! my_recursive_macro { () => { - my_recursive_macro!(); + my_recursive_macro!(); //~ ERROR recursion limit }; } diff --git a/src/test/ui/macros/trace_faulty_macros.stderr b/src/test/ui/macros/trace_faulty_macros.stderr index f4aeb8332f0b0..b0e4a56a3d1d6 100644 --- a/src/test/ui/macros/trace_faulty_macros.stderr +++ b/src/test/ui/macros/trace_faulty_macros.stderr @@ -1,7 +1,7 @@ error: no rules expected the token `bcd` --> $DIR/trace_faulty_macros.rs:17:26 | -17 | my_faulty_macro!(bcd); +17 | my_faulty_macro!(bcd); //~ ERROR no rules | ^^^ ... 43 | my_faulty_macro!(); @@ -20,7 +20,7 @@ note: trace_macro error: recursion limit reached while expanding the macro `my_recursive_macro` --> $DIR/trace_faulty_macros.rs:32:9 | -32 | my_recursive_macro!(); +32 | my_recursive_macro!(); //~ ERROR recursion limit | ^^^^^^^^^^^^^^^^^^^^^^ ... 44 | my_recursive_macro!(); diff --git a/src/test/compile-fail/method-call-err-msg.rs b/src/test/ui/method-call-err-msg.rs similarity index 97% rename from src/test/compile-fail/method-call-err-msg.rs rename to src/test/ui/method-call-err-msg.rs index 14fa74d1f32e5..37806e43a9d1c 100644 --- a/src/test/compile-fail/method-call-err-msg.rs +++ b/src/test/ui/method-call-err-msg.rs @@ -10,7 +10,7 @@ // Test that parameter cardinality or missing method error gets span exactly. -pub struct Foo; +pub struct Foo; //~ NOTE not found for this impl Foo { fn zero(self) -> Foo { self } //~^ NOTE defined here diff --git a/src/test/ui/method-call-err-msg.stderr b/src/test/ui/method-call-err-msg.stderr new file mode 100644 index 0000000000000..472879261ef01 --- /dev/null +++ b/src/test/ui/method-call-err-msg.stderr @@ -0,0 +1,47 @@ +error[E0061]: this function takes 0 parameters but 1 parameter was supplied + --> $DIR/method-call-err-msg.rs:25:12 + | +15 | fn zero(self) -> Foo { self } + | ----------------------------- defined here +... +25 | x.zero(0) //~ ERROR this function takes 0 parameters but 1 parameter was supplied + | ^ expected 0 parameters + +error[E0061]: this function takes 1 parameter but 0 parameters were supplied + --> $DIR/method-call-err-msg.rs:27:7 + | +17 | fn one(self, _: isize) -> Foo { self } + | -------------------------------------- defined here +... +27 | .one() //~ ERROR this function takes 1 parameter but 0 parameters were supplied + | ^^^ expected 1 parameter + +error[E0061]: this function takes 2 parameters but 1 parameter was supplied + --> $DIR/method-call-err-msg.rs:29:11 + | +19 | fn two(self, _: isize, _: isize) -> Foo { self } + | ------------------------------------------------ defined here +... +29 | .two(0); //~ ERROR this function takes 2 parameters but 1 parameter was supplied + | ^ expected 2 parameters + +error[E0599]: no method named `take` found for type `Foo` in the current scope + --> $DIR/method-call-err-msg.rs:34:7 + | +13 | pub struct Foo; //~ NOTE not found for this + | --------------- method `take` not found for this +... +34 | .take() //~ ERROR no method named `take` found for type `Foo` in the current scope + | ^^^^ + | + = note: the method `take` exists but the following trait bounds were not satisfied: + `&mut Foo : std::iter::Iterator` + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `take`, perhaps you need to implement one of them: + candidate #1: `std::collections::hash::Recover` + candidate #2: `std::io::Read` + candidate #3: `std::iter::Iterator` + candidate #4: `alloc::btree::Recover` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/mismatched_types/E0053.rs b/src/test/ui/mismatched_types/E0053.rs index 933462e553e3b..f82f3fb0fa4f7 100644 --- a/src/test/ui/mismatched_types/E0053.rs +++ b/src/test/ui/mismatched_types/E0053.rs @@ -19,11 +19,11 @@ impl Foo for Bar { fn foo(x: i16) { } //~^ ERROR method `foo` has an incompatible type for trait //~| NOTE expected u16 + //~| NOTE expected type `fn(u16)` fn bar(&mut self) { } //~^ ERROR method `bar` has an incompatible type for trait //~| NOTE types differ in mutability //~| NOTE expected type `fn(&Bar)` - //~| NOTE found type `fn(&mut Bar)` } fn main() { diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr index d9871b8970c5c..b80363e3d3e1a 100644 --- a/src/test/ui/mismatched_types/E0053.stderr +++ b/src/test/ui/mismatched_types/E0053.stderr @@ -11,12 +11,12 @@ error[E0053]: method `foo` has an incompatible type for trait found type `fn(i16)` error[E0053]: method `bar` has an incompatible type for trait - --> $DIR/E0053.rs:22:12 + --> $DIR/E0053.rs:23:12 | 13 | fn bar(&self); //~ NOTE type in trait | ----- type in trait ... -22 | fn bar(&mut self) { } +23 | fn bar(&mut self) { } | ^^^^^^^^^ types differ in mutability | = note: expected type `fn(&Bar)` diff --git a/src/test/ui/mismatched_types/E0409.rs b/src/test/ui/mismatched_types/E0409.rs index e89cc9ea5cbf2..17bbc3f24336f 100644 --- a/src/test/ui/mismatched_types/E0409.rs +++ b/src/test/ui/mismatched_types/E0409.rs @@ -18,7 +18,6 @@ fn main() { //~| ERROR E0308 //~| NOTE expected &{integer}, found integral variable //~| NOTE expected type `&{integer}` - //~| NOTE found type `{integer}` _ => () } } diff --git a/src/test/ui/mismatched_types/E0631.rs b/src/test/ui/mismatched_types/E0631.rs index e28f15ab0b62e..7e5490b37c43e 100644 --- a/src/test/ui/mismatched_types/E0631.rs +++ b/src/test/ui/mismatched_types/E0631.rs @@ -14,8 +14,8 @@ fn foo(_: F) {} fn bar>(_: F) {} fn main() { fn f(_: u64) {} - foo(|_: isize| {}); - bar(|_: isize| {}); - foo(f); - bar(f); + foo(|_: isize| {}); //~ ERROR type mismatch + bar(|_: isize| {}); //~ ERROR type mismatch + foo(f); //~ ERROR type mismatch + bar(f); //~ ERROR type mismatch } diff --git a/src/test/ui/mismatched_types/E0631.stderr b/src/test/ui/mismatched_types/E0631.stderr index 235e7a100633d..33a68a29ddca3 100644 --- a/src/test/ui/mismatched_types/E0631.stderr +++ b/src/test/ui/mismatched_types/E0631.stderr @@ -1,7 +1,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/E0631.rs:17:5 | -17 | foo(|_: isize| {}); +17 | foo(|_: isize| {}); //~ ERROR type mismatch | ^^^ ------------- found signature of `fn(isize) -> _` | | | expected signature of `fn(usize) -> _` @@ -11,7 +11,7 @@ error[E0631]: type mismatch in closure arguments error[E0631]: type mismatch in closure arguments --> $DIR/E0631.rs:18:5 | -18 | bar(|_: isize| {}); +18 | bar(|_: isize| {}); //~ ERROR type mismatch | ^^^ ------------- found signature of `fn(isize) -> _` | | | expected signature of `fn(usize) -> _` @@ -21,7 +21,7 @@ error[E0631]: type mismatch in closure arguments error[E0631]: type mismatch in function arguments --> $DIR/E0631.rs:19:5 | -19 | foo(f); +19 | foo(f); //~ ERROR type mismatch | ^^^ | | | expected signature of `fn(usize) -> _` @@ -32,7 +32,7 @@ error[E0631]: type mismatch in function arguments error[E0631]: type mismatch in function arguments --> $DIR/E0631.rs:20:5 | -20 | bar(f); +20 | bar(f); //~ ERROR type mismatch | ^^^ | | | expected signature of `fn(usize) -> _` diff --git a/src/test/ui/mismatched_types/abridged.rs b/src/test/ui/mismatched_types/abridged.rs index 03f889224bedc..f496df58f734e 100644 --- a/src/test/ui/mismatched_types/abridged.rs +++ b/src/test/ui/mismatched_types/abridged.rs @@ -23,19 +23,19 @@ struct X { } fn a() -> Foo { - Some(Foo { bar: 1 }) + Some(Foo { bar: 1 }) //~ ERROR mismatched types } fn a2() -> Foo { - Ok(Foo { bar: 1}) + Ok(Foo { bar: 1}) //~ ERROR mismatched types } fn b() -> Option { - Foo { bar: 1 } + Foo { bar: 1 } //~ ERROR mismatched types } fn c() -> Result { - Foo { bar: 1 } + Foo { bar: 1 } //~ ERROR mismatched types } fn d() -> X, String> { @@ -46,7 +46,7 @@ fn d() -> X, String> { }, y: 3, }; - x + x //~ ERROR mismatched types } fn e() -> X, String> { @@ -57,7 +57,7 @@ fn e() -> X, String> { }, y: "".to_string(), }; - x + x //~ ERROR mismatched types } fn main() {} diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index 8c63d7d6f91c5..2e1e5afad32c1 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -3,7 +3,7 @@ error[E0308]: mismatched types | 25 | fn a() -> Foo { | --- expected `Foo` because of return type -26 | Some(Foo { bar: 1 }) +26 | Some(Foo { bar: 1 }) //~ ERROR mismatched types | ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::option::Option` | = note: expected type `Foo` @@ -14,7 +14,7 @@ error[E0308]: mismatched types | 29 | fn a2() -> Foo { | --- expected `Foo` because of return type -30 | Ok(Foo { bar: 1}) +30 | Ok(Foo { bar: 1}) //~ ERROR mismatched types | ^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::result::Result` | = note: expected type `Foo` @@ -25,7 +25,7 @@ error[E0308]: mismatched types | 33 | fn b() -> Option { | ----------- expected `std::option::Option` because of return type -34 | Foo { bar: 1 } +34 | Foo { bar: 1 } //~ ERROR mismatched types | ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo` | = note: expected type `std::option::Option` @@ -36,7 +36,7 @@ error[E0308]: mismatched types | 37 | fn c() -> Result { | ---------------- expected `std::result::Result` because of return type -38 | Foo { bar: 1 } +38 | Foo { bar: 1 } //~ ERROR mismatched types | ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo` | = note: expected type `std::result::Result` @@ -48,7 +48,7 @@ error[E0308]: mismatched types 41 | fn d() -> X, String> { | ---------------------------- expected `X, std::string::String>` because of return type ... -49 | x +49 | x //~ ERROR mismatched types | ^ expected struct `std::string::String`, found integral variable | = note: expected type `X, std::string::String>` @@ -60,7 +60,7 @@ error[E0308]: mismatched types 52 | fn e() -> X, String> { | ---------------------------- expected `X, std::string::String>` because of return type ... -60 | x +60 | x //~ ERROR mismatched types | ^ expected struct `std::string::String`, found integral variable | = note: expected type `X, _>` diff --git a/src/test/ui/mismatched_types/binops.rs b/src/test/ui/mismatched_types/binops.rs index 98449e596641e..e45616cd67a81 100644 --- a/src/test/ui/mismatched_types/binops.rs +++ b/src/test/ui/mismatched_types/binops.rs @@ -9,10 +9,10 @@ // except according to those terms. fn main() { - 1 + Some(1); - 2 as usize - Some(1); - 3 * (); - 4 / ""; - 5 < String::new(); - 6 == Ok(1); + 1 + Some(1); //~ ERROR is not satisfied + 2 as usize - Some(1); //~ ERROR is not satisfied + 3 * (); //~ ERROR is not satisfied + 4 / ""; //~ ERROR is not satisfied + 5 < String::new(); //~ ERROR is not satisfied + 6 == Ok(1); //~ ERROR is not satisfied } diff --git a/src/test/ui/mismatched_types/binops.stderr b/src/test/ui/mismatched_types/binops.stderr index 6d1a39e0d93c0..8541ad52e0177 100644 --- a/src/test/ui/mismatched_types/binops.stderr +++ b/src/test/ui/mismatched_types/binops.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `{integer}: std::ops::Add>` is not satisfied --> $DIR/binops.rs:12:7 | -12 | 1 + Some(1); +12 | 1 + Some(1); //~ ERROR is not satisfied | ^ no implementation for `{integer} + std::option::Option<{integer}>` | = help: the trait `std::ops::Add>` is not implemented for `{integer}` @@ -9,7 +9,7 @@ error[E0277]: the trait bound `{integer}: std::ops::Add>` is not satisfied --> $DIR/binops.rs:13:16 | -13 | 2 as usize - Some(1); +13 | 2 as usize - Some(1); //~ ERROR is not satisfied | ^ no implementation for `usize - std::option::Option<{integer}>` | = help: the trait `std::ops::Sub>` is not implemented for `usize` @@ -17,7 +17,7 @@ error[E0277]: the trait bound `usize: std::ops::Sub` is not satisfied --> $DIR/binops.rs:14:7 | -14 | 3 * (); +14 | 3 * (); //~ ERROR is not satisfied | ^ no implementation for `{integer} * ()` | = help: the trait `std::ops::Mul<()>` is not implemented for `{integer}` @@ -25,7 +25,7 @@ error[E0277]: the trait bound `{integer}: std::ops::Mul<()>` is not satisfied error[E0277]: the trait bound `{integer}: std::ops::Div<&str>` is not satisfied --> $DIR/binops.rs:15:7 | -15 | 4 / ""; +15 | 4 / ""; //~ ERROR is not satisfied | ^ no implementation for `{integer} / &str` | = help: the trait `std::ops::Div<&str>` is not implemented for `{integer}` @@ -33,7 +33,7 @@ error[E0277]: the trait bound `{integer}: std::ops::Div<&str>` is not satisfied error[E0277]: the trait bound `{integer}: std::cmp::PartialOrd` is not satisfied --> $DIR/binops.rs:16:7 | -16 | 5 < String::new(); +16 | 5 < String::new(); //~ ERROR is not satisfied | ^ can't compare `{integer}` with `std::string::String` | = help: the trait `std::cmp::PartialOrd` is not implemented for `{integer}` @@ -41,7 +41,7 @@ error[E0277]: the trait bound `{integer}: std::cmp::PartialOrd>` is not satisfied --> $DIR/binops.rs:17:7 | -17 | 6 == Ok(1); +17 | 6 == Ok(1); //~ ERROR is not satisfied | ^^ can't compare `{integer}` with `std::result::Result<{integer}, _>` | = help: the trait `std::cmp::PartialEq>` is not implemented for `{integer}` diff --git a/src/test/ui/mismatched_types/cast-rfc0401.rs b/src/test/ui/mismatched_types/cast-rfc0401.rs index f72be0d7054db..15388b3a7647f 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.rs +++ b/src/test/ui/mismatched_types/cast-rfc0401.rs @@ -10,12 +10,12 @@ fn illegal_cast(u: *const U) -> *const V { - u as *const V + u as *const V //~ ERROR is invalid } fn illegal_cast_2(u: *const U) -> *const str { - u as *const str + u as *const str //~ ERROR is invalid } trait Foo { fn foo(&self) {} } @@ -36,47 +36,47 @@ fn main() let fat_sv : *const [i8] = unsafe { &*(0 as *const [i8; 1])}; let foo: &Foo = &f; - let _ = v as &u8; - let _ = v as E; - let _ = v as fn(); - let _ = v as (u32,); - let _ = Some(&v) as *const u8; + let _ = v as &u8; //~ ERROR non-primitive cast + let _ = v as E; //~ ERROR non-primitive cast + let _ = v as fn(); //~ ERROR non-primitive cast + let _ = v as (u32,); //~ ERROR non-primitive cast + let _ = Some(&v) as *const u8; //~ ERROR non-primitive cast - let _ = v as f32; - let _ = main as f64; - let _ = &v as usize; - let _ = f as *const u8; - let _ = 3_i32 as bool; - let _ = E::A as bool; - let _ = 0x61u32 as char; + let _ = v as f32; //~ ERROR is invalid + let _ = main as f64; //~ ERROR is invalid + let _ = &v as usize; //~ ERROR is invalid + let _ = f as *const u8; //~ ERROR is invalid + let _ = 3_i32 as bool; //~ ERROR cannot cast + let _ = E::A as bool; //~ ERROR cannot cast + let _ = 0x61u32 as char; //~ ERROR can be cast as - let _ = false as f32; - let _ = E::A as f32; - let _ = 'a' as f32; + let _ = false as f32; //~ ERROR is invalid + let _ = E::A as f32; //~ ERROR is invalid + let _ = 'a' as f32; //~ ERROR is invalid - let _ = false as *const u8; - let _ = E::A as *const u8; - let _ = 'a' as *const u8; + let _ = false as *const u8; //~ ERROR is invalid + let _ = E::A as *const u8; //~ ERROR is invalid + let _ = 'a' as *const u8; //~ ERROR is invalid - let _ = 42usize as *const [u8]; - let _ = v as *const [u8]; - let _ = fat_v as *const Foo; - let _ = foo as *const str; - let _ = foo as *mut str; - let _ = main as *mut str; - let _ = &f as *mut f32; - let _ = &f as *const f64; - let _ = fat_sv as usize; + let _ = 42usize as *const [u8]; //~ ERROR is invalid + let _ = v as *const [u8]; //~ ERROR cannot cast + let _ = fat_v as *const Foo; //~ ERROR is not satisfied + let _ = foo as *const str; //~ ERROR is invalid + let _ = foo as *mut str; //~ ERROR is invalid + let _ = main as *mut str; //~ ERROR is invalid + let _ = &f as *mut f32; //~ ERROR is invalid + let _ = &f as *const f64; //~ ERROR is invalid + let _ = fat_sv as usize; //~ ERROR is invalid let a : *const str = "hello"; - let _ = a as *const Foo; + let _ = a as *const Foo; //~ ERROR is not satisfied // check no error cascade - let _ = main.f as *const u32; + let _ = main.f as *const u32; //~ ERROR no field let cf: *const Foo = &0; - let _ = cf as *const [u16]; - let _ = cf as *const Bar; + let _ = cf as *const [u16]; //~ ERROR is invalid + let _ = cf as *const Bar; //~ ERROR is invalid - vec![0.0].iter().map(|s| s as f32).collect::>(); + vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid } diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index fb363c388b6e6..fa4f590362123 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -1,7 +1,7 @@ error[E0606]: casting `*const U` as `*const V` is invalid --> $DIR/cast-rfc0401.rs:13:5 | -13 | u as *const V +13 | u as *const V //~ ERROR is invalid | ^^^^^^^^^^^^^ | = note: vtable kinds may not match @@ -9,7 +9,7 @@ error[E0606]: casting `*const U` as `*const V` is invalid error[E0606]: casting `*const U` as `*const str` is invalid --> $DIR/cast-rfc0401.rs:18:5 | -18 | u as *const str +18 | u as *const str //~ ERROR is invalid | ^^^^^^^^^^^^^^^ | = note: vtable kinds may not match @@ -17,13 +17,13 @@ error[E0606]: casting `*const U` as `*const str` is invalid error[E0609]: no field `f` on type `fn() {main}` --> $DIR/cast-rfc0401.rs:75:18 | -75 | let _ = main.f as *const u32; +75 | let _ = main.f as *const u32; //~ ERROR no field | ^ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:39:13 | -39 | let _ = v as &u8; +39 | let _ = v as &u8; //~ ERROR non-primitive cast | ^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait @@ -31,7 +31,7 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:40:13 | -40 | let _ = v as E; +40 | let _ = v as E; //~ ERROR non-primitive cast | ^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait @@ -39,7 +39,7 @@ error[E0605]: non-primitive cast: `*const u8` as `E` error[E0605]: non-primitive cast: `*const u8` as `fn()` --> $DIR/cast-rfc0401.rs:41:13 | -41 | let _ = v as fn(); +41 | let _ = v as fn(); //~ ERROR non-primitive cast | ^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait @@ -47,7 +47,7 @@ error[E0605]: non-primitive cast: `*const u8` as `fn()` error[E0605]: non-primitive cast: `*const u8` as `(u32,)` --> $DIR/cast-rfc0401.rs:42:13 | -42 | let _ = v as (u32,); +42 | let _ = v as (u32,); //~ ERROR non-primitive cast | ^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait @@ -55,7 +55,7 @@ error[E0605]: non-primitive cast: `*const u8` as `(u32,)` error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:43:13 | -43 | let _ = Some(&v) as *const u8; +43 | let _ = Some(&v) as *const u8; //~ ERROR non-primitive cast | ^^^^^^^^^^^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait @@ -63,19 +63,19 @@ error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u error[E0606]: casting `*const u8` as `f32` is invalid --> $DIR/cast-rfc0401.rs:45:13 | -45 | let _ = v as f32; +45 | let _ = v as f32; //~ ERROR is invalid | ^^^^^^^^ error[E0606]: casting `fn() {main}` as `f64` is invalid --> $DIR/cast-rfc0401.rs:46:13 | -46 | let _ = main as f64; +46 | let _ = main as f64; //~ ERROR is invalid | ^^^^^^^^^^^ error[E0606]: casting `&*const u8` as `usize` is invalid --> $DIR/cast-rfc0401.rs:47:13 | -47 | let _ = &v as usize; +47 | let _ = &v as usize; //~ ERROR is invalid | ^^^^^^^^^^^ | = help: cast through a raw pointer first @@ -83,13 +83,13 @@ error[E0606]: casting `&*const u8` as `usize` is invalid error[E0606]: casting `f32` as `*const u8` is invalid --> $DIR/cast-rfc0401.rs:48:13 | -48 | let _ = f as *const u8; +48 | let _ = f as *const u8; //~ ERROR is invalid | ^^^^^^^^^^^^^^ error[E0054]: cannot cast as `bool` --> $DIR/cast-rfc0401.rs:49:13 | -49 | let _ = 3_i32 as bool; +49 | let _ = 3_i32 as bool; //~ ERROR cannot cast | ^^^^^^^^^^^^^ unsupported cast | = help: compare with zero instead @@ -97,7 +97,7 @@ error[E0054]: cannot cast as `bool` error[E0054]: cannot cast as `bool` --> $DIR/cast-rfc0401.rs:50:13 | -50 | let _ = E::A as bool; +50 | let _ = E::A as bool; //~ ERROR cannot cast | ^^^^^^^^^^^^ unsupported cast | = help: compare with zero instead @@ -105,13 +105,13 @@ error[E0054]: cannot cast as `bool` error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/cast-rfc0401.rs:51:13 | -51 | let _ = 0x61u32 as char; +51 | let _ = 0x61u32 as char; //~ ERROR can be cast as | ^^^^^^^^^^^^^^^ error[E0606]: casting `bool` as `f32` is invalid --> $DIR/cast-rfc0401.rs:53:13 | -53 | let _ = false as f32; +53 | let _ = false as f32; //~ ERROR is invalid | ^^^^^^^^^^^^ | = help: cast through an integer first @@ -119,7 +119,7 @@ error[E0606]: casting `bool` as `f32` is invalid error[E0606]: casting `E` as `f32` is invalid --> $DIR/cast-rfc0401.rs:54:13 | -54 | let _ = E::A as f32; +54 | let _ = E::A as f32; //~ ERROR is invalid | ^^^^^^^^^^^ | = help: cast through an integer first @@ -127,7 +127,7 @@ error[E0606]: casting `E` as `f32` is invalid error[E0606]: casting `char` as `f32` is invalid --> $DIR/cast-rfc0401.rs:55:13 | -55 | let _ = 'a' as f32; +55 | let _ = 'a' as f32; //~ ERROR is invalid | ^^^^^^^^^^ | = help: cast through an integer first @@ -135,67 +135,67 @@ error[E0606]: casting `char` as `f32` is invalid error[E0606]: casting `bool` as `*const u8` is invalid --> $DIR/cast-rfc0401.rs:57:13 | -57 | let _ = false as *const u8; +57 | let _ = false as *const u8; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^^^ error[E0606]: casting `E` as `*const u8` is invalid --> $DIR/cast-rfc0401.rs:58:13 | -58 | let _ = E::A as *const u8; +58 | let _ = E::A as *const u8; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^^ error[E0606]: casting `char` as `*const u8` is invalid --> $DIR/cast-rfc0401.rs:59:13 | -59 | let _ = 'a' as *const u8; +59 | let _ = 'a' as *const u8; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^ error[E0606]: casting `usize` as `*const [u8]` is invalid --> $DIR/cast-rfc0401.rs:61:13 | -61 | let _ = 42usize as *const [u8]; +61 | let _ = 42usize as *const [u8]; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^^^^^^^ error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` --> $DIR/cast-rfc0401.rs:62:13 | -62 | let _ = v as *const [u8]; +62 | let _ = v as *const [u8]; //~ ERROR cannot cast | ^^^^^^^^^^^^^^^^ error[E0606]: casting `&Foo` as `*const str` is invalid --> $DIR/cast-rfc0401.rs:64:13 | -64 | let _ = foo as *const str; +64 | let _ = foo as *const str; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^^ error[E0606]: casting `&Foo` as `*mut str` is invalid --> $DIR/cast-rfc0401.rs:65:13 | -65 | let _ = foo as *mut str; +65 | let _ = foo as *mut str; //~ ERROR is invalid | ^^^^^^^^^^^^^^^ error[E0606]: casting `fn() {main}` as `*mut str` is invalid --> $DIR/cast-rfc0401.rs:66:13 | -66 | let _ = main as *mut str; +66 | let _ = main as *mut str; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^ error[E0606]: casting `&f32` as `*mut f32` is invalid --> $DIR/cast-rfc0401.rs:67:13 | -67 | let _ = &f as *mut f32; +67 | let _ = &f as *mut f32; //~ ERROR is invalid | ^^^^^^^^^^^^^^ error[E0606]: casting `&f32` as `*const f64` is invalid --> $DIR/cast-rfc0401.rs:68:13 | -68 | let _ = &f as *const f64; +68 | let _ = &f as *const f64; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^ error[E0606]: casting `*const [i8]` as `usize` is invalid --> $DIR/cast-rfc0401.rs:69:13 | -69 | let _ = fat_sv as usize; +69 | let _ = fat_sv as usize; //~ ERROR is invalid | ^^^^^^^^^^^^^^^ | = help: cast through a thin pointer first @@ -203,7 +203,7 @@ error[E0606]: casting `*const [i8]` as `usize` is invalid error[E0606]: casting `*const Foo` as `*const [u16]` is invalid --> $DIR/cast-rfc0401.rs:78:13 | -78 | let _ = cf as *const [u16]; +78 | let _ = cf as *const [u16]; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^^^ | = note: vtable kinds may not match @@ -211,7 +211,7 @@ error[E0606]: casting `*const Foo` as `*const [u16]` is invalid error[E0606]: casting `*const Foo` as `*const Bar` is invalid --> $DIR/cast-rfc0401.rs:79:13 | -79 | let _ = cf as *const Bar; +79 | let _ = cf as *const Bar; //~ ERROR is invalid | ^^^^^^^^^^^^^^^^ | = note: vtable kinds may not match @@ -219,7 +219,7 @@ error[E0606]: casting `*const Foo` as `*const Bar` is invalid error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied --> $DIR/cast-rfc0401.rs:63:13 | -63 | let _ = fat_v as *const Foo; +63 | let _ = fat_v as *const Foo; //~ ERROR is not satisfied | ^^^^^ `[u8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` @@ -228,7 +228,7 @@ error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied --> $DIR/cast-rfc0401.rs:72:13 | -72 | let _ = a as *const Foo; +72 | let _ = a as *const Foo; //~ ERROR is not satisfied | ^ `str` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `str` @@ -237,13 +237,13 @@ error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied error[E0606]: casting `&{float}` as `f32` is invalid --> $DIR/cast-rfc0401.rs:81:30 | -81 | vec![0.0].iter().map(|s| s as f32).collect::>(); +81 | vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid | ^^^^^^^^ cannot cast `&{float}` as `f32` | help: did you mean `*s`? --> $DIR/cast-rfc0401.rs:81:30 | -81 | vec![0.0].iter().map(|s| s as f32).collect::>(); +81 | vec![0.0].iter().map(|s| s as f32).collect::>(); //~ ERROR is invalid | ^ error: aborting due to 34 previous errors diff --git a/src/test/ui/mismatched_types/closure-arg-count.rs b/src/test/ui/mismatched_types/closure-arg-count.rs index f94471a73ca27..dcdf3070d68a1 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.rs +++ b/src/test/ui/mismatched_types/closure-arg-count.rs @@ -13,7 +13,18 @@ fn f>(_: F) {} fn main() { [1, 2, 3].sort_by(|| panic!()); + //~^ ERROR closure is expected to take [1, 2, 3].sort_by(|tuple| panic!()); + //~^ ERROR closure is expected to take [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); + //~^ ERROR closure is expected to take f(|| panic!()); + //~^ ERROR closure is expected to take + + let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); + //~^ ERROR closure is expected to take + let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); + //~^ ERROR closure is expected to take + let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i); + //~^ ERROR closure is expected to take } diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr index 3031a77b1e828..2d792373cd7e7 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.stderr +++ b/src/test/ui/mismatched_types/closure-arg-count.stderr @@ -1,45 +1,60 @@ -error[E0593]: closure takes 0 arguments but 2 arguments are required +error[E0593]: closure is expected to take 2 arguments, but it takes 0 arguments --> $DIR/closure-arg-count.rs:15:15 | 15 | [1, 2, 3].sort_by(|| panic!()); - | ^^^^^^^ ----------- takes 0 arguments + | ^^^^^^^ -- takes 0 arguments | | | expected closure that takes 2 arguments -error[E0593]: closure takes 1 argument but 2 arguments are required - --> $DIR/closure-arg-count.rs:16:15 +error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument + --> $DIR/closure-arg-count.rs:17:15 | -16 | [1, 2, 3].sort_by(|tuple| panic!()); - | ^^^^^^^ ---------------- takes 1 argument +17 | [1, 2, 3].sort_by(|tuple| panic!()); + | ^^^^^^^ ------- takes 1 argument | | | expected closure that takes 2 arguments -error[E0308]: mismatched types - --> $DIR/closure-arg-count.rs:17:24 - | -17 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); - | ^^^^^^^^^^^^^^^ expected &{integer}, found tuple - | - = note: expected type `&{integer}` - found type `(_, _)` - -error[E0593]: closure takes 1 argument but 2 arguments are required - --> $DIR/closure-arg-count.rs:17:15 +error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument + --> $DIR/closure-arg-count.rs:19:15 | -17 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); - | ^^^^^^^ -------------------------- takes 1 argument +19 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); + | ^^^^^^^ ----------------- takes 1 argument | | | expected closure that takes 2 arguments -error[E0593]: closure takes 0 arguments but 1 argument is required - --> $DIR/closure-arg-count.rs:18:5 +error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments + --> $DIR/closure-arg-count.rs:21:5 | -18 | f(|| panic!()); - | ^ ----------- takes 0 arguments +21 | f(|| panic!()); + | ^ -- takes 0 arguments | | | expected closure that takes 1 argument | = note: required by `f` -error: aborting due to 5 previous errors +error[E0593]: closure is expected to take a single tuple as argument, but it takes 2 distinct arguments + --> $DIR/closure-arg-count.rs:24:53 + | +24 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); + | ^^^ ------ help: consider changing the closure to accept a tuple: `|(i, x)|` + | | + | expected closure that takes a single tuple as argument + +error[E0593]: closure is expected to take a single tuple as argument, but it takes 2 distinct arguments + --> $DIR/closure-arg-count.rs:26:53 + | +26 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); + | ^^^ ------------- help: consider changing the closure to accept a tuple: `|(i, x): (usize, _)|` + | | + | expected closure that takes a single tuple as argument + +error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 3 distinct arguments + --> $DIR/closure-arg-count.rs:28:53 + | +28 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i); + | ^^^ --------- takes 3 distinct arguments + | | + | expected closure that takes a single 2-tuple as argument + +error: aborting due to 7 previous errors diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs index aa9dba4c3f417..566998c374e9e 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs @@ -10,12 +10,13 @@ fn main() { let a = [(1u32, 2u32)]; - a.iter().map(|_: (u32, u32)| 45); - a.iter().map(|_: &(u16, u16)| 45); - a.iter().map(|_: (u16, u16)| 45); + a.iter().map(|_: (u32, u32)| 45); //~ ERROR type mismatch + a.iter().map(|_: &(u16, u16)| 45); //~ ERROR type mismatch + a.iter().map(|_: (u16, u16)| 45); //~ ERROR type mismatch } fn baz(_: F) {} fn _test<'a>(f: fn(*mut &'a u32)) { - baz(f); + baz(f); //~ ERROR type mismatch + //~^ ERROR type mismatch } diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr index 866a024ab08b8..77d3a33276737 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -1,7 +1,7 @@ error[E0631]: type mismatch in closure arguments --> $DIR/closure-arg-type-mismatch.rs:13:14 | -13 | a.iter().map(|_: (u32, u32)| 45); +13 | a.iter().map(|_: (u32, u32)| 45); //~ ERROR type mismatch | ^^^ ------------------ found signature of `fn((u32, u32)) -> _` | | | expected signature of `fn(&(u32, u32)) -> _` @@ -9,7 +9,7 @@ error[E0631]: type mismatch in closure arguments error[E0631]: type mismatch in closure arguments --> $DIR/closure-arg-type-mismatch.rs:14:14 | -14 | a.iter().map(|_: &(u16, u16)| 45); +14 | a.iter().map(|_: &(u16, u16)| 45); //~ ERROR type mismatch | ^^^ ------------------- found signature of `for<'r> fn(&'r (u16, u16)) -> _` | | | expected signature of `fn(&(u32, u32)) -> _` @@ -17,7 +17,7 @@ error[E0631]: type mismatch in closure arguments error[E0631]: type mismatch in closure arguments --> $DIR/closure-arg-type-mismatch.rs:15:14 | -15 | a.iter().map(|_: (u16, u16)| 45); +15 | a.iter().map(|_: (u16, u16)| 45); //~ ERROR type mismatch | ^^^ ------------------ found signature of `fn((u16, u16)) -> _` | | | expected signature of `fn(&(u32, u32)) -> _` @@ -25,7 +25,7 @@ error[E0631]: type mismatch in closure arguments error[E0631]: type mismatch in function arguments --> $DIR/closure-arg-type-mismatch.rs:20:5 | -20 | baz(f); +20 | baz(f); //~ ERROR type mismatch | ^^^ | | | expected signature of `for<'r> fn(*mut &'r u32) -> _` @@ -36,7 +36,7 @@ error[E0631]: type mismatch in function arguments error[E0271]: type mismatch resolving `for<'r> >::Output == ()` --> $DIR/closure-arg-type-mismatch.rs:20:5 | -20 | baz(f); +20 | baz(f); //~ ERROR type mismatch | ^^^ expected bound lifetime parameter, found concrete lifetime | = note: required by `baz` diff --git a/src/test/ui/mismatched_types/closure-mismatch.rs b/src/test/ui/mismatched_types/closure-mismatch.rs index 91298cb2bbd52..5a74e8f933de0 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.rs +++ b/src/test/ui/mismatched_types/closure-mismatch.rs @@ -15,5 +15,6 @@ impl Foo for T {} fn baz(_: T) {} fn main() { - baz(|_| ()); + baz(|_| ()); //~ ERROR type mismatch + //~^ ERROR type mismatch } diff --git a/src/test/ui/mismatched_types/closure-mismatch.stderr b/src/test/ui/mismatched_types/closure-mismatch.stderr index a54fd118cc5ea..99767ba1afaef 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-mismatch.stderr @@ -1,7 +1,7 @@ error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.rs:18:9: 18:15] as std::ops::FnOnce<(&'r (),)>>::Output == ()` --> $DIR/closure-mismatch.rs:18:5 | -18 | baz(|_| ()); +18 | baz(|_| ()); //~ ERROR type mismatch | ^^^ expected bound lifetime parameter, found concrete lifetime | = note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:18:9: 18:15]` @@ -10,7 +10,7 @@ error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.r error[E0631]: type mismatch in closure arguments --> $DIR/closure-mismatch.rs:18:5 | -18 | baz(|_| ()); +18 | baz(|_| ()); //~ ERROR type mismatch | ^^^ ------ found signature of `fn(_) -> _` | | | expected signature of `for<'r> fn(&'r ()) -> _` diff --git a/src/test/ui/mismatched_types/const-fn-in-trait.rs b/src/test/ui/mismatched_types/const-fn-in-trait.rs index 5e44030eab71a..e0d5c19f12558 100644 --- a/src/test/ui/mismatched_types/const-fn-in-trait.rs +++ b/src/test/ui/mismatched_types/const-fn-in-trait.rs @@ -14,11 +14,11 @@ trait Foo { fn f() -> u32; - const fn g(); + const fn g(); //~ ERROR cannot be declared const } impl Foo for u32 { - const fn f() -> u32 { 22 } + const fn f() -> u32 { 22 } //~ ERROR cannot be declared const fn g() {} } diff --git a/src/test/ui/mismatched_types/const-fn-in-trait.stderr b/src/test/ui/mismatched_types/const-fn-in-trait.stderr index f7b7635e41aec..4911db6b2eb43 100644 --- a/src/test/ui/mismatched_types/const-fn-in-trait.stderr +++ b/src/test/ui/mismatched_types/const-fn-in-trait.stderr @@ -1,13 +1,13 @@ error[E0379]: trait fns cannot be declared const --> $DIR/const-fn-in-trait.rs:17:5 | -17 | const fn g(); +17 | const fn g(); //~ ERROR cannot be declared const | ^^^^^ trait fns cannot be const error[E0379]: trait fns cannot be declared const --> $DIR/const-fn-in-trait.rs:21:5 | -21 | const fn f() -> u32 { 22 } +21 | const fn f() -> u32 { 22 } //~ ERROR cannot be declared const | ^^^^^ trait fns cannot be const error: aborting due to 2 previous errors diff --git a/src/test/ui/mismatched_types/fn-variance-1.rs b/src/test/ui/mismatched_types/fn-variance-1.rs index 4bea8177b7c5e..e9e34abc09203 100644 --- a/src/test/ui/mismatched_types/fn-variance-1.rs +++ b/src/test/ui/mismatched_types/fn-variance-1.rs @@ -20,12 +20,14 @@ fn main() { apply(&3, takes_imm); apply(&3, takes_mut); //~^ ERROR type mismatch - //~| NOTE types differ in mutability //~| NOTE required by `apply` + //~| NOTE expected signature + //~| NOTE found signature apply(&mut 3, takes_mut); apply(&mut 3, takes_imm); //~^ ERROR type mismatch - //~| NOTE types differ in mutability //~| NOTE required by `apply` + //~| NOTE expected signature + //~| NOTE found signature } diff --git a/src/test/ui/mismatched_types/fn-variance-1.stderr b/src/test/ui/mismatched_types/fn-variance-1.stderr index 09a90ef3d6be6..e593298633af5 100644 --- a/src/test/ui/mismatched_types/fn-variance-1.stderr +++ b/src/test/ui/mismatched_types/fn-variance-1.stderr @@ -10,9 +10,9 @@ error[E0631]: type mismatch in function arguments = note: required by `apply` error[E0631]: type mismatch in function arguments - --> $DIR/fn-variance-1.rs:27:5 + --> $DIR/fn-variance-1.rs:28:5 | -27 | apply(&mut 3, takes_imm); +28 | apply(&mut 3, takes_imm); | ^^^^^ | | | expected signature of `fn(&mut {integer}) -> _` diff --git a/src/test/ui/mismatched_types/issue-19109.rs b/src/test/ui/mismatched_types/issue-19109.rs index 580684e2e140b..59127c10cd1e6 100644 --- a/src/test/ui/mismatched_types/issue-19109.rs +++ b/src/test/ui/mismatched_types/issue-19109.rs @@ -14,7 +14,6 @@ fn function(t: &mut Trait) { t as *mut Trait //~^ ERROR: mismatched types //~| NOTE: expected type `()` - //~| NOTE: found type `*mut Trait` //~| NOTE: expected (), found *-ptr } diff --git a/src/test/ui/mismatched_types/issue-26480.rs b/src/test/ui/mismatched_types/issue-26480.rs index f842627e76fee..33c5e74fafa1f 100644 --- a/src/test/ui/mismatched_types/issue-26480.rs +++ b/src/test/ui/mismatched_types/issue-26480.rs @@ -23,13 +23,13 @@ macro_rules! write { const stdout: i32 = 1; unsafe { write(stdout, $arr.as_ptr() as *const i8, - $arr.len() * size_of($arr[0])); + $arr.len() * size_of($arr[0])); //~ ERROR mismatched types } }} } macro_rules! cast { - ($x:expr) => ($x as ()) + ($x:expr) => ($x as ()) //~ ERROR non-primitive cast } fn main() { diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index fae831ffb868f..5d25cb2f93c15 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/issue-26480.rs:26:19 | -26 | $arr.len() * size_of($arr[0])); +26 | $arr.len() * size_of($arr[0])); //~ ERROR mismatched types | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize ... 37 | write!(hello); @@ -10,7 +10,7 @@ error[E0308]: mismatched types error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:32:19 | -32 | ($x:expr) => ($x as ()) +32 | ($x:expr) => ($x as ()) //~ ERROR non-primitive cast | ^^^^^^^^ ... 38 | cast!(2); diff --git a/src/test/ui/mismatched_types/issue-35030.rs b/src/test/ui/mismatched_types/issue-35030.rs index 006074ead13bd..503b2e08c39bb 100644 --- a/src/test/ui/mismatched_types/issue-35030.rs +++ b/src/test/ui/mismatched_types/issue-35030.rs @@ -16,7 +16,7 @@ trait Parser { impl Parser for bool { fn parse(text: &str) -> Option { - Some(true) + Some(true) //~ ERROR mismatched types } } diff --git a/src/test/ui/mismatched_types/issue-35030.stderr b/src/test/ui/mismatched_types/issue-35030.stderr index 46d690c5f037b..3ec5d1b7b40c1 100644 --- a/src/test/ui/mismatched_types/issue-35030.stderr +++ b/src/test/ui/mismatched_types/issue-35030.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/issue-35030.rs:19:14 | -19 | Some(true) +19 | Some(true) //~ ERROR mismatched types | ^^^^ expected type parameter, found bool | = note: expected type `bool` (type parameter) diff --git a/src/test/ui/mismatched_types/issue-36053-2.rs b/src/test/ui/mismatched_types/issue-36053-2.rs index 7e489621e2102..76885651c5b53 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.rs +++ b/src/test/ui/mismatched_types/issue-36053-2.rs @@ -16,13 +16,8 @@ use std::iter::once; fn main() { once::<&str>("str").fuse().filter(|a: &str| true).count(); //~^ ERROR no method named `count` - //~| ERROR E0281 - //~| ERROR E0281 - //~| NOTE expected &str, found str - //~| NOTE expected &str, found str - //~| NOTE implements - //~| NOTE implements - //~| NOTE requires - //~| NOTE requires + //~| ERROR type mismatch in closure arguments //~| NOTE the method `count` exists but the following trait bounds + //~| NOTE expected signature + //~| NOTE found signature } diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs index cf66330017f58..b9b6b05996b66 100644 --- a/src/test/ui/mismatched_types/issue-38371.rs +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -13,7 +13,7 @@ struct Foo { } -fn foo(&foo: Foo) { +fn foo(&foo: Foo) { //~ ERROR mismatched types } fn bar(foo: Foo) { @@ -25,13 +25,15 @@ fn qux(foo: &Foo) { fn zar(&foo: &Foo) { } -fn agh(&&bar: &u32) { +// The somewhat unexpected help message in this case is courtesy of +// match_default_bindings. +fn agh(&&bar: &u32) { //~ ERROR mismatched types } -fn bgh(&&bar: u32) { +fn bgh(&&bar: u32) { //~ ERROR mismatched types } -fn ugh(&[bar]: &u32) { +fn ugh(&[bar]: &u32) { //~ ERROR expected an array or slice } fn main() {} diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 9efee4cc5593c..d34f05e054a67 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/issue-38371.rs:16:8 | -16 | fn foo(&foo: Foo) { +16 | fn foo(&foo: Foo) { //~ ERROR mismatched types | ^^^^ expected struct `Foo`, found reference | = note: expected type `Foo` @@ -9,27 +9,28 @@ error[E0308]: mismatched types = help: did you mean `foo: &Foo`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:28:9 + --> $DIR/issue-38371.rs:30:9 | -28 | fn agh(&&bar: &u32) { +30 | fn agh(&&bar: &u32) { //~ ERROR mismatched types | ^^^^ expected u32, found reference | = note: expected type `u32` found type `&_` + = help: did you mean `bar: &u32`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:31:8 + --> $DIR/issue-38371.rs:33:8 | -31 | fn bgh(&&bar: u32) { +33 | fn bgh(&&bar: u32) { //~ ERROR mismatched types | ^^^^^ expected u32, found reference | = note: expected type `u32` found type `&_` error[E0529]: expected an array or slice, found `u32` - --> $DIR/issue-38371.rs:34:9 + --> $DIR/issue-38371.rs:36:9 | -34 | fn ugh(&[bar]: &u32) { +36 | fn ugh(&[bar]: &u32) { //~ ERROR expected an array or slice | ^^^^^ pattern cannot match with input type `u32` error: aborting due to 4 previous errors diff --git a/src/test/ui/mismatched_types/main.rs b/src/test/ui/mismatched_types/main.rs index f7f1c78c3ba0d..7cf1de7cfee3f 100644 --- a/src/test/ui/mismatched_types/main.rs +++ b/src/test/ui/mismatched_types/main.rs @@ -9,7 +9,7 @@ // except according to those terms. fn main() { - let x: u32 = ( + let x: u32 = ( //~ ERROR mismatched types ); } diff --git a/src/test/ui/mismatched_types/main.stderr b/src/test/ui/mismatched_types/main.stderr index c8941fbf95073..41e4c51239804 100644 --- a/src/test/ui/mismatched_types/main.stderr +++ b/src/test/ui/mismatched_types/main.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/main.rs:12:18 | -12 | let x: u32 = ( +12 | let x: u32 = ( //~ ERROR mismatched types | __________________^ 13 | | ); | |_____^ expected u32, found () diff --git a/src/test/ui/mismatched_types/overloaded-calls-bad.rs b/src/test/ui/mismatched_types/overloaded-calls-bad.rs index 3295e2bebd231..da1265dfeff7e 100644 --- a/src/test/ui/mismatched_types/overloaded-calls-bad.rs +++ b/src/test/ui/mismatched_types/overloaded-calls-bad.rs @@ -38,7 +38,6 @@ fn main() { let ans = s("what"); //~ ERROR mismatched types //~^ NOTE expected isize, found reference //~| NOTE expected type - //~| NOTE found type let ans = s(); //~^ ERROR this function takes 1 parameter but 0 parameters were supplied //~| NOTE expected 1 parameter diff --git a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr index cd05684f15d55..feec86342e5cd 100644 --- a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr +++ b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr @@ -8,15 +8,15 @@ error[E0308]: mismatched types found type `&'static str` error[E0057]: this function takes 1 parameter but 0 parameters were supplied - --> $DIR/overloaded-calls-bad.rs:42:15 + --> $DIR/overloaded-calls-bad.rs:41:15 | -42 | let ans = s(); +41 | let ans = s(); | ^^^ expected 1 parameter error[E0057]: this function takes 1 parameter but 2 parameters were supplied - --> $DIR/overloaded-calls-bad.rs:45:17 + --> $DIR/overloaded-calls-bad.rs:44:17 | -45 | let ans = s("burma", "shave"); +44 | let ans = s("burma", "shave"); | ^^^^^^^^^^^^^^^^ expected 1 parameter error: aborting due to 3 previous errors diff --git a/src/test/ui/mismatched_types/trait-bounds-cant-coerce.rs b/src/test/ui/mismatched_types/trait-bounds-cant-coerce.rs index 9f832c7b6e500..115be1bf4de55 100644 --- a/src/test/ui/mismatched_types/trait-bounds-cant-coerce.rs +++ b/src/test/ui/mismatched_types/trait-bounds-cant-coerce.rs @@ -22,8 +22,7 @@ fn c(x: Box) { fn d(x: Box) { a(x); //~ ERROR mismatched types [E0308] - //~| NOTE expected type `Box` - //~| NOTE found type `Box` + //~| NOTE expected type `std::boxed::Box` //~| NOTE expected trait `Foo + std::marker::Send`, found trait `Foo` } diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs index 099c8699e493b..420b59a4df1af 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs @@ -18,8 +18,8 @@ trait Foo { struct Bar; impl Foo for Bar { - fn foo(x: i16) { } - fn bar(&mut self, bar: &Bar) { } + fn foo(x: i16) { } //~ ERROR incompatible type + fn bar(&mut self, bar: &Bar) { } //~ ERROR incompatible type } fn main() { diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index 349432f64bbc2..f3cf1d5661533 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -4,7 +4,7 @@ error[E0053]: method `foo` has an incompatible type for trait 14 | fn foo(x: u16); | --- type in trait ... -21 | fn foo(x: i16) { } +21 | fn foo(x: i16) { } //~ ERROR incompatible type | ^^^ expected u16, found i16 | = note: expected type `fn(u16)` @@ -16,7 +16,7 @@ error[E0053]: method `bar` has an incompatible type for trait 15 | fn bar(&mut self, bar: &mut Bar); | -------- type in trait ... -22 | fn bar(&mut self, bar: &Bar) { } +22 | fn bar(&mut self, bar: &Bar) { } //~ ERROR incompatible type | ^^^^ types differ in mutability | = note: expected type `fn(&mut Bar, &mut Bar)` diff --git a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs index 693a1585320ee..814f2c4d187cd 100644 --- a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs +++ b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.rs @@ -20,15 +20,10 @@ fn call_itisize>(y: isize, mut f: F) -> isize { pub fn main() { let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y }); - //~^ NOTE implements - //~| NOTE implements + //~^ NOTE found signature of `fn(usize, isize) let z = call_it(3, f); //~^ ERROR type mismatch - //~| NOTE expected isize, found usize - //~| NOTE expected isize, found usize - //~| NOTE requires - //~| NOTE requires - //~| NOTE required by `call_it` - //~| NOTE required by `call_it` + //~| NOTE expected signature of `fn(isize, isize) + //~| required by `call_it` println!("{}", z); } diff --git a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr index 5988364928013..1d25632c5e2c2 100644 --- a/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr +++ b/src/test/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr @@ -1,10 +1,10 @@ error[E0631]: type mismatch in closure arguments - --> $DIR/unboxed-closures-vtable-mismatch.rs:25:13 + --> $DIR/unboxed-closures-vtable-mismatch.rs:24:13 | 22 | let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y }); | -------------------------------------------------- found signature of `fn(usize, isize) -> _` -... -25 | let z = call_it(3, f); +23 | //~^ NOTE found signature of `fn(usize, isize) +24 | let z = call_it(3, f); | ^^^^^^^ expected signature of `fn(isize, isize) -> _` | = note: required by `call_it` diff --git a/src/test/ui/missing-items/issue-40221.rs b/src/test/ui/missing-items/issue-40221.rs index 9cf1c7d6de8a4..526fc3a8658a2 100644 --- a/src/test/ui/missing-items/issue-40221.rs +++ b/src/test/ui/missing-items/issue-40221.rs @@ -18,7 +18,7 @@ enum PC { } fn test(proto: P) { - match proto { + match proto { //~ ERROR non-exhaustive patterns P::C(PC::Q) => (), } } diff --git a/src/test/ui/missing-items/issue-40221.stderr b/src/test/ui/missing-items/issue-40221.stderr index fc90c8a2b20be..883c4329f4db6 100644 --- a/src/test/ui/missing-items/issue-40221.stderr +++ b/src/test/ui/missing-items/issue-40221.stderr @@ -1,7 +1,7 @@ error[E0004]: non-exhaustive patterns: `C(QA)` not covered --> $DIR/issue-40221.rs:21:11 | -21 | match proto { +21 | match proto { //~ ERROR non-exhaustive patterns | ^^^^^ pattern `C(QA)` not covered error: aborting due to previous error diff --git a/src/test/ui/missing-items/m2.rs b/src/test/ui/missing-items/m2.rs index ffd7ff7f4323d..9f1954526916f 100644 --- a/src/test/ui/missing-items/m2.rs +++ b/src/test/ui/missing-items/m2.rs @@ -16,5 +16,5 @@ extern crate m1; struct X { } -impl m1::X for X { +impl m1::X for X { //~ ERROR not all trait items implemented } diff --git a/src/test/ui/missing-items/m2.stderr b/src/test/ui/missing-items/m2.stderr index ce061bd167a88..47164a5304abb 100644 --- a/src/test/ui/missing-items/m2.stderr +++ b/src/test/ui/missing-items/m2.stderr @@ -3,7 +3,7 @@ error[E0601]: main function not found error[E0046]: not all trait items implemented, missing: `CONSTANT`, `Type`, `method` --> $DIR/m2.rs:19:1 | -19 | / impl m1::X for X { +19 | / impl m1::X for X { //~ ERROR not all trait items implemented 20 | | } | |_^ missing `CONSTANT`, `Type`, `method` in implementation | diff --git a/src/test/ui/missing-items/missing-type-parameter.rs b/src/test/ui/missing-items/missing-type-parameter.rs index 79368587062e8..f2d5359fb167b 100644 --- a/src/test/ui/missing-items/missing-type-parameter.rs +++ b/src/test/ui/missing-items/missing-type-parameter.rs @@ -11,5 +11,5 @@ fn foo() { } fn main() { - foo(); + foo(); //~ ERROR type annotations needed } diff --git a/src/test/ui/missing-items/missing-type-parameter.stderr b/src/test/ui/missing-items/missing-type-parameter.stderr index a16ae5538bf92..1cb9e5f56d351 100644 --- a/src/test/ui/missing-items/missing-type-parameter.stderr +++ b/src/test/ui/missing-items/missing-type-parameter.stderr @@ -1,7 +1,7 @@ error[E0282]: type annotations needed --> $DIR/missing-type-parameter.rs:14:5 | -14 | foo(); +14 | foo(); //~ ERROR type annotations needed | ^^^ cannot infer type for `X` error: aborting due to previous error diff --git a/src/test/ui/mut-ref.rs b/src/test/ui/mut-ref.rs index f888369968852..715c4adf2e692 100644 --- a/src/test/ui/mut-ref.rs +++ b/src/test/ui/mut-ref.rs @@ -11,6 +11,6 @@ // compile-flags: -Z parse-only fn main() { - let mut ref x = 10; + let mut ref x = 10; //~ ERROR the order of `mut` and `ref` is incorrect let ref mut y = 11; } diff --git a/src/test/ui/mut-ref.stderr b/src/test/ui/mut-ref.stderr index ce6a42f1e5ef1..aaab243e22f72 100644 --- a/src/test/ui/mut-ref.stderr +++ b/src/test/ui/mut-ref.stderr @@ -1,7 +1,7 @@ error: the order of `mut` and `ref` is incorrect --> $DIR/mut-ref.rs:14:9 | -14 | let mut ref x = 10; +14 | let mut ref x = 10; //~ ERROR the order of `mut` and `ref` is incorrect | ^^^^^^^ help: try switching the order: `ref mut` error: aborting due to previous error diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs new file mode 100644 index 0000000000000..e5944e75e4241 --- /dev/null +++ b/src/test/ui/nll/get_default.rs @@ -0,0 +1,57 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Znll -Zborrowck=compare + +struct Map { +} + +impl Map { + fn get(&self) -> Option<&String> { None } + fn set(&mut self, v: String) { } +} + +fn ok(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + return v; + } + None => { + map.set(String::new()); // Just AST errors here + //~^ ERROR borrowed as immutable (Ast) + } + } + } +} + +fn err(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + map.set(String::new()); // Both AST and MIR error here + //~^ ERROR borrowed as immutable (Mir) + //~| ERROR borrowed as immutable (Ast) + return v; + } + None => { + map.set(String::new()); // Just AST errors here + //~^ ERROR borrowed as immutable (Ast) + } + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr new file mode 100644 index 0000000000000..fff2684af1399 --- /dev/null +++ b/src/test/ui/nll/get_default.stderr @@ -0,0 +1,47 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:33:17 + | +28 | match map.get() { + | --- immutable borrow occurs here +... +33 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +38 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:44:17 + | +42 | match map.get() { + | --- immutable borrow occurs here +43 | Some(v) => { +44 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here +... +55 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:50:17 + | +42 | match map.get() { + | --- immutable borrow occurs here +... +50 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +55 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:44:17 + | +42 | match map.get() { + | --- immutable borrow occurs here +43 | Some(v) => { +44 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs new file mode 100644 index 0000000000000..0047f6d59237c --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +} diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr new file mode 100644 index 0000000000000..389334f9c1d8d --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs new file mode 100644 index 0000000000000..64a4d39100063 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + std::mem::drop(wrap); + x = 1; // OK, drop is inert +} diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs new file mode 100644 index 0000000000000..3242136f005e7 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr new file mode 100644 index 0000000000000..9edeca2d18801 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-fragment.rs:31:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs new file mode 100644 index 0000000000000..3e32818b8dcf3 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ This currently errors and it should not. +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr new file mode 100644 index 0000000000000..24d0d6d04c8da --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:32:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs new file mode 100644 index 0000000000000..291fcbd73f3e1 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr new file mode 100644 index 0000000000000..7b1b55d133ac5 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop.rs:26:5 + | +25 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/named-region-basic.rs b/src/test/ui/nll/named-region-basic.rs new file mode 100644 index 0000000000000..001ce41c27793 --- /dev/null +++ b/src/test/ui/nll/named-region-basic.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Znll + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { + &*x //~ ERROR free region `'a` does not outlive `'b` + //~^ ERROR `*x` does not live long enough + //~| WARN not reporting region error due to -Znll +} + +fn main() { } diff --git a/src/test/ui/nll/named-region-basic.stderr b/src/test/ui/nll/named-region-basic.stderr new file mode 100644 index 0000000000000..9c1de6c366cc9 --- /dev/null +++ b/src/test/ui/nll/named-region-basic.stderr @@ -0,0 +1,31 @@ +warning: not reporting region error due to -Znll + --> $DIR/named-region-basic.rs:19:5 + | +19 | &*x //~ ERROR free region `'a` does not outlive `'b` + | ^^^ + +error[E0597]: `*x` does not live long enough + --> $DIR/named-region-basic.rs:19:6 + | +19 | &*x //~ ERROR free region `'a` does not outlive `'b` + | ^^ does not live long enough + | + = note: borrowed value must be valid for the static lifetime... +note: ...but borrowed value is only valid for the lifetime 'a as defined on the function body at 18:1 + --> $DIR/named-region-basic.rs:18:1 + | +18 | / fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { +19 | | &*x //~ ERROR free region `'a` does not outlive `'b` +20 | | //~^ ERROR `*x` does not live long enough +21 | | //~| WARN not reporting region error due to -Znll +22 | | } + | |_^ + +error: free region `'a` does not outlive `'b` + --> $DIR/named-region-basic.rs:19:5 + | +19 | &*x //~ ERROR free region `'a` does not outlive `'b` + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/on-unimplemented/bad-annotation.rs b/src/test/ui/on-unimplemented/bad-annotation.rs index 54d3b3e087653..e7483dbd3b546 100644 --- a/src/test/ui/on-unimplemented/bad-annotation.rs +++ b/src/test/ui/on-unimplemented/bad-annotation.rs @@ -23,7 +23,7 @@ trait MyFromIterator { fn my_from_iter>(iterator: T) -> Self; } -#[rustc_on_unimplemented] //~ ERROR this attribute must have a value +#[rustc_on_unimplemented] //~ ERROR `#[rustc_on_unimplemented]` requires a value trait BadAnnotation1 {} @@ -38,27 +38,34 @@ trait BadAnnotation3 {} #[rustc_on_unimplemented(lorem="")] +//~^ this attribute must have a valid trait BadAnnotation4 {} #[rustc_on_unimplemented(lorem(ipsum(dolor)))] +//~^ this attribute must have a valid trait BadAnnotation5 {} #[rustc_on_unimplemented(message="x", message="y")] +//~^ this attribute must have a valid trait BadAnnotation6 {} #[rustc_on_unimplemented(message="x", on(desugared, message="y"))] +//~^ this attribute must have a valid trait BadAnnotation7 {} #[rustc_on_unimplemented(on(), message="y")] +//~^ empty `on`-clause trait BadAnnotation8 {} #[rustc_on_unimplemented(on="x", message="y")] +//~^ this attribute must have a valid trait BadAnnotation9 {} #[rustc_on_unimplemented(on(x="y"), message="y")] trait BadAnnotation10 {} #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")] +//~^ this attribute must have a valid trait BadAnnotation11 {} pub fn main() { diff --git a/src/test/ui/on-unimplemented/bad-annotation.stderr b/src/test/ui/on-unimplemented/bad-annotation.stderr index 73834f4422d38..7126cc76eb727 100644 --- a/src/test/ui/on-unimplemented/bad-annotation.stderr +++ b/src/test/ui/on-unimplemented/bad-annotation.stderr @@ -1,7 +1,7 @@ error[E0232]: `#[rustc_on_unimplemented]` requires a value --> $DIR/bad-annotation.rs:26:1 | -26 | #[rustc_on_unimplemented] //~ ERROR this attribute must have a value +26 | #[rustc_on_unimplemented] //~ ERROR `#[rustc_on_unimplemented]` requires a value | ^^^^^^^^^^^^^^^^^^^^^^^^^ value required here | = note: eg `#[rustc_on_unimplemented = "foo"]` @@ -27,47 +27,47 @@ error[E0232]: this attribute must have a valid value = note: eg `#[rustc_on_unimplemented = "foo"]` error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:43:26 + --> $DIR/bad-annotation.rs:44:26 | -43 | #[rustc_on_unimplemented(lorem(ipsum(dolor)))] +44 | #[rustc_on_unimplemented(lorem(ipsum(dolor)))] | ^^^^^^^^^^^^^^^^^^^ expected value here | = note: eg `#[rustc_on_unimplemented = "foo"]` error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:46:39 + --> $DIR/bad-annotation.rs:48:39 | -46 | #[rustc_on_unimplemented(message="x", message="y")] +48 | #[rustc_on_unimplemented(message="x", message="y")] | ^^^^^^^^^^^ expected value here | = note: eg `#[rustc_on_unimplemented = "foo"]` error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:49:39 + --> $DIR/bad-annotation.rs:52:39 | -49 | #[rustc_on_unimplemented(message="x", on(desugared, message="y"))] +52 | #[rustc_on_unimplemented(message="x", on(desugared, message="y"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here | = note: eg `#[rustc_on_unimplemented = "foo"]` error[E0232]: empty `on`-clause in `#[rustc_on_unimplemented]` - --> $DIR/bad-annotation.rs:52:26 + --> $DIR/bad-annotation.rs:56:26 | -52 | #[rustc_on_unimplemented(on(), message="y")] +56 | #[rustc_on_unimplemented(on(), message="y")] | ^^^^ empty on-clause here error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:55:26 + --> $DIR/bad-annotation.rs:60:26 | -55 | #[rustc_on_unimplemented(on="x", message="y")] +60 | #[rustc_on_unimplemented(on="x", message="y")] | ^^^^^^ expected value here | = note: eg `#[rustc_on_unimplemented = "foo"]` error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:61:40 + --> $DIR/bad-annotation.rs:67:40 | -61 | #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")] +67 | #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here | = note: eg `#[rustc_on_unimplemented = "foo"]` diff --git a/src/test/ui/owl-import-generates-unused-import-lint.rs b/src/test/ui/owl-import-generates-unused-import-lint.rs new file mode 100644 index 0000000000000..dc30c31835299 --- /dev/null +++ b/src/test/ui/owl-import-generates-unused-import-lint.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(use_nested_groups)] +#![deny(unused_imports)] + +mod foo { + pub enum Bar {} +} + +use foo::{*, *}; //~ ERROR unused import: `*` + +fn main() { + let _: Bar; +} diff --git a/src/test/ui/owl-import-generates-unused-import-lint.stderr b/src/test/ui/owl-import-generates-unused-import-lint.stderr new file mode 100644 index 0000000000000..79089b2a93c73 --- /dev/null +++ b/src/test/ui/owl-import-generates-unused-import-lint.stderr @@ -0,0 +1,14 @@ +error: unused import: `*` + --> $DIR/owl-import-generates-unused-import-lint.rs:18:14 + | +18 | use foo::{*, *}; //~ ERROR unused import: `*` + | ^ + | +note: lint level defined here + --> $DIR/owl-import-generates-unused-import-lint.rs:12:9 + | +12 | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/path-lookahead.stderr b/src/test/ui/path-lookahead.stderr index 1d4ab35046b48..059312b2851c6 100644 --- a/src/test/ui/path-lookahead.stderr +++ b/src/test/ui/path-lookahead.stderr @@ -2,9 +2,14 @@ warning: unnecessary parentheses around `return` value --> $DIR/path-lookahead.rs:18:10 | 18 | return (::to_string(&arg)); //~WARN unnecessary parentheses around `return` value - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove these parentheses | - = note: #[warn(unused_parens)] on by default +note: lint level defined here + --> $DIR/path-lookahead.rs:13:9 + | +13 | #![warn(unused)] + | ^^^^^^ + = note: #[warn(unused_parens)] implied by #[warn(unused)] warning: function is never used: `with_parens` --> $DIR/path-lookahead.rs:17:1 diff --git a/src/test/ui/print_type_sizes/nullable.rs b/src/test/ui/print_type_sizes/niche-filling.rs similarity index 76% rename from src/test/ui/print_type_sizes/nullable.rs rename to src/test/ui/print_type_sizes/niche-filling.rs index 5052c59a39dcf..f1c419d889556 100644 --- a/src/test/ui/print_type_sizes/nullable.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -10,8 +10,8 @@ // compile-flags: -Z print-type-sizes -// This file illustrates how enums with a non-null field are handled, -// modelled after cases like `Option<&u32>` and such. +// This file illustrates how niche-filling enums are handled, +// modelled after cases like `Option<&u32>`, `Option` and such. // // It uses NonZero directly, rather than `&_` or `Unique<_>`, because // the test is not set up to deal with target-dependent pointer width. @@ -68,8 +68,22 @@ impl One for u32 { fn one() -> Self { 1 } } +pub enum Enum4 { + One(A), + Two(B), + Three(C), + Four(D) +} + pub fn main() { let _x: MyOption> = Default::default(); let _y: EmbeddedDiscr = Default::default(); let _z: MyOption> = Default::default(); + let _a: MyOption = Default::default(); + let _b: MyOption = Default::default(); + let _c: MyOption = Default::default(); + let _b: MyOption> = Default::default(); + let _e: Enum4<(), char, (), ()> = Enum4::One(()); + let _f: Enum4<(), (), bool, ()> = Enum4::One(()); + let _g: Enum4<(), (), (), MyOption> = Enum4::One(()); } diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout new file mode 100644 index 0000000000000..af3e89a936ee0 --- /dev/null +++ b/src/test/ui/print_type_sizes/niche-filling.stdout @@ -0,0 +1,80 @@ +print-type-size type: `IndirectNonZero`: 12 bytes, alignment: 4 bytes +print-type-size field `.nested`: 8 bytes +print-type-size field `.post`: 2 bytes +print-type-size field `.pre`: 1 bytes +print-type-size end padding: 1 bytes +print-type-size type: `MyOption>`: 12 bytes, alignment: 4 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 12 bytes +print-type-size field `.0`: 12 bytes +print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Record`: 7 bytes +print-type-size field `.val`: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size field `.pre`: 1 bytes +print-type-size end padding: 1 bytes +print-type-size type: `NestedNonZero`: 8 bytes, alignment: 4 bytes +print-type-size field `.val`: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size field `.pre`: 1 bytes +print-type-size end padding: 1 bytes +print-type-size type: `Enum4<(), char, (), ()>`: 4 bytes, alignment: 4 bytes +print-type-size variant `One`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Two`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size variant `Three`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Four`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size type: `MyOption`: 4 bytes, alignment: 4 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size type: `MyOption>`: 4 bytes, alignment: 4 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size type: `core::nonzero::NonZero`: 4 bytes, alignment: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size type: `Enum4<(), (), (), MyOption>`: 2 bytes, alignment: 1 bytes +print-type-size variant `One`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Two`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Three`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Four`: 2 bytes +print-type-size field `.0`: 2 bytes +print-type-size type: `MyOption>`: 2 bytes, alignment: 1 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 2 bytes +print-type-size field `.0`: 2 bytes +print-type-size type: `MyOption`: 2 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 1 bytes +print-type-size field `.0`: 1 bytes +print-type-size type: `Enum4<(), (), bool, ()>`: 1 bytes, alignment: 1 bytes +print-type-size variant `One`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Two`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Three`: 1 bytes +print-type-size field `.0`: 1 bytes +print-type-size variant `Four`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size type: `MyOption`: 1 bytes, alignment: 1 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 1 bytes +print-type-size field `.0`: 1 bytes +print-type-size type: `MyOption`: 1 bytes, alignment: 1 bytes +print-type-size variant `None`: 0 bytes +print-type-size variant `Some`: 1 bytes +print-type-size field `.0`: 1 bytes +print-type-size type: `core::cmp::Ordering`: 1 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Less`: 0 bytes +print-type-size variant `Equal`: 0 bytes +print-type-size variant `Greater`: 0 bytes diff --git a/src/test/ui/print_type_sizes/nullable.stdout b/src/test/ui/print_type_sizes/nullable.stdout deleted file mode 100644 index 830678f174f88..0000000000000 --- a/src/test/ui/print_type_sizes/nullable.stdout +++ /dev/null @@ -1,24 +0,0 @@ -print-type-size type: `IndirectNonZero`: 12 bytes, alignment: 4 bytes -print-type-size field `.nested`: 8 bytes -print-type-size field `.post`: 2 bytes -print-type-size field `.pre`: 1 bytes -print-type-size end padding: 1 bytes -print-type-size type: `MyOption>`: 12 bytes, alignment: 4 bytes -print-type-size variant `Some`: 12 bytes -print-type-size field `.0`: 12 bytes -print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes -print-type-size variant `Record`: 7 bytes -print-type-size field `.val`: 4 bytes -print-type-size field `.post`: 2 bytes -print-type-size field `.pre`: 1 bytes -print-type-size end padding: 1 bytes -print-type-size type: `NestedNonZero`: 8 bytes, alignment: 4 bytes -print-type-size field `.val`: 4 bytes -print-type-size field `.post`: 2 bytes -print-type-size field `.pre`: 1 bytes -print-type-size end padding: 1 bytes -print-type-size type: `MyOption>`: 4 bytes, alignment: 4 bytes -print-type-size variant `Some`: 4 bytes -print-type-size field `.0`: 4 bytes -print-type-size type: `core::nonzero::NonZero`: 4 bytes, alignment: 4 bytes -print-type-size field `.0`: 4 bytes diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs new file mode 100644 index 0000000000000..69cc4c933601e --- /dev/null +++ b/src/test/ui/print_type_sizes/uninhabited.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +#![feature(never_type)] + +pub fn main() { + let _x: Option = None; + let _y: Result = Ok(42); +} diff --git a/src/test/ui/print_type_sizes/uninhabited.stdout b/src/test/ui/print_type_sizes/uninhabited.stdout new file mode 100644 index 0000000000000..2a8706f7ac551 --- /dev/null +++ b/src/test/ui/print_type_sizes/uninhabited.stdout @@ -0,0 +1,5 @@ +print-type-size type: `std::result::Result`: 4 bytes, alignment: 4 bytes +print-type-size variant `Ok`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size type: `std::option::Option`: 0 bytes, alignment: 1 bytes +print-type-size variant `None`: 0 bytes diff --git a/src/test/ui/pub/pub-restricted-error-fn.rs b/src/test/ui/pub/pub-restricted-error-fn.rs index 13514310371cc..58f3d379ed991 100644 --- a/src/test/ui/pub/pub-restricted-error-fn.rs +++ b/src/test/ui/pub/pub-restricted-error-fn.rs @@ -10,4 +10,4 @@ #![feature(pub_restricted)] -pub(crate) () fn foo() {} +pub(crate) () fn foo() {} //~ unmatched visibility diff --git a/src/test/ui/pub/pub-restricted-error-fn.stderr b/src/test/ui/pub/pub-restricted-error-fn.stderr index 470e833124785..9cfc3968ab125 100644 --- a/src/test/ui/pub/pub-restricted-error-fn.stderr +++ b/src/test/ui/pub/pub-restricted-error-fn.stderr @@ -1,7 +1,7 @@ error: unmatched visibility `pub` --> $DIR/pub-restricted-error-fn.rs:13:10 | -13 | pub(crate) () fn foo() {} +13 | pub(crate) () fn foo() {} //~ unmatched visibility | ^ error: aborting due to previous error diff --git a/src/test/ui/pub/pub-restricted-error.rs b/src/test/ui/pub/pub-restricted-error.rs index 99af031899ab6..4822d01a591b8 100644 --- a/src/test/ui/pub/pub-restricted-error.rs +++ b/src/test/ui/pub/pub-restricted-error.rs @@ -13,7 +13,7 @@ struct Bar(pub(())); struct Foo { - pub(crate) () foo: usize, + pub(crate) () foo: usize, //~ ERROR expected identifier } - +fn main() {} diff --git a/src/test/ui/pub/pub-restricted-error.stderr b/src/test/ui/pub/pub-restricted-error.stderr index b8b4c80778d96..cbf206e6aed5d 100644 --- a/src/test/ui/pub/pub-restricted-error.stderr +++ b/src/test/ui/pub/pub-restricted-error.stderr @@ -1,7 +1,7 @@ error: expected identifier, found `(` --> $DIR/pub-restricted-error.rs:16:16 | -16 | pub(crate) () foo: usize, +16 | pub(crate) () foo: usize, //~ ERROR expected identifier | ^ error: aborting due to previous error diff --git a/src/test/ui/pub/pub-restricted-non-path.rs b/src/test/ui/pub/pub-restricted-non-path.rs index 3f74285717a7b..11428b778edfc 100644 --- a/src/test/ui/pub/pub-restricted-non-path.rs +++ b/src/test/ui/pub/pub-restricted-non-path.rs @@ -10,6 +10,6 @@ #![feature(pub_restricted)] -pub (.) fn afn() {} +pub (.) fn afn() {} //~ ERROR expected identifier fn main() {} diff --git a/src/test/ui/pub/pub-restricted-non-path.stderr b/src/test/ui/pub/pub-restricted-non-path.stderr index ebfccc4d72045..b76e87840c6de 100644 --- a/src/test/ui/pub/pub-restricted-non-path.stderr +++ b/src/test/ui/pub/pub-restricted-non-path.stderr @@ -1,7 +1,7 @@ error: expected identifier, found `.` --> $DIR/pub-restricted-non-path.rs:13:6 | -13 | pub (.) fn afn() {} +13 | pub (.) fn afn() {} //~ ERROR expected identifier | ^ error: aborting due to previous error diff --git a/src/test/ui/pub/pub-restricted.rs b/src/test/ui/pub/pub-restricted.rs index 934ad24c16779..07184d935b437 100644 --- a/src/test/ui/pub/pub-restricted.rs +++ b/src/test/ui/pub/pub-restricted.rs @@ -12,8 +12,8 @@ mod a {} -pub (a) fn afn() {} -pub (b) fn bfn() {} +pub (a) fn afn() {} //~ incorrect visibility restriction +pub (b) fn bfn() {} //~ incorrect visibility restriction pub fn privfn() {} mod x { mod y { @@ -29,8 +29,8 @@ mod y { pub (super) s: usize, valid_private: usize, pub (in y) valid_in_x: usize, - pub (a) invalid: usize, - pub (in x) non_parent_invalid: usize, + pub (a) invalid: usize, //~ incorrect visibility restriction + pub (in x) non_parent_invalid: usize, //~ ERROR visibilities can only be restricted } } @@ -38,4 +38,4 @@ fn main() {} // test multichar names mod xyz {} -pub (xyz) fn xyz() {} +pub (xyz) fn xyz() {} //~ incorrect visibility restriction diff --git a/src/test/ui/pub/pub-restricted.stderr b/src/test/ui/pub/pub-restricted.stderr index ae283f1fb636a..0bedcddc0b4cc 100644 --- a/src/test/ui/pub/pub-restricted.stderr +++ b/src/test/ui/pub/pub-restricted.stderr @@ -1,7 +1,7 @@ error: incorrect visibility restriction --> $DIR/pub-restricted.rs:15:6 | -15 | pub (a) fn afn() {} +15 | pub (a) fn afn() {} //~ incorrect visibility restriction | ^ help: make this visible only to module `a` with `in`: `in a` | = help: some possible visibility restrictions are: @@ -12,7 +12,7 @@ error: incorrect visibility restriction error: incorrect visibility restriction --> $DIR/pub-restricted.rs:16:6 | -16 | pub (b) fn bfn() {} +16 | pub (b) fn bfn() {} //~ incorrect visibility restriction | ^ help: make this visible only to module `b` with `in`: `in b` | = help: some possible visibility restrictions are: @@ -23,7 +23,7 @@ error: incorrect visibility restriction error: incorrect visibility restriction --> $DIR/pub-restricted.rs:32:14 | -32 | pub (a) invalid: usize, +32 | pub (a) invalid: usize, //~ incorrect visibility restriction | ^ help: make this visible only to module `a` with `in`: `in a` | = help: some possible visibility restrictions are: @@ -34,7 +34,7 @@ error: incorrect visibility restriction error: incorrect visibility restriction --> $DIR/pub-restricted.rs:41:6 | -41 | pub (xyz) fn xyz() {} +41 | pub (xyz) fn xyz() {} //~ incorrect visibility restriction | ^^^ help: make this visible only to module `xyz` with `in`: `in xyz` | = help: some possible visibility restrictions are: @@ -45,7 +45,7 @@ error: incorrect visibility restriction error: visibilities can only be restricted to ancestor modules --> $DIR/pub-restricted.rs:33:17 | -33 | pub (in x) non_parent_invalid: usize, +33 | pub (in x) non_parent_invalid: usize, //~ ERROR visibilities can only be restricted | ^ error: aborting due to 5 previous errors diff --git a/src/test/ui/reachable/expr_add.rs b/src/test/ui/reachable/expr_add.rs index 87d017adf6819..dd43c58de6df1 100644 --- a/src/test/ui/reachable/expr_add.rs +++ b/src/test/ui/reachable/expr_add.rs @@ -24,5 +24,5 @@ impl ops::Add for Foo { } fn main() { - let x = Foo + return; + let x = Foo + return; //~ ERROR unreachable } diff --git a/src/test/ui/reachable/expr_add.stderr b/src/test/ui/reachable/expr_add.stderr index 1a2cc252051bf..4ae286d2fff11 100644 --- a/src/test/ui/reachable/expr_add.stderr +++ b/src/test/ui/reachable/expr_add.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_add.rs:27:13 | -27 | let x = Foo + return; +27 | let x = Foo + return; //~ ERROR unreachable | ^^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_again.stderr b/src/test/ui/reachable/expr_again.stderr index bf4e4dc4711cb..152c96e52b6ab 100644 --- a/src/test/ui/reachable/expr_again.stderr +++ b/src/test/ui/reachable/expr_again.stderr @@ -9,7 +9,7 @@ note: lint level defined here | 13 | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/reachable/expr_array.rs b/src/test/ui/reachable/expr_array.rs index 00e8be0772547..31229668796e8 100644 --- a/src/test/ui/reachable/expr_array.rs +++ b/src/test/ui/reachable/expr_array.rs @@ -17,12 +17,12 @@ fn a() { // the `22` is unreachable: - let x: [usize; 2] = [return, 22]; + let x: [usize; 2] = [return, 22]; //~ ERROR unreachable } fn b() { // the `array is unreachable: - let x: [usize; 2] = [22, return]; + let x: [usize; 2] = [22, return]; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_array.stderr b/src/test/ui/reachable/expr_array.stderr index f8dbdb5f8bb66..0f64d15850360 100644 --- a/src/test/ui/reachable/expr_array.stderr +++ b/src/test/ui/reachable/expr_array.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_array.rs:20:34 | -20 | let x: [usize; 2] = [return, 22]; +20 | let x: [usize; 2] = [return, 22]; //~ ERROR unreachable | ^^ | note: lint level defined here @@ -13,7 +13,7 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_array.rs:25:25 | -25 | let x: [usize; 2] = [22, return]; +25 | let x: [usize; 2] = [22, return]; //~ ERROR unreachable | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/reachable/expr_assign.rs b/src/test/ui/reachable/expr_assign.rs index 1b9357013d270..e6fb46a5bac0c 100644 --- a/src/test/ui/reachable/expr_assign.rs +++ b/src/test/ui/reachable/expr_assign.rs @@ -17,7 +17,7 @@ fn foo() { // No error here. let x; - x = return; + x = return; //~ ERROR unreachable } fn bar() { @@ -27,13 +27,13 @@ fn bar() { // Here we consider the `return` unreachable because // "evaluating" the `*p` has type `!`. This is somewhat // dubious, I suppose. - *p = return; + *p = return; //~ ERROR unreachable } } fn baz() { let mut i = 0; - *{return; &mut i} = 22; + *{return; &mut i} = 22; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_assign.stderr b/src/test/ui/reachable/expr_assign.stderr index 807f6a1c1d584..42c00d5a8b7d8 100644 --- a/src/test/ui/reachable/expr_assign.stderr +++ b/src/test/ui/reachable/expr_assign.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_assign.rs:20:5 | -20 | x = return; +20 | x = return; //~ ERROR unreachable | ^^^^^^^^^^ | note: lint level defined here @@ -13,13 +13,13 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_assign.rs:30:14 | -30 | *p = return; +30 | *p = return; //~ ERROR unreachable | ^^^^^^ error: unreachable expression --> $DIR/expr_assign.rs:36:15 | -36 | *{return; &mut i} = 22; +36 | *{return; &mut i} = 22; //~ ERROR unreachable | ^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/reachable/expr_block.rs b/src/test/ui/reachable/expr_block.rs index 093589b4dc839..57b5d3cabce61 100644 --- a/src/test/ui/reachable/expr_block.rs +++ b/src/test/ui/reachable/expr_block.rs @@ -18,7 +18,7 @@ fn a() { // Here the tail expression is considered unreachable: let x = { return; - 22 + 22 //~ ERROR unreachable }; } diff --git a/src/test/ui/reachable/expr_block.stderr b/src/test/ui/reachable/expr_block.stderr index 542ce1c3fd9cb..4c08be524f282 100644 --- a/src/test/ui/reachable/expr_block.stderr +++ b/src/test/ui/reachable/expr_block.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_block.rs:21:9 | -21 | 22 +21 | 22 //~ ERROR unreachable | ^^ | note: lint level defined here @@ -16,7 +16,7 @@ error: unreachable statement 36 | println!("foo"); | ^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/reachable/expr_box.rs b/src/test/ui/reachable/expr_box.rs index 6509b608335af..ab62cbdf83747 100644 --- a/src/test/ui/reachable/expr_box.rs +++ b/src/test/ui/reachable/expr_box.rs @@ -13,6 +13,6 @@ #![deny(unreachable_code)] fn main() { - let x = box return; + let x = box return; //~ ERROR unreachable println!("hi"); } diff --git a/src/test/ui/reachable/expr_box.stderr b/src/test/ui/reachable/expr_box.stderr index 78ba231cef9fc..d8b5d9f8d7684 100644 --- a/src/test/ui/reachable/expr_box.stderr +++ b/src/test/ui/reachable/expr_box.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_box.rs:16:13 | -16 | let x = box return; +16 | let x = box return; //~ ERROR unreachable | ^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_call.rs b/src/test/ui/reachable/expr_call.rs index 8d9f303df7fdc..86b95aad9c253 100644 --- a/src/test/ui/reachable/expr_call.rs +++ b/src/test/ui/reachable/expr_call.rs @@ -20,12 +20,12 @@ fn bar(x: !) { } fn a() { // the `22` is unreachable: - foo(return, 22); + foo(return, 22); //~ ERROR unreachable } fn b() { // the call is unreachable: - bar(return); + bar(return); //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_call.stderr b/src/test/ui/reachable/expr_call.stderr index 5526827f59fc9..eaafe8dc5d593 100644 --- a/src/test/ui/reachable/expr_call.stderr +++ b/src/test/ui/reachable/expr_call.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_call.rs:23:17 | -23 | foo(return, 22); +23 | foo(return, 22); //~ ERROR unreachable | ^^ | note: lint level defined here @@ -13,7 +13,7 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_call.rs:28:5 | -28 | bar(return); +28 | bar(return); //~ ERROR unreachable | ^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/reachable/expr_cast.rs b/src/test/ui/reachable/expr_cast.rs index 926ef864ebf21..76b00c00ad985 100644 --- a/src/test/ui/reachable/expr_cast.rs +++ b/src/test/ui/reachable/expr_cast.rs @@ -17,7 +17,7 @@ fn a() { // the cast is unreachable: - let x = {return} as !; + let x = {return} as !; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_cast.stderr b/src/test/ui/reachable/expr_cast.stderr index a22300dcc1398..d6fb37768c541 100644 --- a/src/test/ui/reachable/expr_cast.stderr +++ b/src/test/ui/reachable/expr_cast.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_cast.rs:20:13 | -20 | let x = {return} as !; +20 | let x = {return} as !; //~ ERROR unreachable | ^^^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_if.stderr b/src/test/ui/reachable/expr_if.stderr index 2cf17474f6e9d..b8f3f494c5c35 100644 --- a/src/test/ui/reachable/expr_if.stderr +++ b/src/test/ui/reachable/expr_if.stderr @@ -9,7 +9,7 @@ note: lint level defined here | 14 | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/reachable/expr_loop.stderr b/src/test/ui/reachable/expr_loop.stderr index 6e98e754c54db..ce4b30c798411 100644 --- a/src/test/ui/reachable/expr_loop.stderr +++ b/src/test/ui/reachable/expr_loop.stderr @@ -9,7 +9,7 @@ note: lint level defined here | 14 | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: unreachable statement --> $DIR/expr_loop.rs:31:5 @@ -17,7 +17,7 @@ error: unreachable statement 31 | println!("I am dead."); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: unreachable statement --> $DIR/expr_loop.rs:41:5 @@ -25,7 +25,7 @@ error: unreachable statement 41 | println!("I am dead."); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/reachable/expr_match.rs b/src/test/ui/reachable/expr_match.rs index 23bdcc035b227..d2b96e51a9519 100644 --- a/src/test/ui/reachable/expr_match.rs +++ b/src/test/ui/reachable/expr_match.rs @@ -17,7 +17,7 @@ fn a() { // The match is considered unreachable here, because the `return` // diverges: - match {return} { } + match {return} { } //~ ERROR unreachable } fn b() { diff --git a/src/test/ui/reachable/expr_match.stderr b/src/test/ui/reachable/expr_match.stderr index f5857a5b345ec..499beac644c52 100644 --- a/src/test/ui/reachable/expr_match.stderr +++ b/src/test/ui/reachable/expr_match.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_match.rs:20:5 | -20 | match {return} { } +20 | match {return} { } //~ ERROR unreachable | ^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -16,7 +16,7 @@ error: unreachable statement 25 | println!("I am dead"); | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: unreachable statement --> $DIR/expr_match.rs:35:5 @@ -24,7 +24,7 @@ error: unreachable statement 35 | println!("I am dead"); | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/reachable/expr_method.rs b/src/test/ui/reachable/expr_method.rs index f1d979d7df79d..8be71e464b202 100644 --- a/src/test/ui/reachable/expr_method.rs +++ b/src/test/ui/reachable/expr_method.rs @@ -23,12 +23,12 @@ impl Foo { fn a() { // the `22` is unreachable: - Foo.foo(return, 22); + Foo.foo(return, 22); //~ ERROR unreachable } fn b() { // the call is unreachable: - Foo.bar(return); + Foo.bar(return); //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_method.stderr b/src/test/ui/reachable/expr_method.stderr index 177d4352a376d..db9d5c3d22c1a 100644 --- a/src/test/ui/reachable/expr_method.stderr +++ b/src/test/ui/reachable/expr_method.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_method.rs:26:21 | -26 | Foo.foo(return, 22); +26 | Foo.foo(return, 22); //~ ERROR unreachable | ^^ | note: lint level defined here @@ -13,7 +13,7 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_method.rs:31:5 | -31 | Foo.bar(return); +31 | Foo.bar(return); //~ ERROR unreachable | ^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/reachable/expr_repeat.rs b/src/test/ui/reachable/expr_repeat.rs index 6078d6d5bde46..47ee2ba62b829 100644 --- a/src/test/ui/reachable/expr_repeat.rs +++ b/src/test/ui/reachable/expr_repeat.rs @@ -17,7 +17,7 @@ fn a() { // the repeat is unreachable: - let x: [usize; 2] = [return; 2]; + let x: [usize; 2] = [return; 2]; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_repeat.stderr b/src/test/ui/reachable/expr_repeat.stderr index 19afc5dd7b5ee..54b29b616f3d7 100644 --- a/src/test/ui/reachable/expr_repeat.stderr +++ b/src/test/ui/reachable/expr_repeat.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_repeat.rs:20:25 | -20 | let x: [usize; 2] = [return; 2]; +20 | let x: [usize; 2] = [return; 2]; //~ ERROR unreachable | ^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_return.rs b/src/test/ui/reachable/expr_return.rs index c640ca0663029..fac1116dc6897 100644 --- a/src/test/ui/reachable/expr_return.rs +++ b/src/test/ui/reachable/expr_return.rs @@ -18,7 +18,7 @@ fn a() { // Here we issue that the "2nd-innermost" return is unreachable, // but we stop there. - let x = {return {return {return;}}}; + let x = {return {return {return;}}}; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_return.stderr b/src/test/ui/reachable/expr_return.stderr index 3eb70a4dd7c84..a96def6011eb8 100644 --- a/src/test/ui/reachable/expr_return.stderr +++ b/src/test/ui/reachable/expr_return.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_return.rs:21:22 | -21 | let x = {return {return {return;}}}; +21 | let x = {return {return {return;}}}; //~ ERROR unreachable | ^^^^^^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_struct.rs b/src/test/ui/reachable/expr_struct.rs index 09e31819279f2..b5acd395be620 100644 --- a/src/test/ui/reachable/expr_struct.rs +++ b/src/test/ui/reachable/expr_struct.rs @@ -22,22 +22,22 @@ struct Foo { fn a() { // struct expr is unreachable: - let x = Foo { a: 22, b: 33, ..return }; + let x = Foo { a: 22, b: 33, ..return }; //~ ERROR unreachable } fn b() { // the `33` is unreachable: - let x = Foo { a: return, b: 33, ..return }; + let x = Foo { a: return, b: 33, ..return }; //~ ERROR unreachable } fn c() { // the `..return` is unreachable: - let x = Foo { a: 22, b: return, ..return }; + let x = Foo { a: 22, b: return, ..return }; //~ ERROR unreachable } fn d() { // the struct expr is unreachable: - let x = Foo { a: 22, b: return }; + let x = Foo { a: 22, b: return }; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_struct.stderr b/src/test/ui/reachable/expr_struct.stderr index 4b7ac6604132c..b2cb1ef19cf83 100644 --- a/src/test/ui/reachable/expr_struct.stderr +++ b/src/test/ui/reachable/expr_struct.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_struct.rs:25:13 | -25 | let x = Foo { a: 22, b: 33, ..return }; +25 | let x = Foo { a: 22, b: 33, ..return }; //~ ERROR unreachable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -13,19 +13,19 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_struct.rs:30:33 | -30 | let x = Foo { a: return, b: 33, ..return }; +30 | let x = Foo { a: return, b: 33, ..return }; //~ ERROR unreachable | ^^ error: unreachable expression --> $DIR/expr_struct.rs:35:39 | -35 | let x = Foo { a: 22, b: return, ..return }; +35 | let x = Foo { a: 22, b: return, ..return }; //~ ERROR unreachable | ^^^^^^ error: unreachable expression --> $DIR/expr_struct.rs:40:13 | -40 | let x = Foo { a: 22, b: return }; +40 | let x = Foo { a: 22, b: return }; //~ ERROR unreachable | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/reachable/expr_tup.rs b/src/test/ui/reachable/expr_tup.rs index 7c75296de6c54..089020bf3853f 100644 --- a/src/test/ui/reachable/expr_tup.rs +++ b/src/test/ui/reachable/expr_tup.rs @@ -17,12 +17,12 @@ fn a() { // the `2` is unreachable: - let x: (usize, usize) = (return, 2); + let x: (usize, usize) = (return, 2); //~ ERROR unreachable } fn b() { // the tuple is unreachable: - let x: (usize, usize) = (2, return); + let x: (usize, usize) = (2, return); //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_tup.stderr b/src/test/ui/reachable/expr_tup.stderr index 63f477fd0c373..af43162a98447 100644 --- a/src/test/ui/reachable/expr_tup.stderr +++ b/src/test/ui/reachable/expr_tup.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_tup.rs:20:38 | -20 | let x: (usize, usize) = (return, 2); +20 | let x: (usize, usize) = (return, 2); //~ ERROR unreachable | ^ | note: lint level defined here @@ -13,7 +13,7 @@ note: lint level defined here error: unreachable expression --> $DIR/expr_tup.rs:25:29 | -25 | let x: (usize, usize) = (2, return); +25 | let x: (usize, usize) = (2, return); //~ ERROR unreachable | ^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/reachable/expr_type.rs b/src/test/ui/reachable/expr_type.rs index 2fa277c382e87..29c59d5304f92 100644 --- a/src/test/ui/reachable/expr_type.rs +++ b/src/test/ui/reachable/expr_type.rs @@ -17,7 +17,7 @@ fn a() { // the cast is unreachable: - let x = {return}: !; + let x = {return}: !; //~ ERROR unreachable } fn main() { } diff --git a/src/test/ui/reachable/expr_type.stderr b/src/test/ui/reachable/expr_type.stderr index 6ed79974ccb77..d6bcb4ec80f8b 100644 --- a/src/test/ui/reachable/expr_type.stderr +++ b/src/test/ui/reachable/expr_type.stderr @@ -1,7 +1,7 @@ error: unreachable expression --> $DIR/expr_type.rs:20:13 | -20 | let x = {return}: !; +20 | let x = {return}: !; //~ ERROR unreachable | ^^^^^^^^^^^ | note: lint level defined here diff --git a/src/test/ui/reachable/expr_unary.rs b/src/test/ui/reachable/expr_unary.rs index 57901fbaa7c44..ad12cb876fe9b 100644 --- a/src/test/ui/reachable/expr_unary.rs +++ b/src/test/ui/reachable/expr_unary.rs @@ -12,10 +12,14 @@ #![allow(unused_assignments)] #![allow(dead_code)] #![deny(unreachable_code)] +#![deny(coerce_never)] #![feature(never_type)] fn foo() { - let x: ! = ! { return; 22 }; + let x: ! = ! { return; 22 }; //~ ERROR unreachable + //~^ ERROR cannot coerce + //~| hard error + //~| ERROR cannot apply unary operator `!` to type `!` } fn main() { } diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr index 9f4562fe29718..39120f0bdf980 100644 --- a/src/test/ui/reachable/expr_unary.stderr +++ b/src/test/ui/reachable/expr_unary.stderr @@ -1,7 +1,7 @@ error: unreachable expression - --> $DIR/expr_unary.rs:18:28 + --> $DIR/expr_unary.rs:19:28 | -18 | let x: ! = ! { return; 22 }; +19 | let x: ! = ! { return; 22 }; //~ ERROR unreachable | ^^ | note: lint level defined here @@ -10,11 +10,25 @@ note: lint level defined here 14 | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ +error: cannot coerce `{integer}` to ! + --> $DIR/expr_unary.rs:19:28 + | +19 | let x: ! = ! { return; 22 }; //~ ERROR unreachable + | ^^ + | +note: lint level defined here + --> $DIR/expr_unary.rs:15:9 + | +15 | #![deny(coerce_never)] + | ^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46325 + error[E0600]: cannot apply unary operator `!` to type `!` - --> $DIR/expr_unary.rs:18:16 + --> $DIR/expr_unary.rs:19:16 | -18 | let x: ! = ! { return; 22 }; +19 | let x: ! = ! { return; 22 }; //~ ERROR unreachable | ^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/reachable/expr_while.stderr b/src/test/ui/reachable/expr_while.stderr index 066cfc86c6462..36109826983e7 100644 --- a/src/test/ui/reachable/expr_while.stderr +++ b/src/test/ui/reachable/expr_while.stderr @@ -9,7 +9,7 @@ note: lint level defined here | 14 | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: unreachable statement --> $DIR/expr_while.rs:33:9 @@ -17,7 +17,7 @@ error: unreachable statement 33 | println!("I am dead."); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: unreachable statement --> $DIR/expr_while.rs:35:5 @@ -25,7 +25,7 @@ error: unreachable statement 35 | println!("I am, too."); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions-fn-subtyping-return-static.stderr index 1598a8a40d2f0..4a97537223cf6 100644 --- a/src/test/ui/regions-fn-subtyping-return-static.stderr +++ b/src/test/ui/regions-fn-subtyping-return-static.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types 51 | want_F(bar); //~ ERROR E0308 | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx | - = note: expected type `fn(&'cx S) -> &'cx S` - found type `fn(&'a S) -> &S {bar::<'_>}` + = note: expected type `for<'cx> fn(&'cx S) -> &'cx S` + found type `for<'a> fn(&'a S) -> &S {bar::<'_>}` error: aborting due to previous error diff --git a/src/test/ui/resolve/enums-are-namespaced-xc.rs b/src/test/ui/resolve/enums-are-namespaced-xc.rs index 4f55f33d7f808..4059aa5527ed1 100644 --- a/src/test/ui/resolve/enums-are-namespaced-xc.rs +++ b/src/test/ui/resolve/enums-are-namespaced-xc.rs @@ -13,12 +13,9 @@ extern crate namespaced_enums; fn main() { let _ = namespaced_enums::A; - //~^ ERROR unresolved value `namespaced_enums::A` - //~| HELP you can import it into scope: `use namespaced_enums::Foo::A;` + //~^ ERROR cannot find value `A` let _ = namespaced_enums::B(10); - //~^ ERROR unresolved function `namespaced_enums::B` - //~| HELP you can import it into scope: `use namespaced_enums::Foo::B;` + //~^ ERROR cannot find function `B` let _ = namespaced_enums::C { a: 10 }; - //~^ ERROR unresolved struct, variant or union type `namespaced_enums::C` - //~| HELP you can import it into scope: `use namespaced_enums::Foo::C;` + //~^ ERROR cannot find struct, variant or union type `C` } diff --git a/src/test/ui/resolve/enums-are-namespaced-xc.stderr b/src/test/ui/resolve/enums-are-namespaced-xc.stderr index a401861274deb..5acc678df90e3 100644 --- a/src/test/ui/resolve/enums-are-namespaced-xc.stderr +++ b/src/test/ui/resolve/enums-are-namespaced-xc.stderr @@ -3,29 +3,26 @@ error[E0425]: cannot find value `A` in module `namespaced_enums` | 15 | let _ = namespaced_enums::A; | ^ not found in `namespaced_enums` - | help: possible candidate is found in another module, you can import it into scope | 14 | use namespaced_enums::Foo::A; | error[E0425]: cannot find function `B` in module `namespaced_enums` - --> $DIR/enums-are-namespaced-xc.rs:18:31 + --> $DIR/enums-are-namespaced-xc.rs:17:31 | -18 | let _ = namespaced_enums::B(10); +17 | let _ = namespaced_enums::B(10); | ^ not found in `namespaced_enums` - | help: possible candidate is found in another module, you can import it into scope | 14 | use namespaced_enums::Foo::B; | error[E0422]: cannot find struct, variant or union type `C` in module `namespaced_enums` - --> $DIR/enums-are-namespaced-xc.rs:21:31 + --> $DIR/enums-are-namespaced-xc.rs:19:31 | -21 | let _ = namespaced_enums::C { a: 10 }; +19 | let _ = namespaced_enums::C { a: 10 }; | ^ not found in `namespaced_enums` - | help: possible candidate is found in another module, you can import it into scope | 14 | use namespaced_enums::Foo::C; diff --git a/src/test/ui/resolve/issue-14254.rs b/src/test/ui/resolve/issue-14254.rs index b1fc6c477207a..38444f69628d1 100644 --- a/src/test/ui/resolve/issue-14254.rs +++ b/src/test/ui/resolve/issue-14254.rs @@ -27,111 +27,92 @@ impl BarTy { impl Foo for *const BarTy { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` a; - //~^ ERROR unresolved value `a` - //~| NOTE no resolution found + //~^ ERROR cannot find value `a` + //~| NOTE not found in this scope } } impl<'a> Foo for &'a BarTy { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` x; - //~^ ERROR unresolved value `x` - //~| NOTE did you mean `self.x`? + //~^ ERROR cannot find value `x` y; - //~^ ERROR unresolved value `y` - //~| NOTE did you mean `self.y`? + //~^ ERROR cannot find value `y` a; - //~^ ERROR unresolved value `a` - //~| NOTE no resolution found + //~^ ERROR cannot find value `a` + //~| NOTE not found in this scope bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` b; - //~^ ERROR unresolved value `b` - //~| NOTE no resolution found + //~^ ERROR cannot find value `b` + //~| NOTE not found in this scope } } impl<'a> Foo for &'a mut BarTy { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` x; - //~^ ERROR unresolved value `x` - //~| NOTE did you mean `self.x`? + //~^ ERROR cannot find value `x` y; - //~^ ERROR unresolved value `y` - //~| NOTE did you mean `self.y`? + //~^ ERROR cannot find value `y` a; - //~^ ERROR unresolved value `a` - //~| NOTE no resolution found + //~^ ERROR cannot find value `a` + //~| NOTE not found in this scope bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` b; - //~^ ERROR unresolved value `b` - //~| NOTE no resolution found + //~^ ERROR cannot find value `b` + //~| NOTE not found in this scope } } impl Foo for Box { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` } } impl Foo for *const isize { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` } } impl<'a> Foo for &'a isize { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` } } impl<'a> Foo for &'a mut isize { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` } } impl Foo for Box { fn bar(&self) { baz(); - //~^ ERROR unresolved function `baz` - //~| NOTE did you mean `self.baz(...)`? + //~^ ERROR cannot find function `baz` bah; - //~^ ERROR unresolved value `bah` - //~| NOTE did you mean `Self::bah`? + //~^ ERROR cannot find value `bah` } } diff --git a/src/test/ui/resolve/issue-14254.stderr b/src/test/ui/resolve/issue-14254.stderr index 7aa0c2707b56f..a472fc861eb65 100644 --- a/src/test/ui/resolve/issue-14254.stderr +++ b/src/test/ui/resolve/issue-14254.stderr @@ -5,141 +5,141 @@ error[E0425]: cannot find function `baz` in this scope | ^^^ help: try: `self.baz` error[E0425]: cannot find value `a` in this scope - --> $DIR/issue-14254.rs:32:9 + --> $DIR/issue-14254.rs:31:9 | -32 | a; +31 | a; | ^ not found in this scope error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:40:9 + --> $DIR/issue-14254.rs:39:9 | -40 | baz(); +39 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `x` in this scope - --> $DIR/issue-14254.rs:43:9 + --> $DIR/issue-14254.rs:41:9 | -43 | x; +41 | x; | ^ help: try: `self.x` error[E0425]: cannot find value `y` in this scope - --> $DIR/issue-14254.rs:46:9 + --> $DIR/issue-14254.rs:43:9 | -46 | y; +43 | y; | ^ help: try: `self.y` error[E0425]: cannot find value `a` in this scope - --> $DIR/issue-14254.rs:49:9 + --> $DIR/issue-14254.rs:45:9 | -49 | a; +45 | a; | ^ not found in this scope error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:52:9 + --> $DIR/issue-14254.rs:48:9 | -52 | bah; +48 | bah; | ^^^ help: try: `Self::bah` error[E0425]: cannot find value `b` in this scope - --> $DIR/issue-14254.rs:55:9 + --> $DIR/issue-14254.rs:50:9 | -55 | b; +50 | b; | ^ not found in this scope error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:63:9 + --> $DIR/issue-14254.rs:58:9 | -63 | baz(); +58 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `x` in this scope - --> $DIR/issue-14254.rs:66:9 + --> $DIR/issue-14254.rs:60:9 | -66 | x; +60 | x; | ^ help: try: `self.x` error[E0425]: cannot find value `y` in this scope - --> $DIR/issue-14254.rs:69:9 + --> $DIR/issue-14254.rs:62:9 | -69 | y; +62 | y; | ^ help: try: `self.y` error[E0425]: cannot find value `a` in this scope - --> $DIR/issue-14254.rs:72:9 + --> $DIR/issue-14254.rs:64:9 | -72 | a; +64 | a; | ^ not found in this scope error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:75:9 + --> $DIR/issue-14254.rs:67:9 | -75 | bah; +67 | bah; | ^^^ help: try: `Self::bah` error[E0425]: cannot find value `b` in this scope - --> $DIR/issue-14254.rs:78:9 + --> $DIR/issue-14254.rs:69:9 | -78 | b; +69 | b; | ^ not found in this scope error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:86:9 + --> $DIR/issue-14254.rs:77:9 | -86 | baz(); +77 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:89:9 + --> $DIR/issue-14254.rs:79:9 | -89 | bah; +79 | bah; | ^^^ help: try: `Self::bah` error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:97:9 + --> $DIR/issue-14254.rs:86:9 | -97 | baz(); +86 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:100:9 - | -100 | bah; - | ^^^ help: try: `Self::bah` + --> $DIR/issue-14254.rs:88:9 + | +88 | bah; + | ^^^ help: try: `Self::bah` error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:108:9 - | -108 | baz(); - | ^^^ help: try: `self.baz` + --> $DIR/issue-14254.rs:95:9 + | +95 | baz(); + | ^^^ help: try: `self.baz` error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:111:9 - | -111 | bah; - | ^^^ help: try: `Self::bah` + --> $DIR/issue-14254.rs:97:9 + | +97 | bah; + | ^^^ help: try: `Self::bah` error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:119:9 + --> $DIR/issue-14254.rs:104:9 | -119 | baz(); +104 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:122:9 + --> $DIR/issue-14254.rs:106:9 | -122 | bah; +106 | bah; | ^^^ help: try: `Self::bah` error[E0425]: cannot find function `baz` in this scope - --> $DIR/issue-14254.rs:130:9 + --> $DIR/issue-14254.rs:113:9 | -130 | baz(); +113 | baz(); | ^^^ help: try: `self.baz` error[E0425]: cannot find value `bah` in this scope - --> $DIR/issue-14254.rs:133:9 + --> $DIR/issue-14254.rs:115:9 | -133 | bah; +115 | bah; | ^^^ help: try: `Self::bah` error[E0601]: main function not found diff --git a/src/test/ui/resolve/issue-16058.rs b/src/test/ui/resolve/issue-16058.rs index 1f777e53632dd..6d9df46eed416 100644 --- a/src/test/ui/resolve/issue-16058.rs +++ b/src/test/ui/resolve/issue-16058.rs @@ -18,10 +18,6 @@ impl GslResult { pub fn new() -> GslResult { Result { //~^ ERROR expected struct, variant or union type, found enum `Result` -//~| HELP possible better candidates are found in other modules, you can import them into scope -//~| HELP std::fmt::Result -//~| HELP std::io::Result -//~| HELP std::thread::Result val: 0f64, err: 0f64 } diff --git a/src/test/ui/resolve/issue-16058.stderr b/src/test/ui/resolve/issue-16058.stderr index 6d7406f891c50..322a1fea52eed 100644 --- a/src/test/ui/resolve/issue-16058.stderr +++ b/src/test/ui/resolve/issue-16058.stderr @@ -3,7 +3,6 @@ error[E0574]: expected struct, variant or union type, found enum `Result` | 19 | Result { | ^^^^^^ not a struct, variant or union type - | help: possible better candidates are found in other modules, you can import them into scope | 12 | use std::fmt::Result; diff --git a/src/test/ui/resolve/issue-17518.rs b/src/test/ui/resolve/issue-17518.rs index 3ac9b379d1892..295880c949988 100644 --- a/src/test/ui/resolve/issue-17518.rs +++ b/src/test/ui/resolve/issue-17518.rs @@ -9,10 +9,10 @@ // except according to those terms. enum SomeEnum { +//~^ HELP you can import it into scope E } fn main() { - E { name: "foobar" }; //~ ERROR unresolved struct, variant or union type `E` - //~^ HELP you can import it into scope: `use SomeEnum::E;` + E { name: "foobar" }; //~ ERROR cannot find struct, variant or union type `E` } diff --git a/src/test/ui/resolve/issue-17518.stderr b/src/test/ui/resolve/issue-17518.stderr index 2f94dbdc2c596..33f15267e4af4 100644 --- a/src/test/ui/resolve/issue-17518.stderr +++ b/src/test/ui/resolve/issue-17518.stderr @@ -1,9 +1,8 @@ error[E0422]: cannot find struct, variant or union type `E` in this scope - --> $DIR/issue-17518.rs:16:5 + --> $DIR/issue-17518.rs:17:5 | -16 | E { name: "foobar" }; //~ ERROR unresolved struct, variant or union type `E` +17 | E { name: "foobar" }; //~ ERROR cannot find struct, variant or union type `E` | ^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 11 | use SomeEnum::E; diff --git a/src/test/ui/resolve/issue-21221-1.rs b/src/test/ui/resolve/issue-21221-1.rs index b1266a5af3581..d3c18d4c80a1c 100644 --- a/src/test/ui/resolve/issue-21221-1.rs +++ b/src/test/ui/resolve/issue-21221-1.rs @@ -51,11 +51,7 @@ struct Foo; // help: `std::ops::Mul` impl Mul for Foo { -//~^ ERROR unresolved trait `Mul` -//~| HELP possible candidates are found in other modules, you can import them into scope -//~| HELP `mul1::Mul` -//~| HELP `mul2::Mul` -//~| HELP `std::ops::Mul` +//~^ ERROR cannot find trait `Mul` } // BEFORE, we got: @@ -70,24 +66,17 @@ impl Mul for Foo { // help: `mul4::Mul` // help: and 2 other candidates fn getMul() -> Mul { -//~^ ERROR unresolved type `Mul` -//~| HELP possible candidates are found in other modules, you can import them into scope -//~| HELP `mul1::Mul` -//~| HELP `mul2::Mul` -//~| HELP `mul3::Mul` -//~| HELP `mul4::Mul` -//~| HELP and 2 other candidates +//~^ ERROR cannot find type `Mul` } // Let's also test what happens if the trait doesn't exist: impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { -//~^ ERROR unresolved trait `ThisTraitReallyDoesntExistInAnyModuleReally` +//~^ ERROR cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` } // Let's also test what happens if there's just one alternative: impl Div for Foo { -//~^ ERROR unresolved trait `Div` -//~| HELP `use std::ops::Div;` +//~^ ERROR cannot find trait `Div` } fn main() { diff --git a/src/test/ui/resolve/issue-21221-1.stderr b/src/test/ui/resolve/issue-21221-1.stderr index ddaee451e90e8..88405fd841b06 100644 --- a/src/test/ui/resolve/issue-21221-1.stderr +++ b/src/test/ui/resolve/issue-21221-1.stderr @@ -3,7 +3,6 @@ error[E0405]: cannot find trait `Mul` in this scope | 53 | impl Mul for Foo { | ^^^ not found in this scope - | help: possible candidates are found in other modules, you can import them into scope | 11 | use mul1::Mul; @@ -14,11 +13,10 @@ help: possible candidates are found in other modules, you can import them into s | error[E0412]: cannot find type `Mul` in this scope - --> $DIR/issue-21221-1.rs:72:16 + --> $DIR/issue-21221-1.rs:68:16 | -72 | fn getMul() -> Mul { +68 | fn getMul() -> Mul { | ^^^ not found in this scope - | help: possible candidates are found in other modules, you can import them into scope | 11 | use mul1::Mul; @@ -32,17 +30,16 @@ help: possible candidates are found in other modules, you can import them into s and 2 other candidates error[E0405]: cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` in this scope - --> $DIR/issue-21221-1.rs:83:6 + --> $DIR/issue-21221-1.rs:73:6 | -83 | impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { +73 | impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope error[E0405]: cannot find trait `Div` in this scope - --> $DIR/issue-21221-1.rs:88:6 + --> $DIR/issue-21221-1.rs:78:6 | -88 | impl Div for Foo { +78 | impl Div for Foo { | ^^^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 11 | use std::ops::Div; diff --git a/src/test/ui/resolve/issue-21221-2.rs b/src/test/ui/resolve/issue-21221-2.rs index 15e859329c407..c0ebc57efb5c2 100644 --- a/src/test/ui/resolve/issue-21221-2.rs +++ b/src/test/ui/resolve/issue-21221-2.rs @@ -9,6 +9,7 @@ // except according to those terms. pub mod foo { +//~^ HELP you can import it into scope pub mod bar { // note: trait T is not public, but being in the current // crate, it's fine to show it, since the programmer can @@ -26,5 +27,4 @@ pub mod baz { struct Foo; impl T for Foo { } -//~^ ERROR unresolved trait `T` -//~| HELP you can import it into scope: `use foo::bar::T;` +//~^ ERROR cannot find trait `T` diff --git a/src/test/ui/resolve/issue-21221-2.stderr b/src/test/ui/resolve/issue-21221-2.stderr index a759116c6acff..ffe57c5099d68 100644 --- a/src/test/ui/resolve/issue-21221-2.stderr +++ b/src/test/ui/resolve/issue-21221-2.stderr @@ -1,9 +1,8 @@ error[E0405]: cannot find trait `T` in this scope - --> $DIR/issue-21221-2.rs:28:6 + --> $DIR/issue-21221-2.rs:29:6 | -28 | impl T for Foo { } +29 | impl T for Foo { } | ^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 11 | use foo::bar::T; diff --git a/src/test/ui/resolve/issue-21221-3.rs b/src/test/ui/resolve/issue-21221-3.rs index 5d62cb85914fd..046066b198639 100644 --- a/src/test/ui/resolve/issue-21221-3.rs +++ b/src/test/ui/resolve/issue-21221-3.rs @@ -16,6 +16,7 @@ extern crate issue_21221_3; struct Foo; +//~^ HELP possible candidate is found in another module // NOTE: This shows only traits accessible from the current // crate, thus the two private entities: @@ -23,8 +24,7 @@ struct Foo; // `issue_21221_3::outer::public_module::OuterTrait` // are hidden from the view. impl OuterTrait for Foo {} -//~^ ERROR unresolved trait `OuterTrait` -//~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;` +//~^ ERROR cannot find trait `OuterTrait` fn main() { println!("Hello, world!"); } diff --git a/src/test/ui/resolve/issue-21221-3.stderr b/src/test/ui/resolve/issue-21221-3.stderr index da849ecc71ab4..f134b86441400 100644 --- a/src/test/ui/resolve/issue-21221-3.stderr +++ b/src/test/ui/resolve/issue-21221-3.stderr @@ -1,9 +1,8 @@ error[E0405]: cannot find trait `OuterTrait` in this scope - --> $DIR/issue-21221-3.rs:25:6 + --> $DIR/issue-21221-3.rs:26:6 | -25 | impl OuterTrait for Foo {} +26 | impl OuterTrait for Foo {} | ^^^^^^^^^^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 18 | use issue_21221_3::outer::OuterTrait; diff --git a/src/test/ui/resolve/issue-21221-4.rs b/src/test/ui/resolve/issue-21221-4.rs index ff6698f8717bb..da8f2c6e778e5 100644 --- a/src/test/ui/resolve/issue-21221-4.rs +++ b/src/test/ui/resolve/issue-21221-4.rs @@ -16,10 +16,10 @@ extern crate issue_21221_4; struct Foo; +//~^ HELP possible candidate is found in another module impl T for Foo {} -//~^ ERROR unresolved trait `T` -//~| HELP you can import it into scope: `use issue_21221_4::T;` +//~^ ERROR cannot find trait `T` fn main() { println!("Hello, world!"); diff --git a/src/test/ui/resolve/issue-21221-4.stderr b/src/test/ui/resolve/issue-21221-4.stderr index 78059ed37bee8..0f3830bc2581d 100644 --- a/src/test/ui/resolve/issue-21221-4.stderr +++ b/src/test/ui/resolve/issue-21221-4.stderr @@ -1,9 +1,8 @@ error[E0405]: cannot find trait `T` in this scope - --> $DIR/issue-21221-4.rs:20:6 + --> $DIR/issue-21221-4.rs:21:6 | -20 | impl T for Foo {} +21 | impl T for Foo {} | ^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 18 | use issue_21221_4::T; diff --git a/src/test/ui/resolve/issue-23305.rs b/src/test/ui/resolve/issue-23305.rs index 19069f4916769..9f7b6ff5767c1 100644 --- a/src/test/ui/resolve/issue-23305.rs +++ b/src/test/ui/resolve/issue-23305.rs @@ -13,10 +13,9 @@ pub trait ToNbt { } impl ToNbt {} -//~^ ERROR unresolved type `Self` -//~| NOTE `Self` is only available in traits and impls -//~| ERROR the trait `ToNbt` cannot be made into an object -//~| NOTE the trait `ToNbt` cannot be made into an object -//~| NOTE method `new` has no receiver +//~^ ERROR unsupported cyclic reference +//~| NOTE cyclic reference +//~| NOTE the cycle begins when processing +//~| NOTE ...which then again requires fn main() {} diff --git a/src/test/ui/resolve/issue-23305.stderr b/src/test/ui/resolve/issue-23305.stderr index fda87de9b9c50..5bba9fc41e276 100644 --- a/src/test/ui/resolve/issue-23305.stderr +++ b/src/test/ui/resolve/issue-23305.stderr @@ -8,7 +8,7 @@ note: the cycle begins when processing ` $DIR/issue-23305.rs:15:1 | 15 | impl ToNbt {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: ...which then again requires processing ``, completing the cycle. error: aborting due to previous error diff --git a/src/test/ui/resolve/issue-2356.rs b/src/test/ui/resolve/issue-2356.rs index 6deb598b631f5..d0490ff981dca 100644 --- a/src/test/ui/resolve/issue-2356.rs +++ b/src/test/ui/resolve/issue-2356.rs @@ -25,24 +25,22 @@ impl MaybeDog { fn bark() { // If this provides a suggestion, it's a bug as MaybeDog doesn't impl Groom shave(); - //~^ ERROR unresolved function `shave` - //~| NOTE no resolution found + //~^ ERROR cannot find function `shave` + //~| NOTE not found in this scope } } impl Clone for cat { fn clone(&self) -> Self { clone(); - //~^ ERROR unresolved function `clone` - //~| NOTE did you mean `self.clone(...)`? + //~^ ERROR cannot find function `clone` loop {} } } impl Default for cat { fn default() -> Self { default(); - //~^ ERROR unresolved function `default` - //~| NOTE did you mean `Self::default`? + //~^ ERROR cannot find function `default` loop {} } } @@ -50,16 +48,13 @@ impl Default for cat { impl Groom for cat { fn shave(other: usize) { whiskers -= other; - //~^ ERROR unresolved value `whiskers` - //~| ERROR unresolved value `whiskers` - //~| NOTE did you mean `self.whiskers`? + //~^ ERROR cannot find value `whiskers` //~| NOTE `self` value is only available in methods with `self` parameter shave(4); - //~^ ERROR unresolved function `shave` - //~| NOTE did you mean `Self::shave`? + //~^ ERROR cannot find function `shave` purr(); - //~^ ERROR unresolved function `purr` - //~| NOTE no resolution found + //~^ ERROR cannot find function `purr` + //~| NOTE not found in this scope } } @@ -68,17 +63,17 @@ impl cat { fn purr_louder() { static_method(); - //~^ ERROR unresolved function `static_method` - //~| NOTE no resolution found + //~^ ERROR cannot find function `static_method` + //~| NOTE not found in this scope purr(); - //~^ ERROR unresolved function `purr` - //~| NOTE no resolution found + //~^ ERROR cannot find function `purr` + //~| NOTE not found in this scope purr(); - //~^ ERROR unresolved function `purr` - //~| NOTE no resolution found + //~^ ERROR cannot find function `purr` + //~| NOTE not found in this scope purr(); - //~^ ERROR unresolved function `purr` - //~| NOTE no resolution found + //~^ ERROR cannot find function `purr` + //~| NOTE not found in this scope } } @@ -93,28 +88,25 @@ impl cat { fn purr(&self) { grow_older(); - //~^ ERROR unresolved function `grow_older` - //~| NOTE no resolution found + //~^ ERROR cannot find function `grow_older` + //~| NOTE not found in this scope shave(); - //~^ ERROR unresolved function `shave` - //~| NOTE no resolution found + //~^ ERROR cannot find function `shave` + //~| NOTE not found in this scope } fn burn_whiskers(&mut self) { whiskers = 0; - //~^ ERROR unresolved value `whiskers` - //~| NOTE did you mean `self.whiskers`? + //~^ ERROR cannot find value `whiskers` } pub fn grow_older(other:usize) { whiskers = 4; - //~^ ERROR unresolved value `whiskers` - //~| ERROR unresolved value `whiskers` - //~| NOTE did you mean `self.whiskers`? + //~^ ERROR cannot find value `whiskers` //~| NOTE `self` value is only available in methods with `self` parameter purr_louder(); - //~^ ERROR unresolved function `purr_louder` - //~| NOTE no resolution found + //~^ ERROR cannot find function `purr_louder` + //~| NOTE not found in this scope } } diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr index ed0edd52587ec..e98d132b519a3 100644 --- a/src/test/ui/resolve/issue-2356.stderr +++ b/src/test/ui/resolve/issue-2356.stderr @@ -11,99 +11,99 @@ error[E0425]: cannot find function `clone` in this scope | ^^^^^ help: try: `self.clone` error[E0425]: cannot find function `default` in this scope - --> $DIR/issue-2356.rs:43:5 + --> $DIR/issue-2356.rs:42:5 | -43 | default(); +42 | default(); | ^^^^^^^ help: try: `Self::default` error[E0425]: cannot find value `whiskers` in this scope - --> $DIR/issue-2356.rs:52:5 + --> $DIR/issue-2356.rs:50:5 | -52 | whiskers -= other; +50 | whiskers -= other; | ^^^^^^^^ | | | `self` value is only available in methods with `self` parameter | help: try: `self.whiskers` error[E0425]: cannot find function `shave` in this scope - --> $DIR/issue-2356.rs:57:5 + --> $DIR/issue-2356.rs:53:5 | -57 | shave(4); +53 | shave(4); | ^^^^^ help: try: `Self::shave` error[E0425]: cannot find function `purr` in this scope - --> $DIR/issue-2356.rs:60:5 + --> $DIR/issue-2356.rs:55:5 | -60 | purr(); +55 | purr(); | ^^^^ not found in this scope error[E0425]: cannot find function `static_method` in this scope - --> $DIR/issue-2356.rs:70:9 + --> $DIR/issue-2356.rs:65:9 | -70 | static_method(); +65 | static_method(); | ^^^^^^^^^^^^^ not found in this scope error[E0425]: cannot find function `purr` in this scope - --> $DIR/issue-2356.rs:73:9 + --> $DIR/issue-2356.rs:68:9 | -73 | purr(); +68 | purr(); | ^^^^ not found in this scope error[E0425]: cannot find function `purr` in this scope - --> $DIR/issue-2356.rs:76:9 + --> $DIR/issue-2356.rs:71:9 | -76 | purr(); +71 | purr(); | ^^^^ not found in this scope error[E0425]: cannot find function `purr` in this scope - --> $DIR/issue-2356.rs:79:9 + --> $DIR/issue-2356.rs:74:9 | -79 | purr(); +74 | purr(); | ^^^^ not found in this scope error[E0424]: expected value, found module `self` - --> $DIR/issue-2356.rs:87:8 + --> $DIR/issue-2356.rs:82:8 | -87 | if self.whiskers > 3 { +82 | if self.whiskers > 3 { | ^^^^ `self` value is only available in methods with `self` parameter error[E0425]: cannot find function `grow_older` in this scope - --> $DIR/issue-2356.rs:95:5 + --> $DIR/issue-2356.rs:90:5 | -95 | grow_older(); +90 | grow_older(); | ^^^^^^^^^^ not found in this scope error[E0425]: cannot find function `shave` in this scope - --> $DIR/issue-2356.rs:98:5 + --> $DIR/issue-2356.rs:93:5 | -98 | shave(); +93 | shave(); | ^^^^^ not found in this scope error[E0425]: cannot find value `whiskers` in this scope - --> $DIR/issue-2356.rs:104:5 - | -104 | whiskers = 0; - | ^^^^^^^^ help: try: `self.whiskers` + --> $DIR/issue-2356.rs:99:5 + | +99 | whiskers = 0; + | ^^^^^^^^ help: try: `self.whiskers` error[E0425]: cannot find value `whiskers` in this scope - --> $DIR/issue-2356.rs:110:5 + --> $DIR/issue-2356.rs:104:5 | -110 | whiskers = 4; +104 | whiskers = 4; | ^^^^^^^^ | | | `self` value is only available in methods with `self` parameter | help: try: `self.whiskers` error[E0425]: cannot find function `purr_louder` in this scope - --> $DIR/issue-2356.rs:115:5 + --> $DIR/issue-2356.rs:107:5 | -115 | purr_louder(); +107 | purr_louder(); | ^^^^^^^^^^^ not found in this scope error[E0424]: expected value, found module `self` - --> $DIR/issue-2356.rs:122:5 + --> $DIR/issue-2356.rs:114:5 | -122 | self += 1; +114 | self += 1; | ^^^^ `self` value is only available in methods with `self` parameter error: aborting due to 17 previous errors diff --git a/src/test/ui/resolve/issue-24968.rs b/src/test/ui/resolve/issue-24968.rs index 0d562cab6b8b1..6065646401fcb 100644 --- a/src/test/ui/resolve/issue-24968.rs +++ b/src/test/ui/resolve/issue-24968.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo(_: Self) { -//~^ ERROR unresolved type `Self` +//~^ ERROR cannot find type `Self` //~| NOTE `Self` is only available in traits and impls } diff --git a/src/test/ui/resolve/issue-3907.stderr b/src/test/ui/resolve/issue-3907.stderr index 7a4d0ca698e6d..26ff7e70fd073 100644 --- a/src/test/ui/resolve/issue-3907.stderr +++ b/src/test/ui/resolve/issue-3907.stderr @@ -3,7 +3,6 @@ error[E0404]: expected trait, found type alias `Foo` | 20 | impl Foo for S { //~ ERROR expected trait, found type alias `Foo` | ^^^ type aliases cannot be used for traits - | help: possible better candidate is found in another module, you can import it into scope | 14 | use issue_3907::Foo; diff --git a/src/test/ui/resolve/issue-39226.rs b/src/test/ui/resolve/issue-39226.rs index f290a74861d18..f58f7cc3869d2 100644 --- a/src/test/ui/resolve/issue-39226.rs +++ b/src/test/ui/resolve/issue-39226.rs @@ -18,7 +18,8 @@ fn main() { let s: Something = Something { handle: Handle - //~^ ERROR cannot find value `Handle` in this scope - //~| NOTE did you mean `handle`? + //~^ ERROR expected value, found struct `Handle` + //~| NOTE did you mean `Handle { /* fields */ }`? + //~| NOTE did you mean `handle` }; } diff --git a/src/test/ui/resolve/issue-5035.rs b/src/test/ui/resolve/issue-5035.rs index 6263e6f6db44f..06a753cca8585 100644 --- a/src/test/ui/resolve/issue-5035.rs +++ b/src/test/ui/resolve/issue-5035.rs @@ -12,6 +12,7 @@ trait I {} type K = I; impl K for isize {} //~ ERROR expected trait, found type alias `K` //~| NOTE type aliases cannot be used for traits + //~| NOTE did you mean `I` use ImportError; //~ ERROR unresolved import `ImportError` [E0432] //~^ no `ImportError` in the root diff --git a/src/test/ui/resolve/issue-5035.stderr b/src/test/ui/resolve/issue-5035.stderr index 3c093e068c507..c9de39759b5ff 100644 --- a/src/test/ui/resolve/issue-5035.stderr +++ b/src/test/ui/resolve/issue-5035.stderr @@ -1,7 +1,7 @@ error[E0432]: unresolved import `ImportError` - --> $DIR/issue-5035.rs:16:5 + --> $DIR/issue-5035.rs:17:5 | -16 | use ImportError; //~ ERROR unresolved import `ImportError` [E0432] +17 | use ImportError; //~ ERROR unresolved import `ImportError` [E0432] | ^^^^^^^^^^^ no `ImportError` in the root error[E0404]: expected trait, found type alias `K` diff --git a/src/test/ui/resolve/levenshtein.rs b/src/test/ui/resolve/levenshtein.rs index 53b6372d8eb40..af27629385dcd 100644 --- a/src/test/ui/resolve/levenshtein.rs +++ b/src/test/ui/resolve/levenshtein.rs @@ -13,14 +13,18 @@ const MAX_ITEM: usize = 10; fn foo_bar() {} fn foo(c: esize) {} // Misspelled primitive type name. +//~^ ERROR cannot find enum Bar { } type A = Baz; // Misspelled type name. +//~^ ERROR cannot find type B = Opiton; // Misspelled type name from the prelude. +//~^ ERROR cannot find mod m { type A = Baz; // No suggestion here, Bar is not visible + //~^ ERROR cannot find pub struct First; pub struct Second; @@ -28,6 +32,10 @@ mod m { fn main() { let v = [0u32; MAXITEM]; // Misspelled constant name. + //~^ ERROR cannot find foobar(); // Misspelled function name. + //~^ ERROR cannot find let b: m::first = m::second; // Misspelled item in module. + //~^ ERROR cannot find + //~| ERROR cannot find } diff --git a/src/test/ui/resolve/levenshtein.stderr b/src/test/ui/resolve/levenshtein.stderr index 4dff2620319e4..68d46ccf6857d 100644 --- a/src/test/ui/resolve/levenshtein.stderr +++ b/src/test/ui/resolve/levenshtein.stderr @@ -5,45 +5,45 @@ error[E0412]: cannot find type `esize` in this scope | ^^^^^ did you mean `isize`? error[E0412]: cannot find type `Baz` in this scope - --> $DIR/levenshtein.rs:19:10 + --> $DIR/levenshtein.rs:20:10 | -19 | type A = Baz; // Misspelled type name. +20 | type A = Baz; // Misspelled type name. | ^^^ did you mean `Bar`? error[E0412]: cannot find type `Opiton` in this scope - --> $DIR/levenshtein.rs:20:10 + --> $DIR/levenshtein.rs:22:10 | -20 | type B = Opiton; // Misspelled type name from the prelude. +22 | type B = Opiton; // Misspelled type name from the prelude. | ^^^^^^ did you mean `Option`? error[E0412]: cannot find type `Baz` in this scope - --> $DIR/levenshtein.rs:23:14 + --> $DIR/levenshtein.rs:26:14 | -23 | type A = Baz; // No suggestion here, Bar is not visible +26 | type A = Baz; // No suggestion here, Bar is not visible | ^^^ not found in this scope error[E0425]: cannot find value `MAXITEM` in this scope - --> $DIR/levenshtein.rs:30:20 + --> $DIR/levenshtein.rs:34:20 | -30 | let v = [0u32; MAXITEM]; // Misspelled constant name. +34 | let v = [0u32; MAXITEM]; // Misspelled constant name. | ^^^^^^^ did you mean `MAX_ITEM`? error[E0425]: cannot find function `foobar` in this scope - --> $DIR/levenshtein.rs:31:5 + --> $DIR/levenshtein.rs:36:5 | -31 | foobar(); // Misspelled function name. +36 | foobar(); // Misspelled function name. | ^^^^^^ did you mean `foo_bar`? error[E0412]: cannot find type `first` in module `m` - --> $DIR/levenshtein.rs:32:15 + --> $DIR/levenshtein.rs:38:15 | -32 | let b: m::first = m::second; // Misspelled item in module. +38 | let b: m::first = m::second; // Misspelled item in module. | ^^^^^ did you mean `First`? error[E0425]: cannot find value `second` in module `m` - --> $DIR/levenshtein.rs:32:26 + --> $DIR/levenshtein.rs:38:26 | -32 | let b: m::first = m::second; // Misspelled item in module. +38 | let b: m::first = m::second; // Misspelled item in module. | ^^^^^^ did you mean `Second`? error: aborting due to 8 previous errors diff --git a/src/test/ui/resolve/name-clash-nullary.rs b/src/test/ui/resolve/name-clash-nullary.rs new file mode 100644 index 0000000000000..adf52c6d8e64d --- /dev/null +++ b/src/test/ui/resolve/name-clash-nullary.rs @@ -0,0 +1,13 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let None: isize = 42; //~ ERROR mismatched types +} diff --git a/src/test/ui/resolve/name-clash-nullary.stderr b/src/test/ui/resolve/name-clash-nullary.stderr new file mode 100644 index 0000000000000..014b1fe1b5b07 --- /dev/null +++ b/src/test/ui/resolve/name-clash-nullary.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/name-clash-nullary.rs:12:7 + | +12 | let None: isize = 42; //~ ERROR mismatched types + | ^^^^ expected isize, found enum `std::option::Option` + | + = note: expected type `isize` + found type `std::option::Option<_>` + +error: aborting due to previous error + diff --git a/src/test/ui/resolve/privacy-struct-ctor.rs b/src/test/ui/resolve/privacy-struct-ctor.rs index 87e7b4f42a1c0..fe3774af47ddb 100644 --- a/src/test/ui/resolve/privacy-struct-ctor.rs +++ b/src/test/ui/resolve/privacy-struct-ctor.rs @@ -25,7 +25,9 @@ mod m { n::Z; //~ ERROR tuple struct `Z` is private Z; //~^ ERROR expected value, found struct `Z` - //~| NOTE tuple struct constructors with private fields are invisible outside of their mod + //~| NOTE constructor is not visible here due to private fields + //~| NOTE did you mean `S` + //~| NOTE did you mean `Z { /* fields */ }` } } @@ -36,11 +38,13 @@ fn main() { S; //~^ ERROR expected value, found struct `S` //~| NOTE constructor is not visible here due to private fields + //~| NOTE did you mean `S { /* fields */ }` m::n::Z; //~ ERROR tuple struct `Z` is private xcrate::m::S; //~ ERROR tuple struct `S` is private xcrate::S; //~^ ERROR expected value, found struct `xcrate::S` + //~| NOTE did you mean `xcrate::S { /* fields */ }` //~| NOTE constructor is not visible here due to private fields xcrate::m::n::Z; //~ ERROR tuple struct `Z` is private } diff --git a/src/test/ui/resolve/privacy-struct-ctor.stderr b/src/test/ui/resolve/privacy-struct-ctor.stderr index f7e5c602644cf..81c52a1b7c37d 100644 --- a/src/test/ui/resolve/privacy-struct-ctor.stderr +++ b/src/test/ui/resolve/privacy-struct-ctor.stderr @@ -7,38 +7,35 @@ error[E0423]: expected value, found struct `Z` | did you mean `S`? | constructor is not visible here due to private fields | did you mean `Z { /* fields */ }`? - | help: possible better candidate is found in another module, you can import it into scope | 22 | use m::n::Z; | error[E0423]: expected value, found struct `S` - --> $DIR/privacy-struct-ctor.rs:36:5 + --> $DIR/privacy-struct-ctor.rs:38:5 | -36 | S; +38 | S; | ^ | | | constructor is not visible here due to private fields | did you mean `S { /* fields */ }`? - | help: possible better candidate is found in another module, you can import it into scope | -32 | use m::S; +34 | use m::S; | error[E0423]: expected value, found struct `xcrate::S` - --> $DIR/privacy-struct-ctor.rs:42:5 + --> $DIR/privacy-struct-ctor.rs:45:5 | -42 | xcrate::S; +45 | xcrate::S; | ^^^^^^^^^ | | | constructor is not visible here due to private fields | did you mean `xcrate::S { /* fields */ }`? - | help: possible better candidate is found in another module, you can import it into scope | -32 | use m::S; +34 | use m::S; | error[E0603]: tuple struct `Z` is private @@ -48,27 +45,27 @@ error[E0603]: tuple struct `Z` is private | ^^^^ error[E0603]: tuple struct `S` is private - --> $DIR/privacy-struct-ctor.rs:35:5 + --> $DIR/privacy-struct-ctor.rs:37:5 | -35 | m::S; //~ ERROR tuple struct `S` is private +37 | m::S; //~ ERROR tuple struct `S` is private | ^^^^ error[E0603]: tuple struct `Z` is private - --> $DIR/privacy-struct-ctor.rs:39:5 + --> $DIR/privacy-struct-ctor.rs:42:5 | -39 | m::n::Z; //~ ERROR tuple struct `Z` is private +42 | m::n::Z; //~ ERROR tuple struct `Z` is private | ^^^^^^^ error[E0603]: tuple struct `S` is private - --> $DIR/privacy-struct-ctor.rs:41:5 + --> $DIR/privacy-struct-ctor.rs:44:5 | -41 | xcrate::m::S; //~ ERROR tuple struct `S` is private +44 | xcrate::m::S; //~ ERROR tuple struct `S` is private | ^^^^^^^^^^^^ error[E0603]: tuple struct `Z` is private - --> $DIR/privacy-struct-ctor.rs:45:5 + --> $DIR/privacy-struct-ctor.rs:49:5 | -45 | xcrate::m::n::Z; //~ ERROR tuple struct `Z` is private +49 | xcrate::m::n::Z; //~ ERROR tuple struct `Z` is private | ^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.rs b/src/test/ui/resolve/resolve-assoc-suggestions.rs index 53e26ddafec37..62d2dc7a8faeb 100644 --- a/src/test/ui/resolve/resolve-assoc-suggestions.rs +++ b/src/test/ui/resolve/resolve-assoc-suggestions.rs @@ -24,34 +24,31 @@ impl Tr for S { fn method(&self) { let _: field; - //~^ ERROR unresolved type `field` - //~| NOTE no resolution found + //~^ ERROR cannot find type `field` + //~| NOTE not found in this scope let field(..); - //~^ ERROR unresolved tuple struct/variant `field` - //~| NOTE no resolution found + //~^ ERROR cannot find tuple struct/variant `field` + //~| NOTE not found in this scope field; - //~^ ERROR unresolved value `field` - //~| NOTE did you mean `self.field`? + //~^ ERROR cannot find value `field` let _: Type; - //~^ ERROR unresolved type `Type` - //~| NOTE did you mean `Self::Type`? + //~^ ERROR cannot find type `Type` let Type(..); - //~^ ERROR unresolved tuple struct/variant `Type` - //~| NOTE no resolution found + //~^ ERROR cannot find tuple struct/variant `Type` + //~| NOTE not found in this scope Type; - //~^ ERROR unresolved value `Type` - //~| NOTE no resolution found + //~^ ERROR cannot find value `Type` + //~| NOTE not found in this scope let _: method; - //~^ ERROR unresolved type `method` - //~| NOTE no resolution found + //~^ ERROR cannot find type `method` + //~| NOTE not found in this scope let method(..); - //~^ ERROR unresolved tuple struct/variant `method` - //~| NOTE no resolution found + //~^ ERROR cannot find tuple struct/variant `method` + //~| NOTE not found in this scope method; - //~^ ERROR unresolved value `method` - //~| NOTE did you mean `self.method(...)`? + //~^ ERROR cannot find value `method` } } diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.stderr b/src/test/ui/resolve/resolve-assoc-suggestions.stderr index 77aa545e2ad6b..4bb3947a02811 100644 --- a/src/test/ui/resolve/resolve-assoc-suggestions.stderr +++ b/src/test/ui/resolve/resolve-assoc-suggestions.stderr @@ -17,39 +17,39 @@ error[E0425]: cannot find value `field` in this scope | ^^^^^ help: try: `self.field` error[E0412]: cannot find type `Type` in this scope - --> $DIR/resolve-assoc-suggestions.rs:36:16 + --> $DIR/resolve-assoc-suggestions.rs:35:16 | -36 | let _: Type; +35 | let _: Type; | ^^^^ help: try: `Self::Type` error[E0531]: cannot find tuple struct/variant `Type` in this scope - --> $DIR/resolve-assoc-suggestions.rs:39:13 + --> $DIR/resolve-assoc-suggestions.rs:37:13 | -39 | let Type(..); +37 | let Type(..); | ^^^^ not found in this scope error[E0425]: cannot find value `Type` in this scope - --> $DIR/resolve-assoc-suggestions.rs:42:9 + --> $DIR/resolve-assoc-suggestions.rs:40:9 | -42 | Type; +40 | Type; | ^^^^ not found in this scope error[E0412]: cannot find type `method` in this scope - --> $DIR/resolve-assoc-suggestions.rs:46:16 + --> $DIR/resolve-assoc-suggestions.rs:44:16 | -46 | let _: method; +44 | let _: method; | ^^^^^^ not found in this scope error[E0531]: cannot find tuple struct/variant `method` in this scope - --> $DIR/resolve-assoc-suggestions.rs:49:13 + --> $DIR/resolve-assoc-suggestions.rs:47:13 | -49 | let method(..); +47 | let method(..); | ^^^^^^ not found in this scope error[E0425]: cannot find value `method` in this scope - --> $DIR/resolve-assoc-suggestions.rs:52:9 + --> $DIR/resolve-assoc-suggestions.rs:50:9 | -52 | method; +50 | method; | ^^^^^^ help: try: `self.method` error: aborting due to 9 previous errors diff --git a/src/test/ui/resolve/resolve-speculative-adjustment.rs b/src/test/ui/resolve/resolve-speculative-adjustment.rs index 95289e23f9e81..120237b662df8 100644 --- a/src/test/ui/resolve/resolve-speculative-adjustment.rs +++ b/src/test/ui/resolve/resolve-speculative-adjustment.rs @@ -25,19 +25,17 @@ impl Tr for S { // Speculative resolution of `Self` and `self` silently fails, // "did you mean" messages are not printed. field; - //~^ ERROR unresolved value `field` - //~| NOTE no resolution found + //~^ ERROR cannot find value `field` + //~| NOTE not found in this scope method(); - //~^ ERROR unresolved function `method` - //~| NOTE no resolution found + //~^ ERROR cannot find function `method` + //~| NOTE not found in this scope } field; - //~^ ERROR unresolved value `field` - //~| NOTE did you mean `self.field`? + //~^ ERROR cannot find value `field` method(); - //~^ ERROR unresolved function `method` - //~| NOTE did you mean `self.method(...)`? + //~^ ERROR cannot find function `method` } } diff --git a/src/test/ui/resolve/resolve-speculative-adjustment.stderr b/src/test/ui/resolve/resolve-speculative-adjustment.stderr index 3e1b075679a50..2d74e427ea04e 100644 --- a/src/test/ui/resolve/resolve-speculative-adjustment.stderr +++ b/src/test/ui/resolve/resolve-speculative-adjustment.stderr @@ -17,9 +17,9 @@ error[E0425]: cannot find value `field` in this scope | ^^^^^ help: try: `self.field` error[E0425]: cannot find function `method` in this scope - --> $DIR/resolve-speculative-adjustment.rs:38:9 + --> $DIR/resolve-speculative-adjustment.rs:37:9 | -38 | method(); +37 | method(); | ^^^^^^ help: try: `self.method` error: aborting due to 4 previous errors diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs index 789bdfb414db4..70d072a388b90 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs @@ -45,6 +45,7 @@ fn h4() -> i32 { a::b.J //~^ ERROR expected value, found module `a::b` //~| NOTE did you mean `a::b::J`? + //~| NOTE did you mean `I` } fn h5() { @@ -54,23 +55,24 @@ fn h5() { let v = Vec::new(); v.push(a::b); //~^ ERROR expected value, found module `a::b` - //~| NOTE not a value + //~| NOTE did you mean `I` } fn h6() -> i32 { a::b.f() //~^ ERROR expected value, found module `a::b` //~| NOTE did you mean `a::b::f(...)`? + //~| NOTE did you mean `I` } fn h7() { a::b //~^ ERROR expected value, found module `a::b` - //~| NOTE not a value + //~| NOTE did you mean `I` } fn h8() -> i32 { a::b() //~^ ERROR expected function, found module `a::b` - //~| NOTE not a function + //~| NOTE did you mean `I` } diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr index d1794d19f6a53..fd5de16bdd1da 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -32,42 +32,42 @@ error[E0423]: expected value, found module `a::b` | did you mean `a::b::J`? error[E0423]: expected value, found module `a` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:51:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:52:5 | -51 | a.b.f(); +52 | a.b.f(); | ^-- | | | did you mean `a::b`? error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:55:12 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:56:12 | -55 | v.push(a::b); +56 | v.push(a::b); | ^^^- | | | did you mean `I`? error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:61:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:62:5 | -61 | a::b.f() +62 | a::b.f() | ^^^----- | | | | | did you mean `I`? | did you mean `a::b::f(...)`? error[E0423]: expected value, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:67:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:69:5 | -67 | a::b +69 | a::b | ^^^- | | | did you mean `I`? error[E0423]: expected function, found module `a::b` - --> $DIR/suggest-path-instead-of-mod-dot-item.rs:73:5 + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:75:5 | -73 | a::b() +75 | a::b() | ^^^- | | | did you mean `I`? diff --git a/src/test/ui/resolve/token-error-correct-2.rs b/src/test/ui/resolve/token-error-correct-2.rs index 6fa1260d18041..121a565b2b1eb 100644 --- a/src/test/ui/resolve/token-error-correct-2.rs +++ b/src/test/ui/resolve/token-error-correct-2.rs @@ -13,7 +13,7 @@ fn main() { if foo { //~^ NOTE: unclosed delimiter - //~| ERROR: unresolved value `foo` - //~| NOTE: no resolution found + //~| ERROR: cannot find value `foo` + //~| NOTE: not found in this scope ) //~ ERROR: incorrect close delimiter: `)` } diff --git a/src/test/ui/resolve/token-error-correct-3.rs b/src/test/ui/resolve/token-error-correct-3.rs index f72b7adf593a9..746eee9ecd74e 100644 --- a/src/test/ui/resolve/token-error-correct-3.rs +++ b/src/test/ui/resolve/token-error-correct-3.rs @@ -18,16 +18,19 @@ pub mod raw { pub fn ensure_dir_exists, F: FnOnce(&Path)>(path: P, callback: F) -> io::Result { - if !is_directory(path.as_ref()) { //~ ERROR: unresolved function `is_directory` - //~^ NOTE: no resolution found + if !is_directory(path.as_ref()) { //~ ERROR: cannot find function `is_directory` + //~^ NOTE: not found in this scope callback(path.as_ref(); //~ NOTE: unclosed delimiter - //~^ ERROR: expected one of + //~^ NOTE: expected one of + //~| ERROR expected one of fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types //~^ expected (), found enum `std::result::Result` //~| expected type `()` //~| found type `std::result::Result` + //~| expected one of } else { //~ ERROR: incorrect close delimiter: `}` //~^ ERROR: expected one of + //~| unexpected token Ok(false); } diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr index c8e19db3707e1..b500a349f6cf6 100644 --- a/src/test/ui/resolve/token-error-correct-3.stderr +++ b/src/test/ui/resolve/token-error-correct-3.stderr @@ -1,7 +1,7 @@ error: incorrect close delimiter: `}` - --> $DIR/token-error-correct-3.rs:29:9 + --> $DIR/token-error-correct-3.rs:31:9 | -29 | } else { //~ ERROR: incorrect close delimiter: `}` +31 | } else { //~ ERROR: incorrect close delimiter: `}` | ^ | note: unclosed delimiter @@ -17,24 +17,24 @@ error: expected one of `,`, `.`, `?`, or an operator, found `;` | ^ expected one of `,`, `.`, `?`, or an operator here error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` - --> $DIR/token-error-correct-3.rs:29:9 + --> $DIR/token-error-correct-3.rs:31:9 | -25 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types +26 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types | - expected one of `.`, `;`, `?`, `}`, or an operator here ... -29 | } else { //~ ERROR: incorrect close delimiter: `}` +31 | } else { //~ ERROR: incorrect close delimiter: `}` | ^ unexpected token error[E0425]: cannot find function `is_directory` in this scope --> $DIR/token-error-correct-3.rs:21:13 | -21 | if !is_directory(path.as_ref()) { //~ ERROR: unresolved function `is_directory` +21 | if !is_directory(path.as_ref()) { //~ ERROR: cannot find function `is_directory` | ^^^^^^^^^^^^ not found in this scope error[E0308]: mismatched types - --> $DIR/token-error-correct-3.rs:25:13 + --> $DIR/token-error-correct-3.rs:26:13 | -25 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types +26 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try adding a semicolon: `;` | | | expected (), found enum `std::result::Result` diff --git a/src/test/ui/resolve/token-error-correct.rs b/src/test/ui/resolve/token-error-correct.rs index 5fd35e51336f3..0c7fe0df1c706 100644 --- a/src/test/ui/resolve/token-error-correct.rs +++ b/src/test/ui/resolve/token-error-correct.rs @@ -15,11 +15,6 @@ fn main() { //~^ NOTE: unclosed delimiter //~| NOTE: unclosed delimiter //~| ERROR: expected expression, found `;` - //~| ERROR: unresolved function `foo` - //~| NOTE: no resolution found - //~| ERROR: unresolved function `bar` - //~| NOTE: no resolution found - //~| ERROR: expected one of `)`, `,`, `.`, `<`, `?` } //~^ ERROR: incorrect close delimiter: `}` //~| ERROR: incorrect close delimiter: `}` diff --git a/src/test/ui/resolve/token-error-correct.stderr b/src/test/ui/resolve/token-error-correct.stderr index 281c21f6f85ee..cad58b30df206 100644 --- a/src/test/ui/resolve/token-error-correct.stderr +++ b/src/test/ui/resolve/token-error-correct.stderr @@ -1,7 +1,7 @@ error: incorrect close delimiter: `}` - --> $DIR/token-error-correct.rs:23:1 + --> $DIR/token-error-correct.rs:18:1 | -23 | } +18 | } | ^ | note: unclosed delimiter @@ -11,9 +11,9 @@ note: unclosed delimiter | ^ error: incorrect close delimiter: `}` - --> $DIR/token-error-correct.rs:23:1 + --> $DIR/token-error-correct.rs:18:1 | -23 | } +18 | } | ^ | note: unclosed delimiter @@ -28,29 +28,11 @@ error: expected expression, found `;` 14 | foo(bar(; | ^ -error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, `yield`, or an operator, found `;` - --> $DIR/token-error-correct.rs:14:13 - | -14 | foo(bar(; - | ^ expected one of 19 possible tokens here - error: expected expression, found `)` - --> $DIR/token-error-correct.rs:23:1 + --> $DIR/token-error-correct.rs:18:1 | -23 | } +18 | } | ^ -error[E0425]: cannot find function `foo` in this scope - --> $DIR/token-error-correct.rs:14:5 - | -14 | foo(bar(; - | ^^^ not found in this scope - -error[E0425]: cannot find function `bar` in this scope - --> $DIR/token-error-correct.rs:14:9 - | -14 | foo(bar(; - | ^^^ not found in this scope - -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/resolve/tuple-struct-alias.rs b/src/test/ui/resolve/tuple-struct-alias.rs index c9c05202fea2b..0dbca07b771d7 100644 --- a/src/test/ui/resolve/tuple-struct-alias.rs +++ b/src/test/ui/resolve/tuple-struct-alias.rs @@ -13,16 +13,16 @@ type A = S; impl S { fn f() { - let s = Self(0, 1); + let s = Self(0, 1); //~ ERROR expected function match s { - Self(..) => {} + Self(..) => {} //~ ERROR expected tuple struct/variant } } } fn main() { - let s = A(0, 1); + let s = A(0, 1); //~ ERROR expected function match s { - A(..) => {} + A(..) => {} //~ ERROR expected tuple struct/variant } } diff --git a/src/test/ui/resolve/tuple-struct-alias.stderr b/src/test/ui/resolve/tuple-struct-alias.stderr index e2ef8f0e568fc..aea9fc356bf2e 100644 --- a/src/test/ui/resolve/tuple-struct-alias.stderr +++ b/src/test/ui/resolve/tuple-struct-alias.stderr @@ -1,19 +1,19 @@ error[E0423]: expected function, found self type `Self` --> $DIR/tuple-struct-alias.rs:16:17 | -16 | let s = Self(0, 1); +16 | let s = Self(0, 1); //~ ERROR expected function | ^^^^ did you mean `Self { /* fields */ }`? error[E0532]: expected tuple struct/variant, found self type `Self` --> $DIR/tuple-struct-alias.rs:18:13 | -18 | Self(..) => {} +18 | Self(..) => {} //~ ERROR expected tuple struct/variant | ^^^^ did you mean `Self { /* fields */ }`? error[E0423]: expected function, found type alias `A` --> $DIR/tuple-struct-alias.rs:24:13 | -24 | let s = A(0, 1); +24 | let s = A(0, 1); //~ ERROR expected function | ^ | | | did you mean `S`? @@ -22,7 +22,7 @@ error[E0423]: expected function, found type alias `A` error[E0532]: expected tuple struct/variant, found type alias `A` --> $DIR/tuple-struct-alias.rs:26:9 | -26 | A(..) => {} +26 | A(..) => {} //~ ERROR expected tuple struct/variant | ^ | | | did you mean `S`? diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs index 57f6ddd2d3c6c..ee4c40f2c8d4d 100644 --- a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs @@ -9,8 +9,8 @@ // except according to those terms. fn f isize>(x: F) {} -//~^ ERROR unresolved trait `Nonexist` -//~| NOTE no resolution found +//~^ ERROR cannot find trait `Nonexist` +//~| NOTE not found in this scope type Typedef = isize; diff --git a/src/test/ui/resolve/unresolved_static_type_field.rs b/src/test/ui/resolve/unresolved_static_type_field.rs index 19beabd88232e..711e46b1248b7 100644 --- a/src/test/ui/resolve/unresolved_static_type_field.rs +++ b/src/test/ui/resolve/unresolved_static_type_field.rs @@ -17,9 +17,7 @@ struct Foo { impl Foo { fn bar() { f(cx); - //~^ ERROR unresolved value `cx` - //~| ERROR unresolved value `cx` - //~| NOTE did you mean `self.cx`? + //~^ ERROR cannot find value `cx` in this scope //~| NOTE `self` value is only available in methods with `self` parameter } } diff --git a/src/test/ui/resolve/use_suggestion_placement.rs b/src/test/ui/resolve/use_suggestion_placement.rs index a43b8fc99df5f..87f38df0442cf 100644 --- a/src/test/ui/resolve/use_suggestion_placement.rs +++ b/src/test/ui/resolve/use_suggestion_placement.rs @@ -22,15 +22,15 @@ mod foo { // test whether the use suggestion isn't // placed into the expansion of `#[derive(Debug)] - type Bar = Path; + type Bar = Path; //~ ERROR cannot find } fn main() { y!(); - let _ = A; + let _ = A; //~ ERROR cannot find foo(); } fn foo() { - type Dict = HashMap; + type Dict = HashMap; //~ ERROR cannot find } diff --git a/src/test/ui/resolve/use_suggestion_placement.stderr b/src/test/ui/resolve/use_suggestion_placement.stderr index 8a4dfdc80276a..1cc2d06ab6843 100644 --- a/src/test/ui/resolve/use_suggestion_placement.stderr +++ b/src/test/ui/resolve/use_suggestion_placement.stderr @@ -1,9 +1,8 @@ error[E0412]: cannot find type `Path` in this scope --> $DIR/use_suggestion_placement.rs:25:16 | -25 | type Bar = Path; +25 | type Bar = Path; //~ ERROR cannot find | ^^^^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 21 | use std::path::Path; @@ -12,9 +11,8 @@ help: possible candidate is found in another module, you can import it into scop error[E0425]: cannot find value `A` in this scope --> $DIR/use_suggestion_placement.rs:30:13 | -30 | let _ = A; +30 | let _ = A; //~ ERROR cannot find | ^ not found in this scope - | help: possible candidate is found in another module, you can import it into scope | 11 | use m::A; @@ -23,9 +21,8 @@ help: possible candidate is found in another module, you can import it into scop error[E0412]: cannot find type `HashMap` in this scope --> $DIR/use_suggestion_placement.rs:35:23 | -35 | type Dict = HashMap; +35 | type Dict = HashMap; //~ ERROR cannot find | ^^^^^^^ not found in this scope - | help: possible candidates are found in other modules, you can import them into scope | 11 | use std::collections::HashMap; @@ -33,17 +30,5 @@ help: possible candidates are found in other modules, you can import them into s 11 | use std::collections::hash_map::HashMap; | -error[E0091]: type parameter `K` is unused - --> $DIR/use_suggestion_placement.rs:35:15 - | -35 | type Dict = HashMap; - | ^ unused type parameter - -error[E0091]: type parameter `V` is unused - --> $DIR/use_suggestion_placement.rs:35:18 - | -35 | type Dict = HashMap; - | ^ unused type parameter - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/rfc-2005-default-binding-mode/const.rs b/src/test/ui/rfc-2005-default-binding-mode/const.rs new file mode 100644 index 0000000000000..fca99f064a273 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/const.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME(tschottdorf): this test should pass. + +#![feature(match_default_bindings)] + +#[derive(PartialEq, Eq)] +struct Foo { + bar: i32, +} + +const FOO: Foo = Foo{bar: 5}; + +fn main() { + let f = Foo{bar:6}; + + match &f { + FOO => {}, //~ ERROR mismatched types + _ => panic!(), + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/const.stderr b/src/test/ui/rfc-2005-default-binding-mode/const.stderr new file mode 100644 index 0000000000000..afcbf76c1a44f --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/const.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/const.rs:26:9 + | +26 | FOO => {}, //~ ERROR mismatched types + | ^^^ expected &Foo, found struct `Foo` + | + = note: expected type `&Foo` + found type `Foo` + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/enum.rs b/src/test/ui/rfc-2005-default-binding-mode/enum.rs new file mode 100644 index 0000000000000..76ea64e248ef8 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/enum.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + *x += 1; //~ ERROR cannot assign to immutable + + + if let Some(x) = &Some(3) { + *x += 1; //~ ERROR cannot assign to immutable + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + *x += 1; //~ ERROR cannot assign to immutable + break; + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/enum.stderr b/src/test/ui/rfc-2005-default-binding-mode/enum.stderr new file mode 100644 index 0000000000000..052ab5892d254 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/enum.stderr @@ -0,0 +1,26 @@ +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:21:5 + | +20 | let Wrap(x) = &Wrap(3); + | - consider changing this to `x` +21 | *x += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:25:9 + | +24 | if let Some(x) = &Some(3) { + | - consider changing this to `x` +25 | *x += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:31:9 + | +30 | while let Some(x) = &Some(3) { + | - consider changing this to `x` +31 | *x += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs new file mode 100644 index 0000000000000..2e43d9722a900 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +// Verify the binding mode shifts - only when no `&` are auto-dereferenced is the +// final default binding mode mutable. + +fn main() { + match &&Some(5i32) { + Some(n) => { + *n += 1; //~ ERROR cannot assign to immutable + let _ = n; + } + None => {}, + }; + + match &mut &Some(5i32) { + Some(n) => { + *n += 1; //~ ERROR cannot assign to immutable + let _ = n; + } + None => {}, + }; + + match &&mut Some(5i32) { + Some(n) => { + *n += 1; //~ ERROR cannot assign to immutable + let _ = n; + } + None => {}, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr new file mode 100644 index 0000000000000..c1c59fe678525 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr @@ -0,0 +1,26 @@ +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:19:13 + | +18 | Some(n) => { + | - consider changing this to `n` +19 | *n += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:27:13 + | +26 | Some(n) => { + | - consider changing this to `n` +27 | *n += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:35:13 + | +34 | Some(n) => { + | - consider changing this to `n` +35 | *n += 1; //~ ERROR cannot assign to immutable + | ^^^^^^^ cannot borrow as mutable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/for.rs b/src/test/ui/rfc-2005-default-binding-mode/for.rs new file mode 100644 index 0000000000000..e9004c13a0e27 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/for.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +struct Foo {} + +pub fn main() { + let mut tups = vec![(Foo{}, Foo{})]; + // The below desugars to &(ref n, mut m). + for (n, mut m) in &tups { + //~^ ERROR cannot bind by-move and by-ref in the same pattern + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/for.stderr b/src/test/ui/rfc-2005-default-binding-mode/for.stderr new file mode 100644 index 0000000000000..795dffb722a15 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/for.stderr @@ -0,0 +1,10 @@ +error[E0009]: cannot bind by-move and by-ref in the same pattern + --> $DIR/for.rs:18:13 + | +18 | for (n, mut m) in &tups { + | - ^^^^^ by-move pattern here + | | + | both by-ref and by-move used + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs new file mode 100644 index 0000000000000..9fbcf5d68b6ff --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +// FIXME(tschottdorf): This should compile. See #44912. + +pub fn main() { + let x = &Some((3, 3)); + let _: &i32 = match x { + Some((x, 3)) | &Some((ref x, 5)) => x, + //~^ ERROR is bound in inconsistent ways + _ => &5i32, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr new file mode 100644 index 0000000000000..7430dc2c87f95 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr @@ -0,0 +1,8 @@ +error[E0409]: variable `x` is bound in inconsistent ways within the same match arm + --> $DIR/issue-44912-or.rs:18:35 + | +18 | Some((x, 3)) | &Some((ref x, 5)) => x, + | - first binding ^ bound in different ways + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/lit.rs b/src/test/ui/rfc-2005-default-binding-mode/lit.rs new file mode 100644 index 0000000000000..783287fd458bc --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/lit.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(match_default_bindings)] + +// FIXME(tschottdorf): we want these to compile, but they don't. + +fn with_str() { + let s: &'static str = "abc"; + + match &s { + "abc" => true, //~ ERROR mismatched types + _ => panic!(), + }; +} + +fn with_bytes() { + let s: &'static [u8] = b"abc"; + + match &s { + b"abc" => true, //~ ERROR mismatched types + _ => panic!(), + }; +} + +pub fn main() { + with_str(); + with_bytes(); +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/lit.stderr b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr new file mode 100644 index 0000000000000..f5ed7ee7181dd --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/lit.rs:19:13 + | +19 | "abc" => true, //~ ERROR mismatched types + | ^^^^^ expected &str, found str + | + = note: expected type `&&str` + found type `&'static str` + +error[E0308]: mismatched types + --> $DIR/lit.rs:28:9 + | +28 | b"abc" => true, //~ ERROR mismatched types + | ^^^^^^ expected &[u8], found array of 3 elements + | + = note: expected type `&&[u8]` + found type `&'static [u8; 3]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs new file mode 100644 index 0000000000000..0b2318d7621d6 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Without caching type lookups in FnCtxt.resolve_ty_and_def_ufcs +// the error below would be reported twice (once when checking +// for a non-ref pattern, once when processing the pattern). + +fn main() { + let foo = 22; + match foo { + u32::XXX => { } //~ ERROR no associated item named + _ => { } + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr new file mode 100644 index 0000000000000..830422875886b --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr @@ -0,0 +1,8 @@ +error[E0599]: no associated item named `XXX` found for type `u32` in the current scope + --> $DIR/no-double-error.rs:18:9 + | +18 | u32::XXX => { } //~ ERROR no associated item named + | ^^^^^^^^ associated item not found in `u32` + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.rs b/src/test/ui/rfc-2005-default-binding-mode/slice.rs new file mode 100644 index 0000000000000..40aa957242cb8 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(slice_patterns)] +#![feature(match_default_bindings)] + +pub fn main() { + let sl: &[u8] = b"foo"; + + match sl { //~ ERROR non-exhaustive patterns + [first, remainder..] => {}, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.stderr b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr new file mode 100644 index 0000000000000..ec2225c9f9236 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr @@ -0,0 +1,8 @@ +error[E0004]: non-exhaustive patterns: `&[]` not covered + --> $DIR/slice.rs:17:11 + | +17 | match sl { //~ ERROR non-exhaustive patterns + | ^^ pattern `&[]` not covered + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/suggestion.rs b/src/test/ui/rfc-2005-default-binding-mode/suggestion.rs new file mode 100644 index 0000000000000..b9b974ff3c521 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/suggestion.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + if let Some(y) = &Some(22) { //~ ERROR non-reference pattern + println!("{}", y); + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/suggestion.stderr b/src/test/ui/rfc-2005-default-binding-mode/suggestion.stderr new file mode 100644 index 0000000000000..ebf9e498ffd9e --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/suggestion.stderr @@ -0,0 +1,10 @@ +error: non-reference pattern used to match a reference (see issue #42640) + --> $DIR/suggestion.rs:12:12 + | +12 | if let Some(y) = &Some(22) { //~ ERROR non-reference pattern + | ^^^^^^^ help: consider using a reference: `&Some(y)` + | + = help: add #![feature(match_default_bindings)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs new file mode 100644 index 0000000000000..87a0b33e63b5e --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.rs @@ -0,0 +1,28 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): "undeclared lifetime" errors will be addressed in a follow-up PR + +trait Foo { + type Bar<'a, 'b>; +} + +trait Baz { + type Quux<'a>; +} + +impl Baz for T where T: Foo { + type Quux<'a> = ::Bar<'a, 'static>; + //~^ ERROR undeclared lifetime +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr new file mode 100644 index 0000000000000..3c3c5d1262781 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/construct_with_other_type.stderr @@ -0,0 +1,8 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/construct_with_other_type.rs:24:37 + | +24 | type Quux<'a> = ::Bar<'a, 'static>; + | ^^ undeclared lifetime + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs b/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs new file mode 100644 index 0000000000000..b12c075d13291 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +trait Foo { + type Bar<,>; + //~^ ERROR expected one of `>`, identifier, or lifetime, found `,` +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr b/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr new file mode 100644 index 0000000000000..de0c1e310bcb8 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr @@ -0,0 +1,8 @@ +error: expected one of `>`, identifier, or lifetime, found `,` + --> $DIR/empty_generics.rs:14:14 + | +14 | type Bar<,>; + | ^ expected one of `>`, identifier, or lifetime here + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.rs b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.rs new file mode 100644 index 0000000000000..eec061bc96ba4 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.rs @@ -0,0 +1,39 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +// Checking the interaction with this other feature +#![feature(associated_type_defaults)] + +//FIXME(#44265): "undeclared lifetime" errors will be addressed in a follow-up PR + +use std::fmt::{Display, Debug}; + +trait Foo { + type Assoc where Self: Sized; + type Assoc2 where T: Display; + type Assoc3; + type WithDefault where T: Debug = Iterator; + type NoGenerics; +} + +struct Bar; + +impl Foo for Bar { + type Assoc = usize; + type Assoc2 = Vec; + type Assoc3 where T: Iterator = Vec; + type WithDefault<'a, T> = &'a Iterator; + //~^ ERROR undeclared lifetime + type NoGenerics = ::std::cell::Cell; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr new file mode 100644 index 0000000000000..e65da028b23b5 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.stderr @@ -0,0 +1,8 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/generic-associated-types-where.rs:34:32 + | +34 | type WithDefault<'a, T> = &'a Iterator; + | ^^ undeclared lifetime + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.rs b/src/test/ui/rfc1598-generic-associated-types/iterable.rs new file mode 100644 index 0000000000000..0019c4be5e8e0 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.rs @@ -0,0 +1,23 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): "undeclared lifetime" errors will be addressed in a follow-up PR + +trait Iterable { + type Item<'a>; + type Iter<'a>: Iterator>; + //~^ ERROR undeclared lifetime + + fn iter<'a>(&'a self) -> Self::Iter<'a>; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/iterable.stderr b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr new file mode 100644 index 0000000000000..0e565047afe63 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/iterable.stderr @@ -0,0 +1,8 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/iterable.rs:17:47 + | +17 | type Iter<'a>: Iterator>; + | ^^ undeclared lifetime + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs new file mode 100644 index 0000000000000..0e598fa14b198 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs @@ -0,0 +1,20 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only +// must-compile-successfully + +#![feature(generic_associated_types)] + +impl Baz for T where T: Foo { + type Quux<'a> = ::Bar<'a, 'static>; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs new file mode 100644 index 0000000000000..8ab519be630d4 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs @@ -0,0 +1,33 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only +// must-compile-successfully + +#![feature(generic_associated_types)] + +use std::ops::Deref; + +trait Foo { + type Bar<'a>; + type Bar<'a, 'b>; + type Bar<'a, 'b,>; + type Bar<'a, 'b, T>; + type Bar<'a, 'b, T, U>; + type Bar<'a, 'b, T, U,>; + type Bar<'a, 'b, T: Debug, U,>; + type Bar<'a, 'b, T: Debug, U,>: Debug; + type Bar<'a, 'b, T: Debug, U,>: Deref + Into; + type Bar<'a, 'b, T: Debug, U,> where T: Deref, U: Into; + type Bar<'a, 'b, T: Debug, U,>: Deref + Into + where T: Deref, U: Into; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/pointer_family.rs b/src/test/ui/rfc1598-generic-associated-types/pointer_family.rs new file mode 100644 index 0000000000000..cbeeb1d6ca7b2 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/pointer_family.rs @@ -0,0 +1,50 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): "type parameter not allowed" errors will be addressed in a follow-up PR + +use std::rc::Rc; +use std::sync::Arc; +use std::ops::Deref; + +trait PointerFamily { + type Pointer: Deref; + fn new(value: T) -> Self::Pointer; + //~^ ERROR type parameters are not allowed on this type [E0109] +} + +struct ArcFamily; + +impl PointerFamily for ArcFamily { + type Pointer = Arc; + fn new(value: T) -> Self::Pointer { + //~^ ERROR type parameters are not allowed on this type [E0109] + Arc::new(value) + } +} + +struct RcFamily; + +impl PointerFamily for RcFamily { + type Pointer = Rc; + fn new(value: T) -> Self::Pointer { + //~^ ERROR type parameters are not allowed on this type [E0109] + Rc::new(value) + } +} + +struct Foo { + bar: P::Pointer, + //~^ ERROR type parameters are not allowed on this type [E0109] +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/pointer_family.stderr b/src/test/ui/rfc1598-generic-associated-types/pointer_family.stderr new file mode 100644 index 0000000000000..cc7f06f3b86d5 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/pointer_family.stderr @@ -0,0 +1,26 @@ +error[E0109]: type parameters are not allowed on this type + --> $DIR/pointer_family.rs:46:21 + | +46 | bar: P::Pointer, + | ^^^^^^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/pointer_family.rs:21:42 + | +21 | fn new(value: T) -> Self::Pointer; + | ^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/pointer_family.rs:29:42 + | +29 | fn new(value: T) -> Self::Pointer { + | ^ type parameter not allowed + +error[E0109]: type parameters are not allowed on this type + --> $DIR/pointer_family.rs:39:42 + | +39 | fn new(value: T) -> Self::Pointer { + | ^ type parameter not allowed + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs new file mode 100644 index 0000000000000..f9e270ee92e22 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs @@ -0,0 +1,38 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +//FIXME(#44265): "lifetime parameter not allowed on this type" errors will be addressed in a +// follow-up PR + +use std::fmt::Display; + +trait StreamingIterator { + type Item<'a>; + // Applying the lifetime parameter `'a` to `Self::Item` inside the trait. + fn next<'a>(&'a self) -> Option>; + //~^ ERROR lifetime parameters are not allowed on this type [E0110] +} + +struct Foo { + // Applying a concrete lifetime to the constructor outside the trait. + bar: ::Item<'static>, + //~^ ERROR lifetime parameters are not allowed on this type [E0110] +} + +// Users can bound parameters by the type constructed by that trait's associated type constructor +// of a trait using HRTB. Both type equality bounds and trait bounds of this kind are valid: +//FIXME(sunjay): This next line should parse and be valid +//fn foo StreamingIterator=&'a [i32]>>(iter: T) { /* ... */ } +fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /* ... */ } +//~^ ERROR lifetime parameters are not allowed on this type [E0110] + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr new file mode 100644 index 0000000000000..b1908d022ed06 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.stderr @@ -0,0 +1,20 @@ +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:27:41 + | +27 | bar: ::Item<'static>, + | ^^^^^^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:35:64 + | +35 | fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /* ... */ } + | ^^ lifetime parameter not allowed on this type + +error[E0110]: lifetime parameters are not allowed on this type + --> $DIR/streaming_iterator.rs:21:48 + | +21 | fn next<'a>(&'a self) -> Option>; + | ^^ lifetime parameter not allowed on this type + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs index 3741ba4f3ae7a..b24b2d0fb2461 100644 --- a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs +++ b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs @@ -56,21 +56,22 @@ fn need_to_use_this_value() -> bool { } fn main() { - need_to_use_this_value(); + need_to_use_this_value(); //~ WARN unused return value let mut m = MyStruct { n: 2 }; let n = MyStruct { n: 3 }; - m.need_to_use_this_method_value(); + m.need_to_use_this_method_value(); //~ WARN unused return value m.is_even(); // trait method! + //~^ WARN unused return value m.replace(3); // won't warn (annotation needs to be in trait definition) // comparison methods are `must_use` - 2.eq(&3); - m.eq(&n); + 2.eq(&3); //~ WARN unused return value + m.eq(&n); //~ WARN unused return value // lint includes comparison operators - 2 == 3; - m == n; + 2 == 3; //~ WARN unused comparison + m == n; //~ WARN unused comparison } diff --git a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr index fdd0a591bc78d..4778f2e6d1b23 100644 --- a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr +++ b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr @@ -1,7 +1,7 @@ warning: unused return value of `need_to_use_this_value` which must be used: it's important --> $DIR/fn_must_use.rs:59:5 | -59 | need_to_use_this_value(); +59 | need_to_use_this_value(); //~ WARN unused return value | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -13,7 +13,7 @@ note: lint level defined here warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used --> $DIR/fn_must_use.rs:64:5 | -64 | m.need_to_use_this_method_value(); +64 | m.need_to_use_this_method_value(); //~ WARN unused return value | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused return value of `EvenNature::is_even` which must be used: no side effects @@ -23,26 +23,26 @@ warning: unused return value of `EvenNature::is_even` which must be used: no sid | ^^^^^^^^^^^^ warning: unused return value of `std::cmp::PartialEq::eq` which must be used - --> $DIR/fn_must_use.rs:70:5 + --> $DIR/fn_must_use.rs:71:5 | -70 | 2.eq(&3); +71 | 2.eq(&3); //~ WARN unused return value | ^^^^^^^^^ warning: unused return value of `std::cmp::PartialEq::eq` which must be used - --> $DIR/fn_must_use.rs:71:5 + --> $DIR/fn_must_use.rs:72:5 | -71 | m.eq(&n); +72 | m.eq(&n); //~ WARN unused return value | ^^^^^^^^^ warning: unused comparison which must be used - --> $DIR/fn_must_use.rs:74:5 + --> $DIR/fn_must_use.rs:75:5 | -74 | 2 == 3; +75 | 2 == 3; //~ WARN unused comparison | ^^^^^^ warning: unused comparison which must be used - --> $DIR/fn_must_use.rs:75:5 + --> $DIR/fn_must_use.rs:76:5 | -75 | m == n; +76 | m == n; //~ WARN unused comparison | ^^^^^^ diff --git a/src/test/ui/short-error-format.rs b/src/test/ui/short-error-format.rs new file mode 100644 index 0000000000000..ecce824ca178b --- /dev/null +++ b/src/test/ui/short-error-format.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --error-format=short -Zunstable-options + +fn foo(_: u32) {} + +fn main() { + foo("Bonjour".to_owned()); + let x = 0u32; + x.salut(); +} diff --git a/src/test/ui/short-error-format.stderr b/src/test/ui/short-error-format.stderr new file mode 100644 index 0000000000000..debe60b463226 --- /dev/null +++ b/src/test/ui/short-error-format.stderr @@ -0,0 +1,3 @@ +$DIR/short-error-format.rs:16:9 - error[E0308]: mismatched types +$DIR/short-error-format.rs:18:7 - error[E0599]: no method named `salut` found for type `u32` in the current scope +error: aborting due to 2 previous errors diff --git a/src/test/ui/similar-tokens.rs b/src/test/ui/similar-tokens.rs new file mode 100644 index 0000000000000..eb7eab9e42dd7 --- /dev/null +++ b/src/test/ui/similar-tokens.rs @@ -0,0 +1,19 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod x { + pub struct A; + pub struct B; +} + +// `.` is similar to `,` so list parsing should continue to closing `}` +use x::{A. B}; //~ ERROR expected one of `,`, `::`, or `as`, found `.` + +fn main() {} diff --git a/src/test/ui/similar-tokens.stderr b/src/test/ui/similar-tokens.stderr new file mode 100644 index 0000000000000..b4968b1018ff4 --- /dev/null +++ b/src/test/ui/similar-tokens.stderr @@ -0,0 +1,8 @@ +error: expected one of `,`, `::`, or `as`, found `.` + --> $DIR/similar-tokens.rs:17:10 + | +17 | use x::{A. B}; //~ ERROR expected one of `,`, `::`, or `as`, found `.` + | ^ expected one of `,`, `::`, or `as` here + +error: aborting due to previous error + diff --git a/src/test/ui/span/E0072.rs b/src/test/ui/span/E0072.rs index 18ade4f1ab68e..554dfc619d7b7 100644 --- a/src/test/ui/span/E0072.rs +++ b/src/test/ui/span/E0072.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct ListNode { +struct ListNode { //~ ERROR has infinite size head: u8, tail: Option, } diff --git a/src/test/ui/span/E0072.stderr b/src/test/ui/span/E0072.stderr index 1f6dd6b1d165f..82b8579d5a12c 100644 --- a/src/test/ui/span/E0072.stderr +++ b/src/test/ui/span/E0072.stderr @@ -1,7 +1,7 @@ error[E0072]: recursive type `ListNode` has infinite size --> $DIR/E0072.rs:11:1 | -11 | struct ListNode { +11 | struct ListNode { //~ ERROR has infinite size | ^^^^^^^^^^^^^^^ recursive type has infinite size 12 | head: u8, 13 | tail: Option, diff --git a/src/test/ui/span/E0204.rs b/src/test/ui/span/E0204.rs index 9fb37607c7da7..0ad6b67330d1a 100644 --- a/src/test/ui/span/E0204.rs +++ b/src/test/ui/span/E0204.rs @@ -12,9 +12,9 @@ struct Foo { foo: Vec, } -impl Copy for Foo { } +impl Copy for Foo { } //~ ERROR may not be implemented for this type -#[derive(Copy)] +#[derive(Copy)] //~ ERROR may not be implemented for this type struct Foo2<'a> { ty: &'a mut bool, } @@ -24,9 +24,9 @@ enum EFoo { Baz, } -impl Copy for EFoo { } +impl Copy for EFoo { } //~ ERROR may not be implemented for this type -#[derive(Copy)] +#[derive(Copy)] //~ ERROR may not be implemented for this type enum EFoo2<'a> { Bar(&'a mut bool), Baz, diff --git a/src/test/ui/span/E0204.stderr b/src/test/ui/span/E0204.stderr index 4fe6afaca8ec6..0d9617c4c7324 100644 --- a/src/test/ui/span/E0204.stderr +++ b/src/test/ui/span/E0204.stderr @@ -4,13 +4,13 @@ error[E0204]: the trait `Copy` may not be implemented for this type 12 | foo: Vec, | ------------- this field does not implement `Copy` ... -15 | impl Copy for Foo { } +15 | impl Copy for Foo { } //~ ERROR may not be implemented for this type | ^^^^ error[E0204]: the trait `Copy` may not be implemented for this type --> $DIR/E0204.rs:17:10 | -17 | #[derive(Copy)] +17 | #[derive(Copy)] //~ ERROR may not be implemented for this type | ^^^^ 18 | struct Foo2<'a> { 19 | ty: &'a mut bool, @@ -22,13 +22,13 @@ error[E0204]: the trait `Copy` may not be implemented for this type 23 | Bar { x: Vec }, | ----------- this field does not implement `Copy` ... -27 | impl Copy for EFoo { } +27 | impl Copy for EFoo { } //~ ERROR may not be implemented for this type | ^^^^ error[E0204]: the trait `Copy` may not be implemented for this type --> $DIR/E0204.rs:29:10 | -29 | #[derive(Copy)] +29 | #[derive(Copy)] //~ ERROR may not be implemented for this type | ^^^^ 30 | enum EFoo2<'a> { 31 | Bar(&'a mut bool), diff --git a/src/test/ui/span/E0493.rs b/src/test/ui/span/E0493.rs index 7915564cafb0a..de81068e2683d 100644 --- a/src/test/ui/span/E0493.rs +++ b/src/test/ui/span/E0493.rs @@ -25,6 +25,7 @@ impl Drop for Bar { } const F : Foo = (Foo { a : 0 }, Foo { a : 1 }).1; +//~^ destructors cannot be evaluated at compile-time fn main() { } diff --git a/src/test/ui/span/E0535.rs b/src/test/ui/span/E0535.rs index 17558cc05c612..f9219436c780a 100644 --- a/src/test/ui/span/E0535.rs +++ b/src/test/ui/span/E0535.rs @@ -11,4 +11,6 @@ #[inline(unknown)] //~ ERROR E0535 pub fn something() {} -fn main() {} +fn main() { + something(); +} diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs index 29fea052b06c2..1c45771ff8a3c 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.rs @@ -24,6 +24,7 @@ fn call(mut f: F) where F: FnMut(Fn) { //~| NOTE first mutable borrow occurs here //~| NOTE second mutable borrow occurs here f((Box::new(|| {}))) + //~^ NOTE borrow occurs due to use of `f` in closure })); //~^ NOTE first borrow ends here } @@ -66,7 +67,7 @@ fn test6() { fn test7() { fn foo(_: F) where F: FnMut(Box, isize) {} let mut f = |g: Box, b: isize| {}; - //~^ NOTE moved + //~^ NOTE captured outer variable f(Box::new(|a| { //~^ NOTE borrow of `f` occurs here foo(f); diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 6e7d0c17f1dfe..0a1429d5509b0 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -8,43 +8,44 @@ error[E0499]: cannot borrow `f` as mutable more than once at a time ... 26 | f((Box::new(|| {}))) | - borrow occurs due to use of `f` in closure -27 | })); +27 | //~^ NOTE borrow occurs due to use of `f` in closure +28 | })); | - first borrow ends here error[E0596]: cannot borrow immutable borrowed content `*f` as mutable - --> $DIR/borrowck-call-is-borrow-issue-12224.rs:39:5 + --> $DIR/borrowck-call-is-borrow-issue-12224.rs:40:5 | -37 | fn test2(f: &F) where F: FnMut() { +38 | fn test2(f: &F) where F: FnMut() { | -- use `&mut F` here to make mutable -38 | //~^ NOTE use `&mut F` here to make mutable -39 | (*f)(); +39 | //~^ NOTE use `&mut F` here to make mutable +40 | (*f)(); | ^^^^ cannot borrow as mutable error[E0596]: cannot borrow immutable `Box` content `*f.f` as mutable - --> $DIR/borrowck-call-is-borrow-issue-12224.rs:50:5 + --> $DIR/borrowck-call-is-borrow-issue-12224.rs:51:5 | -48 | fn test4(f: &Test) { +49 | fn test4(f: &Test) { | ----- use `&mut Test` here to make mutable -49 | //~^ NOTE use `&mut Test` here to make mutable -50 | f.f.call_mut(()) +50 | //~^ NOTE use `&mut Test` here to make mutable +51 | f.f.call_mut(()) | ^^^ cannot borrow as mutable error[E0504]: cannot move `f` into closure because it is borrowed - --> $DIR/borrowck-call-is-borrow-issue-12224.rs:72:13 + --> $DIR/borrowck-call-is-borrow-issue-12224.rs:73:13 | -70 | f(Box::new(|a| { +71 | f(Box::new(|a| { | - borrow of `f` occurs here -71 | //~^ NOTE borrow of `f` occurs here -72 | foo(f); +72 | //~^ NOTE borrow of `f` occurs here +73 | foo(f); | ^ move into closure occurs here error[E0507]: cannot move out of captured outer variable in an `FnMut` closure - --> $DIR/borrowck-call-is-borrow-issue-12224.rs:72:13 + --> $DIR/borrowck-call-is-borrow-issue-12224.rs:73:13 | -68 | let mut f = |g: Box, b: isize| {}; +69 | let mut f = |g: Box, b: isize| {}; | ----- captured outer variable ... -72 | foo(f); +73 | foo(f); | ^ cannot move out of captured outer variable in an `FnMut` closure error: aborting due to 5 previous errors diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 9e316b989a475..b31ba324b0cda 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -26,7 +26,7 @@ fn f() { v3.push(&id('x')); // statement 6 //~^ ERROR borrowed value does not live long enough //~| NOTE temporary value created here - //~| NOTE temporary value only lives until here + //~| NOTE temporary value dropped here while still borrowed //~| NOTE consider using a `let` binding to increase its lifetime { @@ -36,7 +36,7 @@ fn f() { v4.push(&id('y')); //~^ ERROR borrowed value does not live long enough //~| NOTE temporary value created here - //~| NOTE temporary value only lives until here + //~| NOTE temporary value dropped here while still borrowed //~| NOTE consider using a `let` binding to increase its lifetime } // (statement 7) @@ -47,7 +47,7 @@ fn f() { v5.push(&id('z')); //~^ ERROR borrowed value does not live long enough //~| NOTE temporary value created here - //~| NOTE temporary value only lives until here + //~| NOTE temporary value dropped here while still borrowed //~| NOTE consider using a `let` binding to increase its lifetime v1.push(&old[0]); diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.rs b/src/test/ui/span/borrowck-ref-into-rvalue.rs index 726d4bcdf1d08..a059232daca38 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.rs +++ b/src/test/ui/span/borrowck-ref-into-rvalue.rs @@ -11,10 +11,10 @@ fn main() { let msg; match Some("Hello".to_string()) { - Some(ref m) => { //~ ERROR borrowed value does not live long enough + Some(ref m) => { msg = m; }, None => { panic!() } - } + } //~ ERROR borrowed value does not live long enough println!("{}", *msg); } diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.stderr index ced1f762af4a4..91f9cddd58985 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.stderr @@ -1,10 +1,10 @@ error[E0597]: borrowed value does not live long enough --> $DIR/borrowck-ref-into-rvalue.rs:18:5 | -14 | Some(ref m) => { //~ ERROR borrowed value does not live long enough +14 | Some(ref m) => { | ----- borrow occurs here ... -18 | } +18 | } //~ ERROR borrowed value does not live long enough | ^ borrowed value dropped here while still borrowed 19 | println!("{}", *msg); 20 | } diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs index 32d80069f0087..0a3e18043fe45 100644 --- a/src/test/ui/span/coerce-suggestions.rs +++ b/src/test/ui/span/coerce-suggestions.rs @@ -18,37 +18,26 @@ fn main() { //~^ ERROR E0308 //~| NOTE expected usize, found struct `std::string::String` //~| NOTE expected type `usize` - //~| NOTE found type `std::string::String` //~| HELP here are some functions which might fulfill your needs: let x: &str = String::new(); //~^ ERROR E0308 //~| NOTE expected &str, found struct `std::string::String` //~| NOTE expected type `&str` - //~| NOTE found type `std::string::String` - //~| HELP try with `&String::new()` + //~| HELP consider borrowing here let y = String::new(); test(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability //~| NOTE expected type `&mut std::string::String` - //~| NOTE found type `&std::string::String` test2(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability //~| NOTE expected type `&mut i32` - //~| NOTE found type `&std::string::String` let f; f = box f; //~^ ERROR E0308 //~| NOTE cyclic type of infinite size - //~| NOTE expected type `_` - //~| NOTE found type `Box<_>` let s = &mut String::new(); s = format!("foo"); - //~^ ERROR E0308 - //~| NOTE expected mutable reference, found struct `std::string::String` - //~| NOTE expected type `&mut std::string::String` - //~| HELP try with `&mut format!("foo")` - //~| NOTE this error originates in a macro outside of the current crate } diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index b703632bf90c1..604b38bef6cc4 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -11,52 +11,53 @@ error[E0308]: mismatched types - .len() error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:23:19 + --> $DIR/coerce-suggestions.rs:22:19 | -23 | let x: &str = String::new(); - | ^^^^^^^^^^^^^ expected &str, found struct `std::string::String` +22 | let x: &str = String::new(); + | ^^^^^^^^^^^^^ + | | + | expected &str, found struct `std::string::String` + | help: consider borrowing here: `&String::new()` | = note: expected type `&str` found type `std::string::String` - = help: try with `&String::new()` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:30:10 + --> $DIR/coerce-suggestions.rs:28:10 | -30 | test(&y); +28 | test(&y); | ^^ types differ in mutability | = note: expected type `&mut std::string::String` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:35:11 + --> $DIR/coerce-suggestions.rs:32:11 | -35 | test2(&y); +32 | test2(&y); | ^^ types differ in mutability | = note: expected type `&mut i32` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:41:9 + --> $DIR/coerce-suggestions.rs:37:9 | -41 | f = box f; +37 | f = box f; | ^^^^^ cyclic type of infinite size - | - = note: expected type `_` - found type `std::boxed::Box<_>` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:48:9 + --> $DIR/coerce-suggestions.rs:42:9 | -48 | s = format!("foo"); - | ^^^^^^^^^^^^^^ expected mutable reference, found struct `std::string::String` +42 | s = format!("foo"); + | ^^^^^^^^^^^^^^ + | | + | expected mutable reference, found struct `std::string::String` + | help: consider mutably borrowing here: `&mut format!("foo")` | = note: expected type `&mut std::string::String` found type `std::string::String` - = help: try with `&mut format!("foo")` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/src/test/ui/span/destructor-restrictions.rs b/src/test/ui/span/destructor-restrictions.rs index 22f615cafd71e..7c80867856d3b 100644 --- a/src/test/ui/span/destructor-restrictions.rs +++ b/src/test/ui/span/destructor-restrictions.rs @@ -15,7 +15,7 @@ use std::cell::RefCell; fn main() { let b = { let a = Box::new(RefCell::new(4)); - *a.borrow() + 1 //~ ERROR `*a` does not live long enough - }; + *a.borrow() + 1 + }; //~ ERROR `*a` does not live long enough println!("{}", b); } diff --git a/src/test/ui/span/destructor-restrictions.stderr b/src/test/ui/span/destructor-restrictions.stderr index ee885454169cb..e6d24d7c13599 100644 --- a/src/test/ui/span/destructor-restrictions.stderr +++ b/src/test/ui/span/destructor-restrictions.stderr @@ -1,9 +1,9 @@ error[E0597]: `*a` does not live long enough --> $DIR/destructor-restrictions.rs:19:5 | -18 | *a.borrow() + 1 //~ ERROR `*a` does not live long enough +18 | *a.borrow() + 1 | - borrow occurs here -19 | }; +19 | }; //~ ERROR `*a` does not live long enough | ^- borrowed value needs to live until here | | | `*a` dropped here while still borrowed diff --git a/src/test/ui/span/dropck_arr_cycle_checked.rs b/src/test/ui/span/dropck_arr_cycle_checked.rs index 995a198271c11..455c9dc57f52b 100644 --- a/src/test/ui/span/dropck_arr_cycle_checked.rs +++ b/src/test/ui/span/dropck_arr_cycle_checked.rs @@ -13,7 +13,7 @@ // // (Compare against compile-fail/dropck_vec_cycle_checked.rs) -#![feature(const_atomic_usize_new)] + use std::cell::Cell; use id::Id; diff --git a/src/test/ui/span/dropck_vec_cycle_checked.rs b/src/test/ui/span/dropck_vec_cycle_checked.rs index 5bcaa71f73c29..5e7cb79680c1e 100644 --- a/src/test/ui/span/dropck_vec_cycle_checked.rs +++ b/src/test/ui/span/dropck_vec_cycle_checked.rs @@ -12,7 +12,7 @@ // // (Compare against compile-fail/dropck_arr_cycle_checked.rs) -#![feature(const_atomic_usize_new)] + use std::cell::Cell; use id::Id; diff --git a/src/test/ui/span/gated-features-attr-spans.rs b/src/test/ui/span/gated-features-attr-spans.rs index d5ccd2ea7ad3f..ace185d01694a 100644 --- a/src/test/ui/span/gated-features-attr-spans.rs +++ b/src/test/ui/span/gated-features-attr-spans.rs @@ -10,14 +10,14 @@ #![feature(attr_literals)] -#[repr(align(16))] +#[repr(align(16))] //~ ERROR is experimental struct Gem { mohs_hardness: u8, poofed: bool, weapon: Weapon, } -#[repr(simd)] +#[repr(simd)] //~ ERROR are experimental struct Weapon { name: String, damage: u32 @@ -25,9 +25,10 @@ struct Weapon { impl Gem { #[must_use] fn summon_weapon(&self) -> Weapon { self.weapon } + //~^ WARN is experimental } -#[must_use] +#[must_use] //~ WARN is experimental fn bubble(gem: Gem) -> Result { if gem.poofed { Ok(gem) diff --git a/src/test/ui/span/gated-features-attr-spans.stderr b/src/test/ui/span/gated-features-attr-spans.stderr index 66b2567f728a3..d067470942e33 100644 --- a/src/test/ui/span/gated-features-attr-spans.stderr +++ b/src/test/ui/span/gated-features-attr-spans.stderr @@ -1,7 +1,7 @@ error: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33626) --> $DIR/gated-features-attr-spans.rs:13:1 | -13 | #[repr(align(16))] +13 | #[repr(align(16))] //~ ERROR is experimental | ^^^^^^^^^^^^^^^^^^ | = help: add #![feature(repr_align)] to the crate attributes to enable @@ -9,7 +9,7 @@ error: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33 error: SIMD types are experimental and possibly buggy (see issue #27731) --> $DIR/gated-features-attr-spans.rs:20:1 | -20 | #[repr(simd)] +20 | #[repr(simd)] //~ ERROR are experimental | ^^^^^^^^^^^^^ | = help: add #![feature(repr_simd)] to the crate attributes to enable @@ -23,9 +23,9 @@ warning: `#[must_use]` on methods is experimental (see issue #43302) = help: add #![feature(fn_must_use)] to the crate attributes to enable warning: `#[must_use]` on functions is experimental (see issue #43302) - --> $DIR/gated-features-attr-spans.rs:30:1 + --> $DIR/gated-features-attr-spans.rs:31:1 | -30 | #[must_use] +31 | #[must_use] //~ WARN is experimental | ^^^^^^^^^^^ | = help: add #![feature(fn_must_use)] to the crate attributes to enable diff --git a/src/test/ui/span/impl-wrong-item-for-trait.rs b/src/test/ui/span/impl-wrong-item-for-trait.rs index 091df1d5dc898..d4aafabed3791 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.rs +++ b/src/test/ui/span/impl-wrong-item-for-trait.rs @@ -13,7 +13,13 @@ use std::fmt::Debug; trait Foo { fn bar(&self); + //~^ NOTE item in trait + //~| NOTE `bar` from trait + //~| NOTE item in trait + //~| NOTE `bar` from trait const MY_CONST: u32; + //~^ NOTE item in trait + //~| NOTE `MY_CONST` from trait } pub struct FooConstForMethod; @@ -46,10 +52,15 @@ impl Foo for FooTypeForMethod { type bar = u64; //~^ ERROR E0325 //~| NOTE does not match trait + //~| NOTE not a member + //~| ERROR E0437 const MY_CONST: u32 = 1; } impl Debug for FooTypeForMethod { } +//~^^ ERROR E0046 +//~| NOTE missing `fmt` in implementation +//~| NOTE `fmt` from trait: fn main () {} diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index 5812cab0d050a..dfca435f2a073 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -1,86 +1,86 @@ error[E0437]: type `bar` is not a member of trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:46:5 + --> $DIR/impl-wrong-item-for-trait.rs:52:5 | -46 | type bar = u64; +52 | type bar = u64; | ^^^^^^^^^^^^^^^ not a member of trait `Foo` error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:24:5 + --> $DIR/impl-wrong-item-for-trait.rs:30:5 | 15 | fn bar(&self); | -------------- item in trait ... -24 | const bar: u64 = 1; +30 | const bar: u64 = 1; | ^^^^^^^^^^^^^^^^^^^ does not match trait error[E0046]: not all trait items implemented, missing: `bar` - --> $DIR/impl-wrong-item-for-trait.rs:21:1 + --> $DIR/impl-wrong-item-for-trait.rs:27:1 | 15 | fn bar(&self); | -------------- `bar` from trait ... -21 | / impl Foo for FooConstForMethod { -22 | | //~^ ERROR E0046 -23 | | //~| NOTE missing `bar` in implementation -24 | | const bar: u64 = 1; +27 | / impl Foo for FooConstForMethod { +28 | | //~^ ERROR E0046 +29 | | //~| NOTE missing `bar` in implementation +30 | | const bar: u64 = 1; ... | -27 | | const MY_CONST: u32 = 1; -28 | | } +33 | | const MY_CONST: u32 = 1; +34 | | } | |_^ missing `bar` in implementation error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:36:5 + --> $DIR/impl-wrong-item-for-trait.rs:42:5 | -16 | const MY_CONST: u32; +20 | const MY_CONST: u32; | -------------------- item in trait ... -36 | fn MY_CONST() {} +42 | fn MY_CONST() {} | ^^^^^^^^^^^^^^^^ does not match trait error[E0046]: not all trait items implemented, missing: `MY_CONST` - --> $DIR/impl-wrong-item-for-trait.rs:32:1 + --> $DIR/impl-wrong-item-for-trait.rs:38:1 | -16 | const MY_CONST: u32; +20 | const MY_CONST: u32; | -------------------- `MY_CONST` from trait ... -32 | / impl Foo for FooMethodForConst { -33 | | //~^ ERROR E0046 -34 | | //~| NOTE missing `MY_CONST` in implementation -35 | | fn bar(&self) {} +38 | / impl Foo for FooMethodForConst { +39 | | //~^ ERROR E0046 +40 | | //~| NOTE missing `MY_CONST` in implementation +41 | | fn bar(&self) {} ... | -38 | | //~| NOTE does not match trait -39 | | } +44 | | //~| NOTE does not match trait +45 | | } | |_^ missing `MY_CONST` in implementation error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo` - --> $DIR/impl-wrong-item-for-trait.rs:46:5 + --> $DIR/impl-wrong-item-for-trait.rs:52:5 | 15 | fn bar(&self); | -------------- item in trait ... -46 | type bar = u64; +52 | type bar = u64; | ^^^^^^^^^^^^^^^ does not match trait error[E0046]: not all trait items implemented, missing: `bar` - --> $DIR/impl-wrong-item-for-trait.rs:43:1 + --> $DIR/impl-wrong-item-for-trait.rs:49:1 | 15 | fn bar(&self); | -------------- `bar` from trait ... -43 | / impl Foo for FooTypeForMethod { -44 | | //~^ ERROR E0046 -45 | | //~| NOTE missing `bar` in implementation -46 | | type bar = u64; +49 | / impl Foo for FooTypeForMethod { +50 | | //~^ ERROR E0046 +51 | | //~| NOTE missing `bar` in implementation +52 | | type bar = u64; ... | -49 | | const MY_CONST: u32 = 1; -50 | | } +57 | | const MY_CONST: u32 = 1; +58 | | } | |_^ missing `bar` in implementation error[E0046]: not all trait items implemented, missing: `fmt` - --> $DIR/impl-wrong-item-for-trait.rs:52:1 + --> $DIR/impl-wrong-item-for-trait.rs:60:1 | -52 | / impl Debug for FooTypeForMethod { -53 | | } +60 | / impl Debug for FooTypeForMethod { +61 | | } | |_^ missing `fmt` in implementation | = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` diff --git a/src/test/ui/span/issue-11925.rs b/src/test/ui/span/issue-11925.rs index 7bea8642cce14..fd5625f68923a 100644 --- a/src/test/ui/span/issue-11925.rs +++ b/src/test/ui/span/issue-11925.rs @@ -15,7 +15,7 @@ fn to_fn_once>(f: F) -> F { f } fn main() { let r = { let x: Box<_> = box 42; - let f = to_fn_once(move|| &x); + let f = to_fn_once(move|| &x); //~ ERROR does not live long enough f() }; diff --git a/src/test/ui/span/issue-11925.stderr b/src/test/ui/span/issue-11925.stderr index 6b2942bc7a8a7..057ccc8d677e9 100644 --- a/src/test/ui/span/issue-11925.stderr +++ b/src/test/ui/span/issue-11925.stderr @@ -1,7 +1,7 @@ error[E0597]: `x` does not live long enough --> $DIR/issue-11925.rs:18:36 | -18 | let f = to_fn_once(move|| &x); +18 | let f = to_fn_once(move|| &x); //~ ERROR does not live long enough | ^ | | | borrow occurs here diff --git a/src/test/ui/span/issue-15480.rs b/src/test/ui/span/issue-15480.rs index 871e0af50bfc6..90f3e1fd00a08 100644 --- a/src/test/ui/span/issue-15480.rs +++ b/src/test/ui/span/issue-15480.rs @@ -13,7 +13,7 @@ fn id(x: T) -> T { x } fn main() { let v = vec![ &id(3) - ]; + ]; //~ ERROR borrowed value does not live long enough for &&x in &v { println!("{}", x + 3); diff --git a/src/test/ui/span/issue-15480.stderr b/src/test/ui/span/issue-15480.stderr index 7f4ca19241cd2..4d2e6f8374c1f 100644 --- a/src/test/ui/span/issue-15480.stderr +++ b/src/test/ui/span/issue-15480.stderr @@ -3,7 +3,7 @@ error[E0597]: borrowed value does not live long enough | 15 | &id(3) | ----- temporary value created here -16 | ]; +16 | ]; //~ ERROR borrowed value does not live long enough | ^ temporary value dropped here while still borrowed ... 21 | } diff --git a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.rs b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.rs index a04edd99b8b6b..583c569062170 100644 --- a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.rs +++ b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.rs @@ -24,8 +24,8 @@ fn foo(x: RefCell) -> String { fn foo2(x: RefCell) -> String { let ret = { let y = x; - y.borrow().clone() //~ ERROR `y` does not live long enough - }; + y.borrow().clone() + }; //~ ERROR `y` does not live long enough ret } diff --git a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr index 02c033153725c..090cf1d924bdb 100644 --- a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr +++ b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr @@ -11,9 +11,9 @@ error[E0597]: `y` does not live long enough error[E0597]: `y` does not live long enough --> $DIR/issue-23338-locals-die-before-temps-of-body.rs:28:5 | -27 | y.borrow().clone() //~ ERROR `y` does not live long enough +27 | y.borrow().clone() | - borrow occurs here -28 | }; +28 | }; //~ ERROR `y` does not live long enough | ^- borrowed value needs to live until here | | | `y` dropped here while still borrowed diff --git a/src/test/ui/span/issue-24690.rs b/src/test/ui/span/issue-24690.rs index f59d2845108fb..041ca6c426c1f 100644 --- a/src/test/ui/span/issue-24690.rs +++ b/src/test/ui/span/issue-24690.rs @@ -18,8 +18,9 @@ #![warn(unused)] #[rustc_error] -fn main() { - let theTwo = 2; - let theOtherTwo = 2; +fn main() { //~ ERROR compilation successful + let theTwo = 2; //~ WARN should have a snake case name + let theOtherTwo = 2; //~ WARN should have a snake case name + //~^ WARN unused variable println!("{}", theTwo); } diff --git a/src/test/ui/span/issue-24690.stderr b/src/test/ui/span/issue-24690.stderr index 718720ebf8383..7e19c7492ce0b 100644 --- a/src/test/ui/span/issue-24690.stderr +++ b/src/test/ui/span/issue-24690.stderr @@ -1,7 +1,7 @@ warning: unused variable: `theOtherTwo` --> $DIR/issue-24690.rs:23:9 | -23 | let theOtherTwo = 2; +23 | let theOtherTwo = 2; //~ WARN should have a snake case name | ^^^^^^^^^^^ | note: lint level defined here @@ -15,7 +15,7 @@ note: lint level defined here warning: variable `theTwo` should have a snake case name such as `the_two` --> $DIR/issue-24690.rs:22:9 | -22 | let theTwo = 2; +22 | let theTwo = 2; //~ WARN should have a snake case name | ^^^^^^ | = note: #[warn(non_snake_case)] on by default @@ -23,16 +23,17 @@ warning: variable `theTwo` should have a snake case name such as `the_two` warning: variable `theOtherTwo` should have a snake case name such as `the_other_two` --> $DIR/issue-24690.rs:23:9 | -23 | let theOtherTwo = 2; +23 | let theOtherTwo = 2; //~ WARN should have a snake case name | ^^^^^^^^^^^ error: compilation successful --> $DIR/issue-24690.rs:21:1 | -21 | / fn main() { -22 | | let theTwo = 2; -23 | | let theOtherTwo = 2; -24 | | println!("{}", theTwo); -25 | | } +21 | / fn main() { //~ ERROR compilation successful +22 | | let theTwo = 2; //~ WARN should have a snake case name +23 | | let theOtherTwo = 2; //~ WARN should have a snake case name +24 | | //~^ WARN unused variable +25 | | println!("{}", theTwo); +26 | | } | |_^ diff --git a/src/test/ui/span/issue-27522.rs b/src/test/ui/span/issue-27522.rs index 81fcb007eb491..1e3eba4bf3631 100644 --- a/src/test/ui/span/issue-27522.rs +++ b/src/test/ui/span/issue-27522.rs @@ -13,7 +13,7 @@ struct SomeType {} trait Foo { - fn handler(self: &SomeType); + fn handler(self: &SomeType); //~ ERROR invalid `self` type } fn main() {} diff --git a/src/test/ui/span/issue-27522.stderr b/src/test/ui/span/issue-27522.stderr index 117b109780b15..dc02ad73ee2bc 100644 --- a/src/test/ui/span/issue-27522.stderr +++ b/src/test/ui/span/issue-27522.stderr @@ -1,11 +1,11 @@ -error[E0308]: mismatched method receiver +error[E0307]: invalid `self` type: &SomeType --> $DIR/issue-27522.rs:16:22 | -16 | fn handler(self: &SomeType); - | ^^^^^^^^^ expected Self, found struct `SomeType` +16 | fn handler(self: &SomeType); //~ ERROR invalid `self` type + | ^^^^^^^^^ | - = note: expected type `&Self` - found type `&SomeType` + = note: type must be `Self` or a type that dereferences to it` + = help: consider changing to `self`, `&self`, `&mut self`, or `self: Box` error: aborting due to previous error diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr index 2a874181c7ad9..cf5190bba0c04 100644 --- a/src/test/ui/span/issue-33884.stderr +++ b/src/test/ui/span/issue-33884.stderr @@ -6,7 +6,7 @@ error[E0308]: mismatched types | = note: expected type `std::fmt::Arguments<'_>` found type `std::string::String` - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/span/issue-34264.rs b/src/test/ui/span/issue-34264.rs index 00482f50618db..8baa381fb2d73 100644 --- a/src/test/ui/span/issue-34264.rs +++ b/src/test/ui/span/issue-34264.rs @@ -8,13 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(Option, String) {} -fn bar(x, y: usize) {} +fn foo(Option, String) {} //~ ERROR expected one of +//~^ ERROR expected one of +fn bar(x, y: usize) {} //~ ERROR expected one of fn main() { foo(Some(42), 2); - foo(Some(42), 2, ""); - bar("", ""); + foo(Some(42), 2, ""); //~ ERROR this function takes + bar("", ""); //~ ERROR mismatched types bar(1, 2); - bar(1, 2, 3); + bar(1, 2, 3); //~ ERROR this function takes } diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr index e25caacac8feb..6ab92cab83dc2 100644 --- a/src/test/ui/span/issue-34264.stderr +++ b/src/test/ui/span/issue-34264.stderr @@ -1,34 +1,34 @@ error: expected one of `:` or `@`, found `<` --> $DIR/issue-34264.rs:11:14 | -11 | fn foo(Option, String) {} +11 | fn foo(Option, String) {} //~ ERROR expected one of | ^ expected one of `:` or `@` here error: expected one of `:` or `@`, found `)` --> $DIR/issue-34264.rs:11:27 | -11 | fn foo(Option, String) {} +11 | fn foo(Option, String) {} //~ ERROR expected one of | ^ expected one of `:` or `@` here error: expected one of `:` or `@`, found `,` - --> $DIR/issue-34264.rs:12:9 + --> $DIR/issue-34264.rs:13:9 | -12 | fn bar(x, y: usize) {} +13 | fn bar(x, y: usize) {} //~ ERROR expected one of | ^ expected one of `:` or `@` here error[E0061]: this function takes 2 parameters but 3 parameters were supplied - --> $DIR/issue-34264.rs:16:9 + --> $DIR/issue-34264.rs:17:9 | -11 | fn foo(Option, String) {} +11 | fn foo(Option, String) {} //~ ERROR expected one of | ------------------------------ defined here ... -16 | foo(Some(42), 2, ""); +17 | foo(Some(42), 2, ""); //~ ERROR this function takes | ^^^^^^^^^^^^^^^ expected 2 parameters error[E0308]: mismatched types - --> $DIR/issue-34264.rs:17:13 + --> $DIR/issue-34264.rs:18:13 | -17 | bar("", ""); +18 | bar("", ""); //~ ERROR mismatched types | ^^ expected usize, found reference | = note: expected type `usize` @@ -37,12 +37,12 @@ error[E0308]: mismatched types - .len() error[E0061]: this function takes 2 parameters but 3 parameters were supplied - --> $DIR/issue-34264.rs:19:9 + --> $DIR/issue-34264.rs:20:9 | -12 | fn bar(x, y: usize) {} +13 | fn bar(x, y: usize) {} //~ ERROR expected one of | ---------------------- defined here ... -19 | bar(1, 2, 3); +20 | bar(1, 2, 3); //~ ERROR this function takes | ^^^^^^^ expected 2 parameters error: aborting due to 6 previous errors diff --git a/src/test/ui/span/issue-35987.rs b/src/test/ui/span/issue-35987.rs index 8ff5f3b83986c..fa0410686c26c 100644 --- a/src/test/ui/span/issue-35987.rs +++ b/src/test/ui/span/issue-35987.rs @@ -13,6 +13,7 @@ struct Foo(T); use std::ops::Add; impl Add for Foo { +//~^ ERROR expected trait, found type parameter type Output = usize; fn add(self, rhs: Self) -> Self::Output { diff --git a/src/test/ui/span/issue-35987.stderr b/src/test/ui/span/issue-35987.stderr index b57b58e3d2a6d..5e7a492ca2ada 100644 --- a/src/test/ui/span/issue-35987.stderr +++ b/src/test/ui/span/issue-35987.stderr @@ -3,7 +3,6 @@ error[E0404]: expected trait, found type parameter `Add` | 15 | impl Add for Foo { | ^^^ not a trait - | help: possible better candidate is found in another module, you can import it into scope | 13 | use std::ops::Add; diff --git a/src/test/ui/span/issue-36530.rs b/src/test/ui/span/issue-36530.rs index 893c2168c2e16..c6cdb8b6db7cf 100644 --- a/src/test/ui/span/issue-36530.rs +++ b/src/test/ui/span/issue-36530.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[foo] +#[foo] //~ ERROR is currently unknown to the compiler mod foo { - #![foo] + #![foo] //~ ERROR is currently unknown to the compiler } diff --git a/src/test/ui/span/issue-36530.stderr b/src/test/ui/span/issue-36530.stderr index dc6190c2e76b0..50908b2ca3970 100644 --- a/src/test/ui/span/issue-36530.stderr +++ b/src/test/ui/span/issue-36530.stderr @@ -1,7 +1,7 @@ error: The attribute `foo` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) --> $DIR/issue-36530.rs:11:1 | -11 | #[foo] +11 | #[foo] //~ ERROR is currently unknown to the compiler | ^^^^^^ | = help: add #![feature(custom_attribute)] to the crate attributes to enable @@ -9,7 +9,7 @@ error: The attribute `foo` is currently unknown to the compiler and may have mea error: The attribute `foo` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) --> $DIR/issue-36530.rs:13:5 | -13 | #![foo] +13 | #![foo] //~ ERROR is currently unknown to the compiler | ^^^^^^^ | = help: add #![feature(custom_attribute)] to the crate attributes to enable diff --git a/src/test/ui/span/issue-37767.rs b/src/test/ui/span/issue-37767.rs index 49ad40259d91f..2b0250d332df7 100644 --- a/src/test/ui/span/issue-37767.rs +++ b/src/test/ui/span/issue-37767.rs @@ -17,7 +17,7 @@ trait B : A { } fn bar(a: &T) { - a.foo() + a.foo() //~ ERROR multiple applicable items } trait C { @@ -29,7 +29,7 @@ trait D : C { } fn quz(a: &T) { - a.foo() + a.foo() //~ ERROR multiple applicable items } trait E : Sized { @@ -41,7 +41,7 @@ trait F : E { } fn foo(a: T) { - a.foo() + a.foo() //~ ERROR multiple applicable items } fn pass(a: &T) { diff --git a/src/test/ui/span/issue-37767.stderr b/src/test/ui/span/issue-37767.stderr index 7cf74eaab8db1..8babcc74ed5f3 100644 --- a/src/test/ui/span/issue-37767.stderr +++ b/src/test/ui/span/issue-37767.stderr @@ -1,7 +1,7 @@ error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:20:7 | -20 | a.foo() +20 | a.foo() //~ ERROR multiple applicable items | ^^^ multiple `foo` found | note: candidate #1 is defined in the trait `A` @@ -20,7 +20,7 @@ note: candidate #2 is defined in the trait `B` error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:32:7 | -32 | a.foo() +32 | a.foo() //~ ERROR multiple applicable items | ^^^ multiple `foo` found | note: candidate #1 is defined in the trait `C` @@ -39,7 +39,7 @@ note: candidate #2 is defined in the trait `D` error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:44:7 | -44 | a.foo() +44 | a.foo() //~ ERROR multiple applicable items | ^^^ multiple `foo` found | note: candidate #1 is defined in the trait `E` diff --git a/src/test/ui/span/issue-39018.rs b/src/test/ui/span/issue-39018.rs index 1cbc5ff1d2ab5..4c9d10ba46bc8 100644 --- a/src/test/ui/span/issue-39018.rs +++ b/src/test/ui/span/issue-39018.rs @@ -10,11 +10,13 @@ pub fn main() { let x = "Hello " + "World!"; + //~^ ERROR cannot be applied to type // Make sure that the span outputs a warning // for not having an implementation for std::ops::Add // that won't output for the above string concatenation let y = World::Hello + World::Goodbye; + //~^ ERROR cannot be applied to type } enum World { diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr index d87fc122d8ee2..db662a1df5972 100644 --- a/src/test/ui/span/issue-39018.stderr +++ b/src/test/ui/span/issue-39018.stderr @@ -1,18 +1,17 @@ -error[E0369]: binary operation `+` cannot be applied to type `&'static str` +error[E0369]: binary operation `+` cannot be applied to type `&str` --> $DIR/issue-39018.rs:12:13 | 12 | let x = "Hello " + "World!"; | ^^^^^^^^^^^^^^^^^^^ `+` can't be used to concatenate two `&str` strings - | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | 12 | let x = "Hello ".to_owned() + "World!"; | ^^^^^^^^^^^^^^^^^^^ error[E0369]: binary operation `+` cannot be applied to type `World` - --> $DIR/issue-39018.rs:17:13 + --> $DIR/issue-39018.rs:18:13 | -17 | let y = World::Hello + World::Goodbye; +18 | let y = World::Hello + World::Goodbye; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: an implementation of `std::ops::Add` might be missing for `World` diff --git a/src/test/ui/span/issue-39698.rs b/src/test/ui/span/issue-39698.rs index 17b3f1c5a885e..33071c468b872 100644 --- a/src/test/ui/span/issue-39698.rs +++ b/src/test/ui/span/issue-39698.rs @@ -18,5 +18,9 @@ enum T { fn main() { match T::T1(123, 456) { T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + //~^ ERROR is not bound in all patterns + //~| ERROR is not bound in all patterns + //~| ERROR is not bound in all patterns + //~| ERROR is not bound in all patterns } } diff --git a/src/test/ui/span/issue-40157.rs b/src/test/ui/span/issue-40157.rs index 8f3a7ae341736..9e33ecde91ced 100644 --- a/src/test/ui/span/issue-40157.rs +++ b/src/test/ui/span/issue-40157.rs @@ -10,4 +10,5 @@ fn main () { {println!("{:?}", match { let foo = vec![1, 2]; foo.get(1) } { x => x });} + //~^ ERROR does not live long enough } diff --git a/src/test/ui/span/issue-40157.stderr b/src/test/ui/span/issue-40157.stderr index b689bef63f156..be7967ff619ee 100644 --- a/src/test/ui/span/issue-40157.stderr +++ b/src/test/ui/span/issue-40157.stderr @@ -8,7 +8,7 @@ error[E0597]: `foo` does not live long enough | | borrow occurs here | borrowed value needs to live until here | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/span/issue-43927-non-ADT-derive.rs b/src/test/ui/span/issue-43927-non-ADT-derive.rs index cf2a4b8d03767..782cce26a9578 100644 --- a/src/test/ui/span/issue-43927-non-ADT-derive.rs +++ b/src/test/ui/span/issue-43927-non-ADT-derive.rs @@ -11,6 +11,7 @@ #![allow(dead_code)] #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! +//~^ ERROR `derive` may only be applied to structs, enums and unions struct DerivedOn; fn main() {} diff --git a/src/test/ui/span/issue-7575.rs b/src/test/ui/span/issue-7575.rs index b74036c4f5ce8..f7059e01261a6 100644 --- a/src/test/ui/span/issue-7575.rs +++ b/src/test/ui/span/issue-7575.rs @@ -45,7 +45,7 @@ impl OtherTrait for usize { } } -struct Myisize(isize); +struct Myisize(isize); //~ NOTE not found for this impl Myisize { fn fff(i: isize) -> isize { //~ NOTE candidate @@ -74,18 +74,18 @@ fn no_param_bound(u: usize, m: Myisize) -> usize { u.f8(42) + u.f9(342) + m.fff(42) //~^ ERROR no method named `f9` found for type `usize` in the current scope //~| NOTE found the following associated functions; to be used as methods, functions must have a `self` parameter - //~| NOTE to use it here write `CtxtFn::f9(u, 342)` instead + //~| NOTE the following traits define an item //~| ERROR no method named `fff` found for type `Myisize` in the current scope //~| NOTE found the following associated functions; to be used as methods, functions must have a `self` parameter - //~| NOTE to use it here write `OtherTrait::f9(u, 342)` instead - //~| NOTE to use it here write `UnusedTrait::f9(u, 342)` instead + + } fn param_bound(t: T) -> bool { t.is_str() //~^ ERROR no method named `is_str` found for type `T` in the current scope //~| NOTE found the following associated functions; to be used as methods, functions must have a `self` parameter - //~| NOTE to use it here write `ManyImplTrait::is_str(t)` instead + //~| NOTE the following trait defines } fn main() { diff --git a/src/test/ui/span/issue-7575.stderr b/src/test/ui/span/issue-7575.stderr index 08ec2a87fcdc4..a1ed5db69c0db 100644 --- a/src/test/ui/span/issue-7575.stderr +++ b/src/test/ui/span/issue-7575.stderr @@ -33,6 +33,9 @@ note: candidate #3 is defined in the trait `UnusedTrait` error[E0599]: no method named `fff` found for type `Myisize` in the current scope --> $DIR/issue-7575.rs:74:30 | +48 | struct Myisize(isize); //~ NOTE not found for this + | ---------------------- method `fff` not found for this +... 74 | u.f8(42) + u.f9(342) + m.fff(42) | ^^^ | diff --git a/src/test/ui/span/lint-unused-unsafe.stderr b/src/test/ui/span/lint-unused-unsafe.stderr index 1fa5f94aa4ca6..f4998e08907a3 100644 --- a/src/test/ui/span/lint-unused-unsafe.stderr +++ b/src/test/ui/span/lint-unused-unsafe.stderr @@ -65,12 +65,14 @@ note: because it's nested under this `unsafe` block | |_____^ error: unnecessary `unsafe` block - --> $DIR/lint-unused-unsafe.rs:40:9 + --> $DIR/lint-unused-unsafe.rs:39:5 | -40 | / unsafe { //~ ERROR: unnecessary `unsafe` block +39 | / unsafe { //~ ERROR: unnecessary `unsafe` block +40 | | unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } - | |_________^ unnecessary `unsafe` block +43 | | } + | |_____^ unnecessary `unsafe` block | note: because it's nested under this `unsafe` fn --> $DIR/lint-unused-unsafe.rs:38:1 @@ -85,14 +87,12 @@ note: because it's nested under this `unsafe` fn | |_^ error: unnecessary `unsafe` block - --> $DIR/lint-unused-unsafe.rs:39:5 + --> $DIR/lint-unused-unsafe.rs:40:9 | -39 | / unsafe { //~ ERROR: unnecessary `unsafe` block -40 | | unsafe { //~ ERROR: unnecessary `unsafe` block +40 | / unsafe { //~ ERROR: unnecessary `unsafe` block 41 | | unsf() 42 | | } -43 | | } - | |_____^ unnecessary `unsafe` block + | |_________^ unnecessary `unsafe` block | note: because it's nested under this `unsafe` fn --> $DIR/lint-unused-unsafe.rs:38:1 diff --git a/src/test/ui/span/macro-span-replacement.rs b/src/test/ui/span/macro-span-replacement.rs index b7aae39c46927..71f37f6555e47 100644 --- a/src/test/ui/span/macro-span-replacement.rs +++ b/src/test/ui/span/macro-span-replacement.rs @@ -12,7 +12,7 @@ macro_rules! m { ($a:tt $b:tt) => { - $b $a; + $b $a; //~ WARN struct is never used } } diff --git a/src/test/ui/span/macro-span-replacement.stderr b/src/test/ui/span/macro-span-replacement.stderr index af03aa6a36908..ed96189630698 100644 --- a/src/test/ui/span/macro-span-replacement.stderr +++ b/src/test/ui/span/macro-span-replacement.stderr @@ -1,7 +1,7 @@ warning: struct is never used: `S` --> $DIR/macro-span-replacement.rs:15:9 | -15 | $b $a; +15 | $b $a; //~ WARN struct is never used | ^^^^^^ ... 20 | m!(S struct); diff --git a/src/test/ui/span/macro-ty-params.rs b/src/test/ui/span/macro-ty-params.rs index c2443b024ceb2..5d93b1266a4ae 100644 --- a/src/test/ui/span/macro-ty-params.rs +++ b/src/test/ui/span/macro-ty-params.rs @@ -15,7 +15,8 @@ macro_rules! m { } fn main() { - foo::!(); - foo::<>!(); - m!(MyTrait<>); + foo::!(); //~ ERROR generic arguments in macro path + foo::<>!(); //~ ERROR generic arguments in macro path + m!(MyTrait<>); //~ ERROR generic arguments in macro path + //~^ ERROR unexpected generic arguments in path } diff --git a/src/test/ui/span/macro-ty-params.stderr b/src/test/ui/span/macro-ty-params.stderr index 1d2f7bb2f07e9..e3e9334d9fbb7 100644 --- a/src/test/ui/span/macro-ty-params.stderr +++ b/src/test/ui/span/macro-ty-params.stderr @@ -1,31 +1,25 @@ error: unexpected generic arguments in path --> $DIR/macro-ty-params.rs:20:8 | -20 | m!(MyTrait<>); - | ^^^^^^^^^ - -error: unexpected generic arguments in path - --> $DIR/macro-ty-params.rs:20:8 - | -20 | m!(MyTrait<>); +20 | m!(MyTrait<>); //~ ERROR generic arguments in macro path | ^^^^^^^^^ error: generic arguments in macro path --> $DIR/macro-ty-params.rs:18:8 | -18 | foo::!(); +18 | foo::!(); //~ ERROR generic arguments in macro path | ^^^^^ error: generic arguments in macro path --> $DIR/macro-ty-params.rs:19:8 | -19 | foo::<>!(); +19 | foo::<>!(); //~ ERROR generic arguments in macro path | ^^^^ error: generic arguments in macro path --> $DIR/macro-ty-params.rs:20:15 | -20 | m!(MyTrait<>); +20 | m!(MyTrait<>); //~ ERROR generic arguments in macro path | ^^ error: aborting due to 5 previous errors diff --git a/src/test/ui/span/missing-unit-argument.rs b/src/test/ui/span/missing-unit-argument.rs index 2cdab5bedc49a..a586d33eaa738 100644 --- a/src/test/ui/span/missing-unit-argument.rs +++ b/src/test/ui/span/missing-unit-argument.rs @@ -11,9 +11,17 @@ fn foo(():(), ():()) {} fn bar(():()) {} +struct S; +impl S { + fn baz(self, (): ()) { } + fn generic(self, _: T) { } +} + fn main() { - let _: Result<(), String> = Ok(); - foo(); - foo(()); - bar(); + let _: Result<(), String> = Ok(); //~ ERROR this function takes + foo(); //~ ERROR this function takes + foo(()); //~ ERROR this function takes + bar(); //~ ERROR this function takes + S.baz(); //~ ERROR this function takes + S.generic::<()>(); //~ ERROR this function takes } diff --git a/src/test/ui/span/missing-unit-argument.stderr b/src/test/ui/span/missing-unit-argument.stderr index e508a30d1826c..27f9972557b77 100644 --- a/src/test/ui/span/missing-unit-argument.stderr +++ b/src/test/ui/span/missing-unit-argument.stderr @@ -1,45 +1,69 @@ error[E0061]: this function takes 1 parameter but 0 parameters were supplied - --> $DIR/missing-unit-argument.rs:15:33 + --> $DIR/missing-unit-argument.rs:21:33 | -15 | let _: Result<(), String> = Ok(); +21 | let _: Result<(), String> = Ok(); //~ ERROR this function takes | ^^^^ +help: expected the unit value `()`; create it with empty parentheses | -help: expected the unit value `()`. You can create one with a pair of parenthesis - | -15 | let _: Result<(), String> = Ok(()); +21 | let _: Result<(), String> = Ok(()); //~ ERROR this function takes | ^^ error[E0061]: this function takes 2 parameters but 0 parameters were supplied - --> $DIR/missing-unit-argument.rs:16:5 + --> $DIR/missing-unit-argument.rs:22:5 | 11 | fn foo(():(), ():()) {} | ----------------------- defined here ... -16 | foo(); +22 | foo(); //~ ERROR this function takes | ^^^^^ expected 2 parameters error[E0061]: this function takes 2 parameters but 1 parameter was supplied - --> $DIR/missing-unit-argument.rs:17:9 + --> $DIR/missing-unit-argument.rs:23:9 | 11 | fn foo(():(), ():()) {} | ----------------------- defined here ... -17 | foo(()); +23 | foo(()); //~ ERROR this function takes | ^^ expected 2 parameters error[E0061]: this function takes 1 parameter but 0 parameters were supplied - --> $DIR/missing-unit-argument.rs:18:5 + --> $DIR/missing-unit-argument.rs:24:5 | 12 | fn bar(():()) {} | ---------------- defined here ... -18 | bar(); +24 | bar(); //~ ERROR this function takes | ^^^^^ +help: expected the unit value `()`; create it with empty parentheses | -help: expected the unit value `()`. You can create one with a pair of parenthesis - | -18 | bar(()); +24 | bar(()); //~ ERROR this function takes | ^^ -error: aborting due to 4 previous errors +error[E0061]: this function takes 1 parameter but 0 parameters were supplied + --> $DIR/missing-unit-argument.rs:25:7 + | +16 | fn baz(self, (): ()) { } + | ------------------------ defined here +... +25 | S.baz(); //~ ERROR this function takes + | ^^^ +help: expected the unit value `()`; create it with empty parentheses + | +25 | S.baz(()); //~ ERROR this function takes + | ^^ + +error[E0061]: this function takes 1 parameter but 0 parameters were supplied + --> $DIR/missing-unit-argument.rs:26:7 + | +17 | fn generic(self, _: T) { } + | ----------------------------- defined here +... +26 | S.generic::<()>(); //~ ERROR this function takes + | ^^^^^^^ +help: expected the unit value `()`; create it with empty parentheses + | +26 | S.generic::<()>(()); //~ ERROR this function takes + | ^^ + +error: aborting due to 6 previous errors diff --git a/src/test/ui/span/move-closure.rs b/src/test/ui/span/move-closure.rs index e11ef0dddaa94..87ce15292979a 100644 --- a/src/test/ui/span/move-closure.rs +++ b/src/test/ui/span/move-closure.rs @@ -12,5 +12,5 @@ // Make sure that the span of a closure marked `move` begins at the `move` keyword. fn main() { - let x: () = move || (); + let x: () = move || (); //~ ERROR mismatched types } diff --git a/src/test/ui/span/move-closure.stderr b/src/test/ui/span/move-closure.stderr index 2294e6476d61f..9135a26bbaf77 100644 --- a/src/test/ui/span/move-closure.stderr +++ b/src/test/ui/span/move-closure.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/move-closure.rs:15:17 | -15 | let x: () = move || (); +15 | let x: () = move || (); //~ ERROR mismatched types | ^^^^^^^^^^ expected (), found closure | = note: expected type `()` diff --git a/src/test/ui/span/multiline-span-E0072.rs b/src/test/ui/span/multiline-span-E0072.rs index 323e7fb5a42c0..2344d72f45c79 100644 --- a/src/test/ui/span/multiline-span-E0072.rs +++ b/src/test/ui/span/multiline-span-E0072.rs @@ -9,7 +9,7 @@ // except according to those terms. // It should just use the entire body instead of pointing at the next two lines -struct +struct //~ ERROR has infinite size ListNode { head: u8, diff --git a/src/test/ui/span/multiline-span-E0072.stderr b/src/test/ui/span/multiline-span-E0072.stderr index a06cbd04deb4f..124a53219a9dc 100644 --- a/src/test/ui/span/multiline-span-E0072.stderr +++ b/src/test/ui/span/multiline-span-E0072.stderr @@ -1,7 +1,7 @@ error[E0072]: recursive type `ListNode` has infinite size --> $DIR/multiline-span-E0072.rs:12:1 | -12 | / struct +12 | / struct //~ ERROR has infinite size 13 | | ListNode 14 | | { 15 | | head: u8, diff --git a/src/test/ui/span/multiline-span-simple.rs b/src/test/ui/span/multiline-span-simple.rs index 451492ba6930b..f8e4cbcbf191f 100644 --- a/src/test/ui/span/multiline-span-simple.rs +++ b/src/test/ui/span/multiline-span-simple.rs @@ -20,7 +20,7 @@ fn main() { let x = 1; let y = 2; let z = 3; - foo(1 as u32 + + foo(1 as u32 + //~ ERROR not satisfied bar(x, diff --git a/src/test/ui/span/multiline-span-simple.stderr b/src/test/ui/span/multiline-span-simple.stderr index 3915f1b655cf5..b068798630ed8 100644 --- a/src/test/ui/span/multiline-span-simple.stderr +++ b/src/test/ui/span/multiline-span-simple.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `u32: std::ops::Add<()>` is not satisfied --> $DIR/multiline-span-simple.rs:23:18 | -23 | foo(1 as u32 + +23 | foo(1 as u32 + //~ ERROR not satisfied | ^ no implementation for `u32 + ()` | = help: the trait `std::ops::Add<()>` is not implemented for `u32` diff --git a/src/test/ui/span/multispan-import-lint.rs b/src/test/ui/span/multispan-import-lint.rs index 66536b29c0298..c3582b0a0c5c6 100644 --- a/src/test/ui/span/multispan-import-lint.rs +++ b/src/test/ui/span/multispan-import-lint.rs @@ -11,6 +11,7 @@ #![warn(unused)] use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd}; +//~^ WARN unused imports fn main() { let _ = min(1, 2); diff --git a/src/test/ui/span/mut-arg-hint.rs b/src/test/ui/span/mut-arg-hint.rs index 296ee6ca10e05..b22cadf3ef2b0 100644 --- a/src/test/ui/span/mut-arg-hint.rs +++ b/src/test/ui/span/mut-arg-hint.rs @@ -10,19 +10,19 @@ trait B { fn foo(mut a: &String) { - a.push_str("bar"); + a.push_str("bar"); //~ ERROR cannot borrow immutable borrowed content } } pub fn foo<'a>(mut a: &'a String) { - a.push_str("foo"); + a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content } struct A {} impl A { pub fn foo(mut a: &String) { - a.push_str("foo"); + a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content } } diff --git a/src/test/ui/span/mut-arg-hint.stderr b/src/test/ui/span/mut-arg-hint.stderr index f8584c6739078..02c607ddc3775 100644 --- a/src/test/ui/span/mut-arg-hint.stderr +++ b/src/test/ui/span/mut-arg-hint.stderr @@ -3,7 +3,7 @@ error[E0596]: cannot borrow immutable borrowed content `*a` as mutable | 12 | fn foo(mut a: &String) { | ------- use `&mut String` here to make mutable -13 | a.push_str("bar"); +13 | a.push_str("bar"); //~ ERROR cannot borrow immutable borrowed content | ^ cannot borrow as mutable error[E0596]: cannot borrow immutable borrowed content `*a` as mutable @@ -11,7 +11,7 @@ error[E0596]: cannot borrow immutable borrowed content `*a` as mutable | 17 | pub fn foo<'a>(mut a: &'a String) { | ---------- use `&'a mut String` here to make mutable -18 | a.push_str("foo"); +18 | a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content | ^ cannot borrow as mutable error[E0596]: cannot borrow immutable borrowed content `*a` as mutable @@ -19,7 +19,7 @@ error[E0596]: cannot borrow immutable borrowed content `*a` as mutable | 24 | pub fn foo(mut a: &String) { | ------- use `&mut String` here to make mutable -25 | a.push_str("foo"); +25 | a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content | ^ cannot borrow as mutable error: aborting due to 3 previous errors diff --git a/src/test/ui/span/mut-ptr-cant-outlive-ref.rs b/src/test/ui/span/mut-ptr-cant-outlive-ref.rs index 8e968d90a2f65..135b577d7f785 100644 --- a/src/test/ui/span/mut-ptr-cant-outlive-ref.rs +++ b/src/test/ui/span/mut-ptr-cant-outlive-ref.rs @@ -15,6 +15,6 @@ fn main() { let p; { let b = m.borrow(); - p = &*b; //~ ERROR `b` does not live long enough - } + p = &*b; + } //~ ERROR `b` does not live long enough } diff --git a/src/test/ui/span/mut-ptr-cant-outlive-ref.stderr b/src/test/ui/span/mut-ptr-cant-outlive-ref.stderr index 007a9fad83073..d9f5736061a35 100644 --- a/src/test/ui/span/mut-ptr-cant-outlive-ref.stderr +++ b/src/test/ui/span/mut-ptr-cant-outlive-ref.stderr @@ -1,9 +1,9 @@ error[E0597]: `b` does not live long enough --> $DIR/mut-ptr-cant-outlive-ref.rs:19:5 | -18 | p = &*b; //~ ERROR `b` does not live long enough +18 | p = &*b; | - borrow occurs here -19 | } +19 | } //~ ERROR `b` does not live long enough | ^ `b` dropped here while still borrowed 20 | } | - borrowed value needs to live until here diff --git a/src/test/ui/span/non-existing-module-import.rs b/src/test/ui/span/non-existing-module-import.rs index 3d45a94d531c1..ad6409f014ad1 100644 --- a/src/test/ui/span/non-existing-module-import.rs +++ b/src/test/ui/span/non-existing-module-import.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::bar::{foo1, foo2}; +use std::bar::{foo1, foo2}; //~ ERROR unresolved import fn main() {} diff --git a/src/test/ui/span/non-existing-module-import.stderr b/src/test/ui/span/non-existing-module-import.stderr index 93339576f4904..74f5dac493772 100644 --- a/src/test/ui/span/non-existing-module-import.stderr +++ b/src/test/ui/span/non-existing-module-import.stderr @@ -1,7 +1,7 @@ error[E0432]: unresolved import `std::bar` --> $DIR/non-existing-module-import.rs:11:10 | -11 | use std::bar::{foo1, foo2}; +11 | use std::bar::{foo1, foo2}; //~ ERROR unresolved import | ^^^ Could not find `bar` in `std` error: aborting due to previous error diff --git a/src/test/ui/span/pub-struct-field.rs b/src/test/ui/span/pub-struct-field.rs index 54cb0b59c75c3..11af361e8a846 100644 --- a/src/test/ui/span/pub-struct-field.rs +++ b/src/test/ui/span/pub-struct-field.rs @@ -13,8 +13,8 @@ struct Foo { bar: u8, - pub bar: u8, - pub(crate) bar: u8, + pub bar: u8, //~ ERROR is already declared + pub(crate) bar: u8, //~ ERROR is already declared } fn main() {} diff --git a/src/test/ui/span/pub-struct-field.stderr b/src/test/ui/span/pub-struct-field.stderr index c66361c8546b8..5b303758d2bac 100644 --- a/src/test/ui/span/pub-struct-field.stderr +++ b/src/test/ui/span/pub-struct-field.stderr @@ -3,7 +3,7 @@ error[E0124]: field `bar` is already declared | 15 | bar: u8, | ------- `bar` first declared here -16 | pub bar: u8, +16 | pub bar: u8, //~ ERROR is already declared | ^^^^^^^^^^^ field already declared error[E0124]: field `bar` is already declared @@ -11,8 +11,8 @@ error[E0124]: field `bar` is already declared | 15 | bar: u8, | ------- `bar` first declared here -16 | pub bar: u8, -17 | pub(crate) bar: u8, +16 | pub bar: u8, //~ ERROR is already declared +17 | pub(crate) bar: u8, //~ ERROR is already declared | ^^^^^^^^^^^^^^^^^^ field already declared error: aborting due to 2 previous errors diff --git a/src/test/ui/span/range-2.rs b/src/test/ui/span/range-2.rs index 94967693ecf91..589f7182129d8 100644 --- a/src/test/ui/span/range-2.rs +++ b/src/test/ui/span/range-2.rs @@ -15,7 +15,7 @@ pub fn main() { let a = 42; let b = 42; &a..&b - //~^ ERROR `a` does not live long enough - //~^^ ERROR `b` does not live long enough }; + //~^ ERROR `a` does not live long enough + //~^^ ERROR `b` does not live long enough } diff --git a/src/test/ui/span/range-2.stderr b/src/test/ui/span/range-2.stderr index 285ab4aee4bc4..80d364cd8be4b 100644 --- a/src/test/ui/span/range-2.stderr +++ b/src/test/ui/span/range-2.stderr @@ -1,22 +1,22 @@ error[E0597]: `a` does not live long enough - --> $DIR/range-2.rs:20:5 + --> $DIR/range-2.rs:18:5 | 17 | &a..&b | - borrow occurs here -... -20 | }; +18 | }; | ^ `a` dropped here while still borrowed +... 21 | } | - borrowed value needs to live until here error[E0597]: `b` does not live long enough - --> $DIR/range-2.rs:20:5 + --> $DIR/range-2.rs:18:5 | 17 | &a..&b | - borrow occurs here -... -20 | }; +18 | }; | ^ `b` dropped here while still borrowed +... 21 | } | - borrowed value needs to live until here diff --git a/src/test/ui/span/recursive-type-field.rs b/src/test/ui/span/recursive-type-field.rs index 6fef4d30f7a77..764b5392578b8 100644 --- a/src/test/ui/span/recursive-type-field.rs +++ b/src/test/ui/span/recursive-type-field.rs @@ -10,12 +10,12 @@ use std::rc::Rc; -struct Foo<'a> { +struct Foo<'a> { //~ ERROR recursive type bar: Bar<'a>, b: Rc>, } -struct Bar<'a> { +struct Bar<'a> { //~ ERROR recursive type y: (Foo<'a>, Foo<'a>), z: Option>, a: &'a Foo<'a>, diff --git a/src/test/ui/span/recursive-type-field.stderr b/src/test/ui/span/recursive-type-field.stderr index b4d0b5a6a25d0..bd9f5f032ef37 100644 --- a/src/test/ui/span/recursive-type-field.stderr +++ b/src/test/ui/span/recursive-type-field.stderr @@ -1,7 +1,7 @@ error[E0072]: recursive type `Foo` has infinite size --> $DIR/recursive-type-field.rs:13:1 | -13 | struct Foo<'a> { +13 | struct Foo<'a> { //~ ERROR recursive type | ^^^^^^^^^^^^^^ recursive type has infinite size 14 | bar: Bar<'a>, | ------------ recursive without indirection @@ -11,7 +11,7 @@ error[E0072]: recursive type `Foo` has infinite size error[E0072]: recursive type `Bar` has infinite size --> $DIR/recursive-type-field.rs:18:1 | -18 | struct Bar<'a> { +18 | struct Bar<'a> { //~ ERROR recursive type | ^^^^^^^^^^^^^^ recursive type has infinite size 19 | y: (Foo<'a>, Foo<'a>), | --------------------- recursive without indirection diff --git a/src/test/ui/span/regionck-unboxed-closure-lifetimes.rs b/src/test/ui/span/regionck-unboxed-closure-lifetimes.rs index 8ec6036762f48..74a6a2960c947 100644 --- a/src/test/ui/span/regionck-unboxed-closure-lifetimes.rs +++ b/src/test/ui/span/regionck-unboxed-closure-lifetimes.rs @@ -14,7 +14,7 @@ fn main() { let mut f; { let c = 1; - let c_ref = &c; //~ ERROR `c` does not live long enough + let c_ref = &c; f = move |a: isize, b: isize| { a + b + *c_ref }; - } + } //~ ERROR `c` does not live long enough } diff --git a/src/test/ui/span/regionck-unboxed-closure-lifetimes.stderr b/src/test/ui/span/regionck-unboxed-closure-lifetimes.stderr index 29848412e4e77..acd995c6bb86f 100644 --- a/src/test/ui/span/regionck-unboxed-closure-lifetimes.stderr +++ b/src/test/ui/span/regionck-unboxed-closure-lifetimes.stderr @@ -1,10 +1,10 @@ error[E0597]: `c` does not live long enough --> $DIR/regionck-unboxed-closure-lifetimes.rs:19:5 | -17 | let c_ref = &c; //~ ERROR `c` does not live long enough +17 | let c_ref = &c; | - borrow occurs here 18 | f = move |a: isize, b: isize| { a + b + *c_ref }; -19 | } +19 | } //~ ERROR `c` does not live long enough | ^ `c` dropped here while still borrowed 20 | } | - borrowed value needs to live until here diff --git a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.rs b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.rs index 99b0d6ed2964c..5449bbfdce3a3 100644 --- a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.rs +++ b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.rs @@ -21,5 +21,5 @@ fn main() { { let ss: &isize = &id(1); blah = box ss as Box; - } + } //~ ERROR does not live long enough } diff --git a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr index 6a3625441b49e..69bdde889166f 100644 --- a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr +++ b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr @@ -4,7 +4,7 @@ error[E0597]: borrowed value does not live long enough 22 | let ss: &isize = &id(1); | ----- temporary value created here 23 | blah = box ss as Box; -24 | } +24 | } //~ ERROR does not live long enough | ^ temporary value dropped here while still borrowed 25 | } | - temporary value needs to live until here diff --git a/src/test/ui/span/regions-close-over-type-parameter-2.rs b/src/test/ui/span/regions-close-over-type-parameter-2.rs index 053af49e0684b..cd838db92e188 100644 --- a/src/test/ui/span/regions-close-over-type-parameter-2.rs +++ b/src/test/ui/span/regions-close-over-type-parameter-2.rs @@ -30,7 +30,7 @@ fn main() { let _ = { let tmp0 = 3; - let tmp1 = &tmp0; //~ ERROR `tmp0` does not live long enough + let tmp1 = &tmp0; repeater3(tmp1) - }; + }; //~ ERROR `tmp0` does not live long enough } diff --git a/src/test/ui/span/regions-close-over-type-parameter-2.stderr b/src/test/ui/span/regions-close-over-type-parameter-2.stderr index a89127b1436c5..a7f79f8b26528 100644 --- a/src/test/ui/span/regions-close-over-type-parameter-2.stderr +++ b/src/test/ui/span/regions-close-over-type-parameter-2.stderr @@ -1,10 +1,10 @@ error[E0597]: `tmp0` does not live long enough --> $DIR/regions-close-over-type-parameter-2.rs:35:5 | -33 | let tmp1 = &tmp0; //~ ERROR `tmp0` does not live long enough +33 | let tmp1 = &tmp0; | ---- borrow occurs here 34 | repeater3(tmp1) -35 | }; +35 | }; //~ ERROR `tmp0` does not live long enough | ^- borrowed value needs to live until here | | | `tmp0` dropped here while still borrowed diff --git a/src/test/ui/span/regions-escape-loop-via-variable.rs b/src/test/ui/span/regions-escape-loop-via-variable.rs index f588655d1afa8..1dc1ae6071578 100644 --- a/src/test/ui/span/regions-escape-loop-via-variable.rs +++ b/src/test/ui/span/regions-escape-loop-via-variable.rs @@ -18,6 +18,6 @@ fn main() { loop { let x = 1 + *p; - p = &x; //~ ERROR `x` does not live long enough - } + p = &x; + } //~ ERROR `x` does not live long enough } diff --git a/src/test/ui/span/regions-escape-loop-via-variable.stderr b/src/test/ui/span/regions-escape-loop-via-variable.stderr index cfdd1c8b1539d..554ffe91e6d1f 100644 --- a/src/test/ui/span/regions-escape-loop-via-variable.stderr +++ b/src/test/ui/span/regions-escape-loop-via-variable.stderr @@ -1,9 +1,9 @@ error[E0597]: `x` does not live long enough --> $DIR/regions-escape-loop-via-variable.rs:22:5 | -21 | p = &x; //~ ERROR `x` does not live long enough +21 | p = &x; | - borrow occurs here -22 | } +22 | } //~ ERROR `x` does not live long enough | ^ `x` dropped here while still borrowed 23 | } | - borrowed value needs to live until here diff --git a/src/test/ui/span/regions-escape-loop-via-vec.rs b/src/test/ui/span/regions-escape-loop-via-vec.rs index 8982b5cd98de4..7c4834751d899 100644 --- a/src/test/ui/span/regions-escape-loop-via-vec.rs +++ b/src/test/ui/span/regions-escape-loop-via-vec.rs @@ -19,12 +19,11 @@ fn broken() { //~^ NOTE use of borrowed `x` let mut z = x; //~ ERROR cannot use `x` because it was mutably borrowed //~^ NOTE use of borrowed `x` - _y.push(&mut z); //~ ERROR `z` does not live long enough - //~^ NOTE does not live long enough + _y.push(&mut z); //~ NOTE borrow occurs here x += 1; //~ ERROR cannot assign //~^ NOTE assignment to borrowed `x` occurs here - } - //~^ NOTE borrowed value only lives until here + } //~ NOTE `z` dropped here while still borrowed + //~^ ERROR `z` does not live long enough } //~^ NOTE borrowed value needs to live until here diff --git a/src/test/ui/span/regions-escape-loop-via-vec.stderr b/src/test/ui/span/regions-escape-loop-via-vec.stderr index b61df82e8a14f..a7224fcd4cc8c 100644 --- a/src/test/ui/span/regions-escape-loop-via-vec.stderr +++ b/src/test/ui/span/regions-escape-loop-via-vec.stderr @@ -1,13 +1,13 @@ error[E0597]: `z` does not live long enough - --> $DIR/regions-escape-loop-via-vec.rs:26:5 + --> $DIR/regions-escape-loop-via-vec.rs:25:5 | -22 | _y.push(&mut z); //~ ERROR `z` does not live long enough +22 | _y.push(&mut z); //~ NOTE borrow occurs here | - borrow occurs here ... -26 | } +25 | } //~ NOTE `z` dropped here while still borrowed | ^ `z` dropped here while still borrowed -27 | //~^ NOTE borrowed value only lives until here -28 | } +26 | //~^ ERROR `z` does not live long enough +27 | } | - borrowed value needs to live until here error[E0503]: cannot use `x` because it was mutably borrowed @@ -29,12 +29,12 @@ error[E0503]: cannot use `x` because it was mutably borrowed | ^^^^^ use of borrowed `x` error[E0506]: cannot assign to `x` because it is borrowed - --> $DIR/regions-escape-loop-via-vec.rs:24:9 + --> $DIR/regions-escape-loop-via-vec.rs:23:9 | 14 | let mut _y = vec![&mut x]; | - borrow of `x` occurs here ... -24 | x += 1; //~ ERROR cannot assign +23 | x += 1; //~ ERROR cannot assign | ^^^^^^ assignment to borrowed `x` occurs here error: aborting due to 4 previous errors diff --git a/src/test/ui/span/regions-infer-borrow-scope-within-loop.rs b/src/test/ui/span/regions-infer-borrow-scope-within-loop.rs index a05658e9e581d..3bb069813a29f 100644 --- a/src/test/ui/span/regions-infer-borrow-scope-within-loop.rs +++ b/src/test/ui/span/regions-infer-borrow-scope-within-loop.rs @@ -21,11 +21,11 @@ fn foo(mut cond: C, mut make_box: M) where // Here we complain because the resulting region // of this borrow is the fn body as a whole. - y = borrow(&*x); //~ ERROR `*x` does not live long enough + y = borrow(&*x); assert_eq!(*x, *y); if cond() { break; } - } + } //~ ERROR `*x` does not live long enough assert!(*y != 0); } diff --git a/src/test/ui/span/regions-infer-borrow-scope-within-loop.stderr b/src/test/ui/span/regions-infer-borrow-scope-within-loop.stderr index 0960f5e1b2566..4a44a3a681379 100644 --- a/src/test/ui/span/regions-infer-borrow-scope-within-loop.stderr +++ b/src/test/ui/span/regions-infer-borrow-scope-within-loop.stderr @@ -1,10 +1,10 @@ error[E0597]: `*x` does not live long enough --> $DIR/regions-infer-borrow-scope-within-loop.rs:28:5 | -24 | y = borrow(&*x); //~ ERROR `*x` does not live long enough +24 | y = borrow(&*x); | -- borrow occurs here ... -28 | } +28 | } //~ ERROR `*x` does not live long enough | ^ `*x` dropped here while still borrowed 29 | assert!(*y != 0); 30 | } diff --git a/src/test/ui/span/send-is-not-static-ensures-scoping.rs b/src/test/ui/span/send-is-not-static-ensures-scoping.rs index 1b7718d2283a7..d294840bdfb33 100644 --- a/src/test/ui/span/send-is-not-static-ensures-scoping.rs +++ b/src/test/ui/span/send-is-not-static-ensures-scoping.rs @@ -23,13 +23,13 @@ impl<'a> Guard<'a> { fn main() { let bad = { let x = 1; - let y = &x; //~ ERROR `x` does not live long enough + let y = &x; scoped(|| { let _z = y; //~^ ERROR `y` does not live long enough }) - }; + }; //~ ERROR `x` does not live long enough bad.join(); } diff --git a/src/test/ui/span/send-is-not-static-ensures-scoping.stderr b/src/test/ui/span/send-is-not-static-ensures-scoping.stderr index 02fc9820495a9..cc12b2c1ee2b2 100644 --- a/src/test/ui/span/send-is-not-static-ensures-scoping.stderr +++ b/src/test/ui/span/send-is-not-static-ensures-scoping.stderr @@ -1,10 +1,10 @@ error[E0597]: `x` does not live long enough --> $DIR/send-is-not-static-ensures-scoping.rs:32:5 | -26 | let y = &x; //~ ERROR `x` does not live long enough +26 | let y = &x; | - borrow occurs here ... -32 | }; +32 | }; //~ ERROR `x` does not live long enough | ^ `x` dropped here while still borrowed ... 35 | } @@ -18,7 +18,7 @@ error[E0597]: `y` does not live long enough 29 | let _z = y; | ^ does not live long enough ... -32 | }; +32 | }; //~ ERROR `x` does not live long enough | - borrowed value only lives until here ... 35 | } diff --git a/src/test/ui/span/send-is-not-static-std-sync-2.rs b/src/test/ui/span/send-is-not-static-std-sync-2.rs index d9d3706586ba9..762eeffaa6af4 100644 --- a/src/test/ui/span/send-is-not-static-std-sync-2.rs +++ b/src/test/ui/span/send-is-not-static-std-sync-2.rs @@ -18,8 +18,8 @@ use std::sync::{Mutex, RwLock, mpsc}; fn mutex() { let lock = { let x = 1; - Mutex::new(&x) //~ ERROR does not live long enough - }; + Mutex::new(&x) + }; //~ ERROR does not live long enough let _dangling = *lock.lock().unwrap(); } @@ -27,8 +27,8 @@ fn mutex() { fn rwlock() { let lock = { let x = 1; - RwLock::new(&x) //~ ERROR does not live long enough - }; + RwLock::new(&x) + }; //~ ERROR does not live long enough let _dangling = *lock.read().unwrap(); } @@ -36,9 +36,9 @@ fn channel() { let (_tx, rx) = { let x = 1; let (tx, rx) = mpsc::channel(); - let _ = tx.send(&x); //~ ERROR does not live long enough + let _ = tx.send(&x); (tx, rx) - }; + }; //~ ERROR does not live long enough let _dangling = rx.recv(); } diff --git a/src/test/ui/span/send-is-not-static-std-sync-2.stderr b/src/test/ui/span/send-is-not-static-std-sync-2.stderr index 318dab599f5a3..beee85c8b1d9a 100644 --- a/src/test/ui/span/send-is-not-static-std-sync-2.stderr +++ b/src/test/ui/span/send-is-not-static-std-sync-2.stderr @@ -1,9 +1,9 @@ error[E0597]: `x` does not live long enough --> $DIR/send-is-not-static-std-sync-2.rs:22:5 | -21 | Mutex::new(&x) //~ ERROR does not live long enough +21 | Mutex::new(&x) | - borrow occurs here -22 | }; +22 | }; //~ ERROR does not live long enough | ^ `x` dropped here while still borrowed ... 25 | } @@ -12,9 +12,9 @@ error[E0597]: `x` does not live long enough error[E0597]: `x` does not live long enough --> $DIR/send-is-not-static-std-sync-2.rs:31:5 | -30 | RwLock::new(&x) //~ ERROR does not live long enough +30 | RwLock::new(&x) | - borrow occurs here -31 | }; +31 | }; //~ ERROR does not live long enough | ^ `x` dropped here while still borrowed 32 | let _dangling = *lock.read().unwrap(); 33 | } @@ -23,10 +23,10 @@ error[E0597]: `x` does not live long enough error[E0597]: `x` does not live long enough --> $DIR/send-is-not-static-std-sync-2.rs:41:5 | -39 | let _ = tx.send(&x); //~ ERROR does not live long enough +39 | let _ = tx.send(&x); | - borrow occurs here 40 | (tx, rx) -41 | }; +41 | }; //~ ERROR does not live long enough | ^ `x` dropped here while still borrowed ... 44 | } diff --git a/src/test/ui/span/send-is-not-static-std-sync.rs b/src/test/ui/span/send-is-not-static-std-sync.rs index 8ec2fe8a1ec89..05cb962755801 100644 --- a/src/test/ui/span/send-is-not-static-std-sync.rs +++ b/src/test/ui/span/send-is-not-static-std-sync.rs @@ -23,8 +23,8 @@ fn mutex() { drop(y); //~ ERROR cannot move out { let z = 2; - *lock.lock().unwrap() = &z; //~ ERROR does not live long enough - } + *lock.lock().unwrap() = &z; + } //~ ERROR does not live long enough } fn rwlock() { @@ -35,8 +35,8 @@ fn rwlock() { drop(y); //~ ERROR cannot move out { let z = 2; - *lock.write().unwrap() = &z; //~ ERROR does not live long enough - } + *lock.write().unwrap() = &z; + } //~ ERROR does not live long enough } fn channel() { @@ -49,8 +49,8 @@ fn channel() { drop(y); //~ ERROR cannot move out { let z = 2; - tx.send(&z).unwrap(); //~ ERROR does not live long enough - } + tx.send(&z).unwrap(); + } //~ ERROR does not live long enough } fn main() {} diff --git a/src/test/ui/span/send-is-not-static-std-sync.stderr b/src/test/ui/span/send-is-not-static-std-sync.stderr index 988ff228105de..e078e4e14a5ca 100644 --- a/src/test/ui/span/send-is-not-static-std-sync.stderr +++ b/src/test/ui/span/send-is-not-static-std-sync.stderr @@ -1,9 +1,9 @@ error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:27:5 | -26 | *lock.lock().unwrap() = &z; //~ ERROR does not live long enough +26 | *lock.lock().unwrap() = &z; | - borrow occurs here -27 | } +27 | } //~ ERROR does not live long enough | ^ `z` dropped here while still borrowed 28 | } | - borrowed value needs to live until here @@ -19,9 +19,9 @@ error[E0505]: cannot move out of `y` because it is borrowed error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:39:5 | -38 | *lock.write().unwrap() = &z; //~ ERROR does not live long enough +38 | *lock.write().unwrap() = &z; | - borrow occurs here -39 | } +39 | } //~ ERROR does not live long enough | ^ `z` dropped here while still borrowed 40 | } | - borrowed value needs to live until here @@ -37,9 +37,9 @@ error[E0505]: cannot move out of `y` because it is borrowed error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:53:5 | -52 | tx.send(&z).unwrap(); //~ ERROR does not live long enough +52 | tx.send(&z).unwrap(); | - borrow occurs here -53 | } +53 | } //~ ERROR does not live long enough | ^ `z` dropped here while still borrowed 54 | } | - borrowed value needs to live until here diff --git a/src/test/ui/span/slice-borrow.rs b/src/test/ui/span/slice-borrow.rs index 1b022f2324662..6bb98c34b7e6f 100644 --- a/src/test/ui/span/slice-borrow.rs +++ b/src/test/ui/span/slice-borrow.rs @@ -15,5 +15,5 @@ fn main() { { let x: &[isize] = &vec![1, 2, 3, 4, 5]; y = &x[1..]; - } + } //~ ERROR does not live long enough } diff --git a/src/test/ui/span/slice-borrow.stderr b/src/test/ui/span/slice-borrow.stderr index 5e8edf80df69e..c1b760aed2bda 100644 --- a/src/test/ui/span/slice-borrow.stderr +++ b/src/test/ui/span/slice-borrow.stderr @@ -4,12 +4,12 @@ error[E0597]: borrowed value does not live long enough 16 | let x: &[isize] = &vec![1, 2, 3, 4, 5]; | ------------------- temporary value created here 17 | y = &x[1..]; -18 | } +18 | } //~ ERROR does not live long enough | ^ temporary value dropped here while still borrowed 19 | } | - temporary value needs to live until here | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/span/suggestion-non-ascii.rs b/src/test/ui/span/suggestion-non-ascii.rs index 67dbe1dc7b566..2d82bba33c5bd 100644 --- a/src/test/ui/span/suggestion-non-ascii.rs +++ b/src/test/ui/span/suggestion-non-ascii.rs @@ -11,6 +11,6 @@ fn main() { let tup = (1,); - println!("☃{}", tup[0]); + println!("☃{}", tup[0]); //~ ERROR cannot index into a value of type } diff --git a/src/test/ui/span/suggestion-non-ascii.stderr b/src/test/ui/span/suggestion-non-ascii.stderr index c67a8fe32b9dd..9ee8ccb01d0f1 100644 --- a/src/test/ui/span/suggestion-non-ascii.stderr +++ b/src/test/ui/span/suggestion-non-ascii.stderr @@ -1,7 +1,7 @@ error[E0608]: cannot index into a value of type `({integer},)` --> $DIR/suggestion-non-ascii.rs:14:21 | -14 | println!("☃{}", tup[0]); +14 | println!("☃{}", tup[0]); //~ ERROR cannot index into a value of type | ^^^^^^ help: to access tuple elements, use: `tup.0` error: aborting due to previous error diff --git a/src/test/ui/span/type-binding.rs b/src/test/ui/span/type-binding.rs index 05285c732f414..d8a2e332a030f 100644 --- a/src/test/ui/span/type-binding.rs +++ b/src/test/ui/span/type-binding.rs @@ -14,5 +14,6 @@ use std::ops::Deref; fn homura>(_: T) {} +//~^ ERROR not found fn main() {} diff --git a/src/test/ui/span/typo-suggestion.rs b/src/test/ui/span/typo-suggestion.rs index 536bf16142b30..7aeaa44bec282 100644 --- a/src/test/ui/span/typo-suggestion.rs +++ b/src/test/ui/span/typo-suggestion.rs @@ -12,8 +12,8 @@ fn main() { let foo = 1; // `foo` shouldn't be suggested, it is too dissimilar from `bar`. - println!("Hello {}", bar); + println!("Hello {}", bar); //~ ERROR cannot find value // But this is close enough. - println!("Hello {}", fob); + println!("Hello {}", fob); //~ ERROR cannot find value } diff --git a/src/test/ui/span/typo-suggestion.stderr b/src/test/ui/span/typo-suggestion.stderr index dca0a93f897ba..2a084b1ae67ff 100644 --- a/src/test/ui/span/typo-suggestion.stderr +++ b/src/test/ui/span/typo-suggestion.stderr @@ -1,13 +1,13 @@ error[E0425]: cannot find value `bar` in this scope --> $DIR/typo-suggestion.rs:15:26 | -15 | println!("Hello {}", bar); +15 | println!("Hello {}", bar); //~ ERROR cannot find value | ^^^ not found in this scope error[E0425]: cannot find value `fob` in this scope --> $DIR/typo-suggestion.rs:18:26 | -18 | println!("Hello {}", fob); +18 | println!("Hello {}", fob); //~ ERROR cannot find value | ^^^ did you mean `foo`? error: aborting due to 2 previous errors diff --git a/src/test/ui/span/unused-warning-point-at-signature.rs b/src/test/ui/span/unused-warning-point-at-signature.rs index eb659d08da0c9..6a7071d49aed6 100644 --- a/src/test/ui/span/unused-warning-point-at-signature.rs +++ b/src/test/ui/span/unused-warning-point-at-signature.rs @@ -12,25 +12,25 @@ #![warn(unused)] -enum Enum { +enum Enum { //~ WARN enum is never used A, B, C, D, } -struct Struct { +struct Struct { //~ WARN struct is never used a: usize, b: usize, c: usize, d: usize, } -fn func() -> usize { +fn func() -> usize { //~ WARN function is never used 3 } -fn +fn //~ WARN function is never used func_complete_span() -> usize { diff --git a/src/test/ui/span/unused-warning-point-at-signature.stderr b/src/test/ui/span/unused-warning-point-at-signature.stderr index 8e658670e9c0c..ed36bd1780178 100644 --- a/src/test/ui/span/unused-warning-point-at-signature.stderr +++ b/src/test/ui/span/unused-warning-point-at-signature.stderr @@ -1,7 +1,7 @@ warning: enum is never used: `Enum` --> $DIR/unused-warning-point-at-signature.rs:15:1 | -15 | enum Enum { +15 | enum Enum { //~ WARN enum is never used | ^^^^^^^^^ | note: lint level defined here @@ -14,19 +14,19 @@ note: lint level defined here warning: struct is never used: `Struct` --> $DIR/unused-warning-point-at-signature.rs:22:1 | -22 | struct Struct { +22 | struct Struct { //~ WARN struct is never used | ^^^^^^^^^^^^^ warning: function is never used: `func` --> $DIR/unused-warning-point-at-signature.rs:29:1 | -29 | fn func() -> usize { +29 | fn func() -> usize { //~ WARN function is never used | ^^^^^^^^^^^^^^^^^^ warning: function is never used: `func_complete_span` --> $DIR/unused-warning-point-at-signature.rs:33:1 | -33 | / fn +33 | / fn //~ WARN function is never used 34 | | func_complete_span() 35 | | -> usize 36 | | { diff --git a/src/test/ui/span/vec-must-not-hide-type-from-dropck.rs b/src/test/ui/span/vec-must-not-hide-type-from-dropck.rs index d99f3bb19dbad..c4596e7c3684d 100644 --- a/src/test/ui/span/vec-must-not-hide-type-from-dropck.rs +++ b/src/test/ui/span/vec-must-not-hide-type-from-dropck.rs @@ -23,7 +23,7 @@ // conditions above to be satisfied, meaning that if the dropck is // sound, it should reject this code. -#![feature(const_atomic_usize_new)] + use std::cell::Cell; use id::Id; diff --git a/src/test/ui/span/visibility-ty-params.rs b/src/test/ui/span/visibility-ty-params.rs index 2a4a5edd04c08..5df28971e9942 100644 --- a/src/test/ui/span/visibility-ty-params.rs +++ b/src/test/ui/span/visibility-ty-params.rs @@ -14,7 +14,6 @@ macro_rules! m { struct S(T); m!{ S } //~ ERROR unexpected generic arguments in path -//~^ ERROR expected module, found struct `S` mod m { m!{ m<> } //~ ERROR unexpected generic arguments in path diff --git a/src/test/ui/span/visibility-ty-params.stderr b/src/test/ui/span/visibility-ty-params.stderr index 673b9a38e035f..8460f81af8335 100644 --- a/src/test/ui/span/visibility-ty-params.stderr +++ b/src/test/ui/span/visibility-ty-params.stderr @@ -5,9 +5,9 @@ error: unexpected generic arguments in path | ^^^^^ error: unexpected generic arguments in path - --> $DIR/visibility-ty-params.rs:20:9 + --> $DIR/visibility-ty-params.rs:19:9 | -20 | m!{ m<> } //~ ERROR unexpected generic arguments in path +19 | m!{ m<> } //~ ERROR unexpected generic arguments in path | ^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/wf-method-late-bound-regions.rs b/src/test/ui/span/wf-method-late-bound-regions.rs index b9d292fd15685..ce5ec63fbd10d 100644 --- a/src/test/ui/span/wf-method-late-bound-regions.rs +++ b/src/test/ui/span/wf-method-late-bound-regions.rs @@ -27,7 +27,7 @@ fn main() { let f2 = f; let dangling = { let pointer = Box::new(42); - f2.xmute(&pointer) //~ ERROR `pointer` does not live long enough - }; + f2.xmute(&pointer) + }; //~ ERROR `pointer` does not live long enough println!("{}", dangling); } diff --git a/src/test/ui/span/wf-method-late-bound-regions.stderr b/src/test/ui/span/wf-method-late-bound-regions.stderr index a37b5d17aac98..a3fdc53b8e54f 100644 --- a/src/test/ui/span/wf-method-late-bound-regions.stderr +++ b/src/test/ui/span/wf-method-late-bound-regions.stderr @@ -1,9 +1,9 @@ error[E0597]: `pointer` does not live long enough --> $DIR/wf-method-late-bound-regions.rs:31:5 | -30 | f2.xmute(&pointer) //~ ERROR `pointer` does not live long enough +30 | f2.xmute(&pointer) | ------- borrow occurs here -31 | }; +31 | }; //~ ERROR `pointer` does not live long enough | ^ `pointer` dropped here while still borrowed 32 | println!("{}", dangling); 33 | } diff --git a/src/test/ui/static-lifetime.rs b/src/test/ui/static-lifetime.rs index 7b1887b2d1a29..62abf11654edf 100644 --- a/src/test/ui/static-lifetime.rs +++ b/src/test/ui/static-lifetime.rs @@ -10,7 +10,7 @@ pub trait Arbitrary: Sized + 'static {} -impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} +impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} //~ ERROR lifetime bound fn main() { -} \ No newline at end of file +} diff --git a/src/test/ui/static-lifetime.stderr b/src/test/ui/static-lifetime.stderr index f73dff4f73d0e..a99dbf21e54e2 100644 --- a/src/test/ui/static-lifetime.stderr +++ b/src/test/ui/static-lifetime.stderr @@ -1,10 +1,15 @@ -error[E0477]: the type `std::borrow::Cow<'a, A>` does not fulfill the required lifetime +error[E0478]: lifetime bound not satisfied --> $DIR/static-lifetime.rs:13:20 | -13 | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} +13 | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} //~ ERROR lifetime bound | ^^^^^^^^^ | - = note: type must satisfy the static lifetime +note: lifetime parameter instantiated with the lifetime 'a as defined on the impl at 13:1 + --> $DIR/static-lifetime.rs:13:1 + | +13 | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} //~ ERROR lifetime bound + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: but lifetime parameter must outlive the static lifetime error: aborting due to previous error diff --git a/src/test/ui/str-lit-type-mismatch.rs b/src/test/ui/str-lit-type-mismatch.rs index 0fd7d3a9d869e..4a062f78a327d 100644 --- a/src/test/ui/str-lit-type-mismatch.rs +++ b/src/test/ui/str-lit-type-mismatch.rs @@ -10,7 +10,7 @@ fn main() { - let x: &[u8] = "foo"; - let y: &[u8; 4] = "baaa"; - let z: &str = b"foo"; + let x: &[u8] = "foo"; //~ ERROR mismatched types + let y: &[u8; 4] = "baaa"; //~ ERROR mismatched types + let z: &str = b"foo"; //~ ERROR mismatched types } diff --git a/src/test/ui/str-lit-type-mismatch.stderr b/src/test/ui/str-lit-type-mismatch.stderr index 47418522df8ac..b232bf74666b1 100644 --- a/src/test/ui/str-lit-type-mismatch.stderr +++ b/src/test/ui/str-lit-type-mismatch.stderr @@ -1,32 +1,38 @@ error[E0308]: mismatched types --> $DIR/str-lit-type-mismatch.rs:13:20 | -13 | let x: &[u8] = "foo"; - | ^^^^^ expected slice, found str +13 | let x: &[u8] = "foo"; //~ ERROR mismatched types + | ^^^^^ + | | + | expected slice, found str + | help: consider adding a leading `b`: `b"foo"` | = note: expected type `&[u8]` found type `&'static str` - = help: try `b"foo"` error[E0308]: mismatched types --> $DIR/str-lit-type-mismatch.rs:14:23 | -14 | let y: &[u8; 4] = "baaa"; - | ^^^^^^ expected array of 4 elements, found str +14 | let y: &[u8; 4] = "baaa"; //~ ERROR mismatched types + | ^^^^^^ + | | + | expected array of 4 elements, found str + | help: consider adding a leading `b`: `b"baaa"` | = note: expected type `&[u8; 4]` found type `&'static str` - = help: try `b"baaa"` error[E0308]: mismatched types --> $DIR/str-lit-type-mismatch.rs:15:19 | -15 | let z: &str = b"foo"; - | ^^^^^^ expected str, found array of 3 elements +15 | let z: &str = b"foo"; //~ ERROR mismatched types + | ^^^^^^ + | | + | expected str, found array of 3 elements + | help: consider removing the leading `b`: `"foo"` | = note: expected type `&str` found type `&'static [u8; 3]` - = help: try `"foo"` error: aborting due to 3 previous errors diff --git a/src/test/ui/struct-field-init-syntax.rs b/src/test/ui/struct-field-init-syntax.rs new file mode 100644 index 0000000000000..f1e24495f844f --- /dev/null +++ b/src/test/ui/struct-field-init-syntax.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +// issue #41834 + +fn main() { + let foo = Foo { + one: 111, + ..Foo::default(), + //~^ ERROR cannot use a comma after the base struct + }; + + let foo = Foo { + ..Foo::default(), + //~^ ERROR cannot use a comma after the base struct + one: 111, + }; +} diff --git a/src/test/ui/struct-field-init-syntax.stderr b/src/test/ui/struct-field-init-syntax.stderr new file mode 100644 index 0000000000000..0bca3f83eb1a3 --- /dev/null +++ b/src/test/ui/struct-field-init-syntax.stderr @@ -0,0 +1,18 @@ +error: cannot use a comma after the base struct + --> $DIR/struct-field-init-syntax.rs:18:9 + | +18 | ..Foo::default(), + | ^^^^^^^^^^^^^^^^- help: remove this comma + | + = note: the base struct must always be the last field + +error: cannot use a comma after the base struct + --> $DIR/struct-field-init-syntax.rs:23:9 + | +23 | ..Foo::default(), + | ^^^^^^^^^^^^^^^^- help: remove this comma + | + = note: the base struct must always be the last field + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/suggestions/auxiliary/m1.rs b/src/test/ui/suggestions/auxiliary/m1.rs new file mode 100644 index 0000000000000..b61667cfd882c --- /dev/null +++ b/src/test/ui/suggestions/auxiliary/m1.rs @@ -0,0 +1,11 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn foo() {} diff --git a/src/test/ui/suggestions/auxiliary/m2.rs b/src/test/ui/suggestions/auxiliary/m2.rs new file mode 100644 index 0000000000000..94ff5e4497fe9 --- /dev/null +++ b/src/test/ui/suggestions/auxiliary/m2.rs @@ -0,0 +1,11 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn bar() {} diff --git a/src/test/ui/suggestions/closure-immutable-outer-variable.rs b/src/test/ui/suggestions/closure-immutable-outer-variable.rs new file mode 100644 index 0000000000000..1d14afd6a01ac --- /dev/null +++ b/src/test/ui/suggestions/closure-immutable-outer-variable.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Point at the captured immutable outer variable + +fn foo(mut f: Box) { + f(); +} + +fn main() { + let y = true; + foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable +} diff --git a/src/test/ui/suggestions/closure-immutable-outer-variable.stderr b/src/test/ui/suggestions/closure-immutable-outer-variable.stderr new file mode 100644 index 0000000000000..f272a3582c619 --- /dev/null +++ b/src/test/ui/suggestions/closure-immutable-outer-variable.stderr @@ -0,0 +1,10 @@ +error[E0594]: cannot assign to captured outer variable in an `FnMut` closure + --> $DIR/closure-immutable-outer-variable.rs:19:26 + | +18 | let y = true; + | - help: consider making `y` mutable: `mut y` +19 | foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-18343.rs b/src/test/ui/suggestions/confuse-field-and-method/issue-18343.rs index fc3c58e5223a3..6e0f9999ec1d2 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-18343.rs +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-18343.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Obj where F: FnMut() -> u32 { +struct Obj where F: FnMut() -> u32 { //~ NOTE not found for this closure: F, } diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-18343.stderr b/src/test/ui/suggestions/confuse-field-and-method/issue-18343.stderr index 0ca61127634bc..62dceceedf388 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-18343.stderr +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-18343.stderr @@ -1,6 +1,9 @@ error[E0599]: no method named `closure` found for type `Obj<[closure@$DIR/issue-18343.rs:16:28: 16:33]>` in the current scope --> $DIR/issue-18343.rs:17:7 | +11 | struct Obj where F: FnMut() -> u32 { //~ NOTE not found for this + | ------------------------------------- method `closure` not found for this +... 17 | o.closure(); | ^^^^^^^ field, not a method | diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-2392.rs b/src/test/ui/suggestions/confuse-field-and-method/issue-2392.rs index f84f35ce84bf9..cba0ecbf58ec6 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-2392.rs +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-2392.rs @@ -13,6 +13,9 @@ use std::boxed::FnBox; struct FuncContainer { +//~^ NOTE not found for this +//~| NOTE not found for this +//~| NOTE not found for this f1: fn(data: u8), f2: extern "C" fn(data: u8), f3: unsafe fn(data: u8), @@ -23,11 +26,19 @@ struct FuncContainerOuter { } struct Obj where F: FnOnce() -> u32 { +//~^ NOTE not found for this +//~| NOTE not found for this +//~| NOTE not found for this +//~| NOTE not found for this +//~| NOTE not found for this +//~| NOTE not found for this closure: F, not_closure: usize, } struct BoxedObj { +//~^ NOTE not found for this +//~| NOTE not found for this boxed_closure: Box u32>, } diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-2392.stderr b/src/test/ui/suggestions/confuse-field-and-method/issue-2392.stderr index c6f134f118db7..b4086b1602701 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-2392.stderr +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-2392.stderr @@ -1,87 +1,120 @@ -error[E0599]: no method named `closure` found for type `Obj<[closure@$DIR/issue-2392.rs:49:36: 49:41]>` in the current scope - --> $DIR/issue-2392.rs:50:15 +error[E0599]: no method named `closure` found for type `Obj<[closure@$DIR/issue-2392.rs:60:36: 60:41]>` in the current scope + --> $DIR/issue-2392.rs:61:15 | -50 | o_closure.closure(); //~ ERROR no method named `closure` found +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `closure` not found for this +... +61 | o_closure.closure(); //~ ERROR no method named `closure` found | ^^^^^^^ field, not a method | = help: use `(o_closure.closure)(...)` if you meant to call the function stored in the `closure` field -error[E0599]: no method named `not_closure` found for type `Obj<[closure@$DIR/issue-2392.rs:49:36: 49:41]>` in the current scope - --> $DIR/issue-2392.rs:54:15 +error[E0599]: no method named `not_closure` found for type `Obj<[closure@$DIR/issue-2392.rs:60:36: 60:41]>` in the current scope + --> $DIR/issue-2392.rs:65:15 | -54 | o_closure.not_closure(); +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `not_closure` not found for this +... +65 | o_closure.not_closure(); | ^^^^^^^^^^^ field, not a method | = help: did you mean to write `o_closure.not_closure` instead of `o_closure.not_closure(...)`? error[E0599]: no method named `closure` found for type `Obj u32 {func}>` in the current scope - --> $DIR/issue-2392.rs:60:12 + --> $DIR/issue-2392.rs:71:12 | -60 | o_func.closure(); //~ ERROR no method named `closure` found +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `closure` not found for this +... +71 | o_func.closure(); //~ ERROR no method named `closure` found | ^^^^^^^ field, not a method | = help: use `(o_func.closure)(...)` if you meant to call the function stored in the `closure` field error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope - --> $DIR/issue-2392.rs:65:14 + --> $DIR/issue-2392.rs:76:14 | -65 | boxed_fn.boxed_closure();//~ ERROR no method named `boxed_closure` found +39 | struct BoxedObj { + | --------------- method `boxed_closure` not found for this +... +76 | boxed_fn.boxed_closure();//~ ERROR no method named `boxed_closure` found | ^^^^^^^^^^^^^ field, not a method | = help: use `(boxed_fn.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope - --> $DIR/issue-2392.rs:70:19 + --> $DIR/issue-2392.rs:81:19 | -70 | boxed_closure.boxed_closure();//~ ERROR no method named `boxed_closure` found +39 | struct BoxedObj { + | --------------- method `boxed_closure` not found for this +... +81 | boxed_closure.boxed_closure();//~ ERROR no method named `boxed_closure` found | ^^^^^^^^^^^^^ field, not a method | = help: use `(boxed_closure.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field error[E0599]: no method named `closure` found for type `Obj u32 {func}>` in the current scope - --> $DIR/issue-2392.rs:77:12 + --> $DIR/issue-2392.rs:88:12 | -77 | w.wrap.closure();//~ ERROR no method named `closure` found +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `closure` not found for this +... +88 | w.wrap.closure();//~ ERROR no method named `closure` found | ^^^^^^^ field, not a method | = help: use `(w.wrap.closure)(...)` if you meant to call the function stored in the `closure` field error[E0599]: no method named `not_closure` found for type `Obj u32 {func}>` in the current scope - --> $DIR/issue-2392.rs:81:12 + --> $DIR/issue-2392.rs:92:12 | -81 | w.wrap.not_closure(); +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `not_closure` not found for this +... +92 | w.wrap.not_closure(); | ^^^^^^^^^^^ field, not a method | = help: did you mean to write `w.wrap.not_closure` instead of `w.wrap.not_closure(...)`? error[E0599]: no method named `closure` found for type `Obj + 'static>>` in the current scope - --> $DIR/issue-2392.rs:86:24 + --> $DIR/issue-2392.rs:97:24 | -86 | check_expression().closure();//~ ERROR no method named `closure` found +28 | struct Obj where F: FnOnce() -> u32 { + | -------------------------------------- method `closure` not found for this +... +97 | check_expression().closure();//~ ERROR no method named `closure` found | ^^^^^^^ field, not a method | = help: use `(check_expression().closure)(...)` if you meant to call the function stored in the `closure` field error[E0599]: no method named `f1` found for type `FuncContainer` in the current scope - --> $DIR/issue-2392.rs:94:31 - | -94 | (*self.container).f1(1); //~ ERROR no method named `f1` found - | ^^ field, not a method - | - = help: use `((*self.container).f1)(...)` if you meant to call the function stored in the `f1` field + --> $DIR/issue-2392.rs:105:31 + | +15 | struct FuncContainer { + | -------------------- method `f1` not found for this +... +105 | (*self.container).f1(1); //~ ERROR no method named `f1` found + | ^^ field, not a method + | + = help: use `((*self.container).f1)(...)` if you meant to call the function stored in the `f1` field error[E0599]: no method named `f2` found for type `FuncContainer` in the current scope - --> $DIR/issue-2392.rs:97:31 - | -97 | (*self.container).f2(1); //~ ERROR no method named `f2` found - | ^^ field, not a method - | - = help: use `((*self.container).f2)(...)` if you meant to call the function stored in the `f2` field + --> $DIR/issue-2392.rs:108:31 + | +15 | struct FuncContainer { + | -------------------- method `f2` not found for this +... +108 | (*self.container).f2(1); //~ ERROR no method named `f2` found + | ^^ field, not a method + | + = help: use `((*self.container).f2)(...)` if you meant to call the function stored in the `f2` field error[E0599]: no method named `f3` found for type `FuncContainer` in the current scope - --> $DIR/issue-2392.rs:100:31 + --> $DIR/issue-2392.rs:111:31 | -100 | (*self.container).f3(1); //~ ERROR no method named `f3` found +15 | struct FuncContainer { + | -------------------- method `f3` not found for this +... +111 | (*self.container).f3(1); //~ ERROR no method named `f3` found | ^^ field, not a method | = help: use `((*self.container).f3)(...)` if you meant to call the function stored in the `f3` field diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-32128.rs b/src/test/ui/suggestions/confuse-field-and-method/issue-32128.rs index 2fd7dc246c206..32d5c7f5e8aef 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-32128.rs +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-32128.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Example { +struct Example { //~ NOTE not found for this example: Box i32> } diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-32128.stderr b/src/test/ui/suggestions/confuse-field-and-method/issue-32128.stderr index a45e70d48c99e..813b6060db072 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-32128.stderr +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-32128.stderr @@ -1,6 +1,9 @@ error[E0599]: no method named `example` found for type `Example` in the current scope --> $DIR/issue-32128.rs:22:10 | +11 | struct Example { //~ NOTE not found for this + | -------------- method `example` not found for this +... 22 | demo.example(1); | ^^^^^^^ field, not a method | diff --git a/src/test/ui/suggestions/confuse-field-and-method/issue-33784.rs b/src/test/ui/suggestions/confuse-field-and-method/issue-33784.rs index 03c84fc57befe..8734985522191 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/issue-33784.rs +++ b/src/test/ui/suggestions/confuse-field-and-method/issue-33784.rs @@ -36,14 +36,14 @@ fn main() { let p = &o; p.closure(); //~ ERROR no method named `closure` found //~^ HELP use `(p.closure)(...)` if you meant to call the function stored in the `closure` field - //~| NOTE `closure` is a field storing a function, not a method + //~| NOTE field, not a method let q = &p; q.fn_ptr(); //~ ERROR no method named `fn_ptr` found //~^ HELP use `(q.fn_ptr)(...)` if you meant to call the function stored in the `fn_ptr` field - //~| NOTE `fn_ptr` is a field storing a function, not a method + //~| NOTE field, not a method let r = D(C { c_fn_ptr: empty }); let s = &r; s.c_fn_ptr(); //~ ERROR no method named `c_fn_ptr` found //~^ HELP use `(s.c_fn_ptr)(...)` if you meant to call the function stored in the `c_fn_ptr` - //~| NOTE `c_fn_ptr` is a field storing a function, not a method + //~| NOTE field, not a method } diff --git a/src/test/ui/suggestions/confuse-field-and-method/private-field.rs b/src/test/ui/suggestions/confuse-field-and-method/private-field.rs index 94cf38fb32f2f..4cf939bbed6f5 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/private-field.rs +++ b/src/test/ui/suggestions/confuse-field-and-method/private-field.rs @@ -23,7 +23,7 @@ pub mod animal { fn main() { let dog = animal::Dog::new(3); - let dog_age = dog.dog_age(); + let dog_age = dog.dog_age(); //~ ERROR no method //let dog_age = dog.dog_age; println!("{}", dog_age); } diff --git a/src/test/ui/suggestions/confuse-field-and-method/private-field.stderr b/src/test/ui/suggestions/confuse-field-and-method/private-field.stderr index 9451926626028..caf78af6eb9f9 100644 --- a/src/test/ui/suggestions/confuse-field-and-method/private-field.stderr +++ b/src/test/ui/suggestions/confuse-field-and-method/private-field.stderr @@ -1,7 +1,10 @@ error[E0599]: no method named `dog_age` found for type `animal::Dog` in the current scope --> $DIR/private-field.rs:26:23 | -26 | let dog_age = dog.dog_age(); +12 | pub struct Dog { + | -------------- method `dog_age` not found for this +... +26 | let dog_age = dog.dog_age(); //~ ERROR no method | ^^^^^^^ private field, not a method error: aborting due to previous error diff --git a/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs new file mode 100644 index 0000000000000..72269768e0f5c --- /dev/null +++ b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo(s: &str) -> bool { true } + +fn main() { + let x = vec![(String::new(), String::new())]; + x.iter() + .filter(|&(ref a, _)| foo(a)) + //~^ ERROR non-reference pattern used to match a reference + //~| HELP consider using a reference + //~| HELP add + .collect(); +} diff --git a/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr new file mode 100644 index 0000000000000..799d9080b9d18 --- /dev/null +++ b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr @@ -0,0 +1,10 @@ +error: non-reference pattern used to match a reference (see issue #42640) + --> $DIR/dont-suggest-dereference-on-arg.rs:16:18 + | +16 | .filter(|&(ref a, _)| foo(a)) + | ^^^^^^^^^^^ help: consider using a reference: `&&(ref a, _)` + | + = help: add #![feature(match_default_bindings)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/extern-crate-rename.rs b/src/test/ui/suggestions/extern-crate-rename.rs new file mode 100644 index 0000000000000..b58149fb0b8dc --- /dev/null +++ b/src/test/ui/suggestions/extern-crate-rename.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:m1.rs +// aux-build:m2.rs + + +extern crate m1; +extern crate m2 as m1; //~ ERROR is defined multiple times + +fn main() {} diff --git a/src/test/ui/suggestions/extern-crate-rename.stderr b/src/test/ui/suggestions/extern-crate-rename.stderr new file mode 100644 index 0000000000000..6268935b08cca --- /dev/null +++ b/src/test/ui/suggestions/extern-crate-rename.stderr @@ -0,0 +1,15 @@ +error[E0259]: the name `m1` is defined multiple times + --> $DIR/extern-crate-rename.rs:16:1 + | +15 | extern crate m1; + | ---------------- previous import of the extern crate `m1` here +16 | extern crate m2 as m1; //~ ERROR is defined multiple times + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | `m1` reimported here + | You can use `as` to change the binding name of the import + | + = note: `m1` must be defined only once in the type namespace of this module + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/issue-32354-suggest-import-rename.rs b/src/test/ui/suggestions/issue-32354-suggest-import-rename.rs new file mode 100644 index 0000000000000..9d71ab1a788eb --- /dev/null +++ b/src/test/ui/suggestions/issue-32354-suggest-import-rename.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod extension1 { + pub trait ConstructorExtension {} +} + +pub mod extension2 { + pub trait ConstructorExtension {} +} + +use extension1::ConstructorExtension; +use extension2::ConstructorExtension; //~ ERROR is defined multiple times + +fn main() {} diff --git a/src/test/ui/suggestions/issue-32354-suggest-import-rename.stderr b/src/test/ui/suggestions/issue-32354-suggest-import-rename.stderr new file mode 100644 index 0000000000000..ae892db364ba7 --- /dev/null +++ b/src/test/ui/suggestions/issue-32354-suggest-import-rename.stderr @@ -0,0 +1,16 @@ +error[E0252]: the name `ConstructorExtension` is defined multiple times + --> $DIR/issue-32354-suggest-import-rename.rs:20:5 + | +19 | use extension1::ConstructorExtension; + | -------------------------------- previous import of the trait `ConstructorExtension` here +20 | use extension2::ConstructorExtension; //~ ERROR is defined multiple times + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ConstructorExtension` reimported here + | + = note: `ConstructorExtension` must be defined only once in the type namespace of this module +help: You can use `as` to change the binding name of the import + | +20 | use extension2::ConstructorExtension as OtherConstructorExtension; //~ ERROR is defined multiple times + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/issue-43420-no-over-suggest.rs b/src/test/ui/suggestions/issue-43420-no-over-suggest.rs index d504b7cae28c4..8c5bde45baed0 100644 --- a/src/test/ui/suggestions/issue-43420-no-over-suggest.rs +++ b/src/test/ui/suggestions/issue-43420-no-over-suggest.rs @@ -15,5 +15,5 @@ fn foo(b: &[u16]) {} fn main() { let a: Vec = Vec::new(); - foo(&a); + foo(&a); //~ ERROR mismatched types } diff --git a/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr b/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr index bcad9ce56c65e..c3f64fef50c66 100644 --- a/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr +++ b/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/issue-43420-no-over-suggest.rs:18:9 | -18 | foo(&a); +18 | foo(&a); //~ ERROR mismatched types | ^^ expected slice, found struct `std::vec::Vec` | = note: expected type `&[u16]` diff --git a/src/test/ui/suggestions/pub-ident-fn-2.rs b/src/test/ui/suggestions/pub-ident-fn-2.rs new file mode 100644 index 0000000000000..44884bfcdfdce --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-2.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub foo(s: usize) { bar() } +//~^ ERROR missing `fn` for method definition + +fn main() { + foo(2); +} diff --git a/src/test/ui/suggestions/pub-ident-fn-2.stderr b/src/test/ui/suggestions/pub-ident-fn-2.stderr new file mode 100644 index 0000000000000..7d3abceb11b58 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-2.stderr @@ -0,0 +1,12 @@ +error: missing `fn` for method definition + --> $DIR/pub-ident-fn-2.rs:11:4 + | +11 | pub foo(s: usize) { bar() } + | ^ +help: add `fn` here to parse `foo` as a public method + | +11 | pub fn foo(s: usize) { bar() } + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/pub-ident-fn-or-struct-2.rs b/src/test/ui/suggestions/pub-ident-fn-or-struct-2.rs new file mode 100644 index 0000000000000..1ccadc8a40b72 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-or-struct-2.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub S(); +//~^ ERROR missing `fn` or `struct` for method or struct definition + +fn main() {} diff --git a/src/test/ui/suggestions/pub-ident-fn-or-struct-2.stderr b/src/test/ui/suggestions/pub-ident-fn-or-struct-2.stderr new file mode 100644 index 0000000000000..68dea2aec3a54 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-or-struct-2.stderr @@ -0,0 +1,8 @@ +error: missing `fn` or `struct` for method or struct definition + --> $DIR/pub-ident-fn-or-struct-2.rs:11:4 + | +11 | pub S(); + | ---^- help: if you meant to call a macro, write instead: `S!` + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/pub-ident-fn-or-struct.rs b/src/test/ui/suggestions/pub-ident-fn-or-struct.rs new file mode 100644 index 0000000000000..0664918945b43 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-or-struct.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub S (foo) bar +//~^ ERROR missing `fn` or `struct` for method or struct definition + +fn main() {} diff --git a/src/test/ui/suggestions/pub-ident-fn-or-struct.stderr b/src/test/ui/suggestions/pub-ident-fn-or-struct.stderr new file mode 100644 index 0000000000000..0c19f776bd18e --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn-or-struct.stderr @@ -0,0 +1,8 @@ +error: missing `fn` or `struct` for method or struct definition + --> $DIR/pub-ident-fn-or-struct.rs:11:4 + | +11 | pub S (foo) bar + | ---^- help: if you meant to call a macro, write instead: `S!` + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/pub-ident-fn.rs b/src/test/ui/suggestions/pub-ident-fn.rs new file mode 100644 index 0000000000000..1d64199642093 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub foo(s: usize) -> bool { true } +//~^ ERROR missing `fn` for method definition + +fn main() { + foo(2); +} diff --git a/src/test/ui/suggestions/pub-ident-fn.stderr b/src/test/ui/suggestions/pub-ident-fn.stderr new file mode 100644 index 0000000000000..d36b9b127e0c1 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-fn.stderr @@ -0,0 +1,12 @@ +error: missing `fn` for method definition + --> $DIR/pub-ident-fn.rs:11:4 + | +11 | pub foo(s: usize) -> bool { true } + | ^^^ +help: add `fn` here to parse `foo` as a public method + | +11 | pub fn foo(s: usize) -> bool { true } + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/pub-ident-struct.rs b/src/test/ui/suggestions/pub-ident-struct.rs new file mode 100644 index 0000000000000..d08d498f87a01 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-struct.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub S { +//~^ ERROR missing `struct` for struct definition +} +fn main() {} diff --git a/src/test/ui/suggestions/pub-ident-struct.stderr b/src/test/ui/suggestions/pub-ident-struct.stderr new file mode 100644 index 0000000000000..36ef307272231 --- /dev/null +++ b/src/test/ui/suggestions/pub-ident-struct.stderr @@ -0,0 +1,12 @@ +error: missing `struct` for struct definition + --> $DIR/pub-ident-struct.rs:11:4 + | +11 | pub S { + | ^ +help: add `struct` here to parse `S` as a public struct + | +11 | pub struct S { + | ^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/str-array-assignment.rs b/src/test/ui/suggestions/str-array-assignment.rs new file mode 100644 index 0000000000000..444684507d387 --- /dev/null +++ b/src/test/ui/suggestions/str-array-assignment.rs @@ -0,0 +1,32 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { //~ NOTE expected `()` because of default return type + let s = "abc"; + let t = if true { s[..2] } else { s }; + //~^ ERROR if and else have incompatible types + //~| NOTE expected str, found &str + //~| NOTE expected type + let u: &str = if true { s[..2] } else { s }; + //~^ ERROR mismatched types + //~| NOTE expected &str, found str + //~| NOTE expected type + let v = s[..2]; + //~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied + //~| HELP consider borrowing here + //~| NOTE `str` does not have a constant size known at compile-time + //~| HELP the trait `std::marker::Sized` is not implemented for `str` + //~| NOTE all local variables must have a statically known size + let w: &str = s[..2]; + //~^ ERROR mismatched types + //~| NOTE expected &str, found str + //~| NOTE expected type + //~| HELP consider borrowing here +} diff --git a/src/test/ui/suggestions/str-array-assignment.stderr b/src/test/ui/suggestions/str-array-assignment.stderr new file mode 100644 index 0000000000000..c65639805af6c --- /dev/null +++ b/src/test/ui/suggestions/str-array-assignment.stderr @@ -0,0 +1,46 @@ +error[E0308]: if and else have incompatible types + --> $DIR/str-array-assignment.rs:13:11 + | +13 | let t = if true { s[..2] } else { s }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found &str + | + = note: expected type `str` + found type `&str` + +error[E0308]: mismatched types + --> $DIR/str-array-assignment.rs:17:27 + | +11 | fn main() { //~ NOTE expected `()` because of default return type + | - expected `()` because of default return type +... +17 | let u: &str = if true { s[..2] } else { s }; + | ^^^^^^ expected &str, found str + | + = note: expected type `&str` + found type `str` + +error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied + --> $DIR/str-array-assignment.rs:21:7 + | +21 | let v = s[..2]; + | ^ ------ help: consider borrowing here: `&s[..2]` + | | + | `str` does not have a constant size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: all local variables must have a statically known size + +error[E0308]: mismatched types + --> $DIR/str-array-assignment.rs:27:17 + | +27 | let w: &str = s[..2]; + | ^^^^^^ + | | + | expected &str, found str + | help: consider borrowing here: `&s[..2]` + | + = note: expected type `&str` + found type `str` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/suggestions/suggest-labels.rs b/src/test/ui/suggestions/suggest-labels.rs new file mode 100644 index 0000000000000..8c97301f40b91 --- /dev/null +++ b/src/test/ui/suggestions/suggest-labels.rs @@ -0,0 +1,26 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[allow(unreachable_code)] +fn main() { + 'foo: loop { + break 'fo; //~ ERROR use of undeclared label + } + + 'bar: loop { + continue 'bor; //~ ERROR use of undeclared label + } + + 'longlabel: loop { + 'longlabel1: loop { + break 'longlable; //~ ERROR use of undeclared label + } + } +} diff --git a/src/test/ui/suggestions/suggest-labels.stderr b/src/test/ui/suggestions/suggest-labels.stderr new file mode 100644 index 0000000000000..c82b5decfd839 --- /dev/null +++ b/src/test/ui/suggestions/suggest-labels.stderr @@ -0,0 +1,20 @@ +error[E0426]: use of undeclared label `'fo` + --> $DIR/suggest-labels.rs:14:15 + | +14 | break 'fo; //~ ERROR use of undeclared label + | ^^^ did you mean `'foo`? + +error[E0426]: use of undeclared label `'bor` + --> $DIR/suggest-labels.rs:18:18 + | +18 | continue 'bor; //~ ERROR use of undeclared label + | ^^^^ did you mean `'bar`? + +error[E0426]: use of undeclared label `'longlable` + --> $DIR/suggest-labels.rs:23:19 + | +23 | break 'longlable; //~ ERROR use of undeclared label + | ^^^^^^^^^^ did you mean `'longlabel1`? + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/suggestions/suggest-methods.rs b/src/test/ui/suggestions/suggest-methods.rs index b02881dc7eee0..49027deecc198 100644 --- a/src/test/ui/suggestions/suggest-methods.rs +++ b/src/test/ui/suggestions/suggest-methods.rs @@ -25,16 +25,16 @@ impl FooT for Foo { fn main() { let f = Foo; - f.bat(1.0); + f.bat(1.0); //~ ERROR no method named let s = "foo".to_string(); - let _ = s.is_emtpy(); + let _ = s.is_emtpy(); //~ ERROR no method named // Generates a warning for `count_zeros()`. `count_ones()` is also a close // match, but the former is closer. - let _ = 63u32.count_eos(); + let _ = 63u32.count_eos(); //~ ERROR no method named // Does not generate a warning - let _ = 63u32.count_o(); + let _ = 63u32.count_o(); //~ ERROR no method named } diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index 41beb73b1bc35..d3d8d302c70c4 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -1,7 +1,10 @@ error[E0599]: no method named `bat` found for type `Foo` in the current scope --> $DIR/suggest-methods.rs:28:7 | -28 | f.bat(1.0); +11 | struct Foo; + | ----------- method `bat` not found for this +... +28 | f.bat(1.0); //~ ERROR no method named | ^^^ | = help: did you mean `bar`? @@ -9,7 +12,7 @@ error[E0599]: no method named `bat` found for type `Foo` in the current scope error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope --> $DIR/suggest-methods.rs:31:15 | -31 | let _ = s.is_emtpy(); +31 | let _ = s.is_emtpy(); //~ ERROR no method named | ^^^^^^^^ | = help: did you mean `is_empty`? @@ -17,7 +20,7 @@ error[E0599]: no method named `is_emtpy` found for type `std::string::String` in error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:35:19 | -35 | let _ = 63u32.count_eos(); +35 | let _ = 63u32.count_eos(); //~ ERROR no method named | ^^^^^^^^^ | = help: did you mean `count_zeros`? @@ -25,7 +28,7 @@ error[E0599]: no method named `count_eos` found for type `u32` in the current sc error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:38:19 | -38 | let _ = 63u32.count_o(); +38 | let _ = 63u32.count_o(); //~ ERROR no method named | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/suggestions/try-on-option.rs b/src/test/ui/suggestions/try-on-option.rs new file mode 100644 index 0000000000000..65ca23402d27e --- /dev/null +++ b/src/test/ui/suggestions/try-on-option.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(try_trait)] + +fn main() {} + +fn foo() -> Result { + let x: Option = None; + x?; //~ the trait bound + Ok(22) +} + +fn bar() -> u32 { + let x: Option = None; + x?; //~ the `?` operator + 22 +} diff --git a/src/test/ui/suggestions/try-on-option.stderr b/src/test/ui/suggestions/try-on-option.stderr new file mode 100644 index 0000000000000..b1be9ad3cf697 --- /dev/null +++ b/src/test/ui/suggestions/try-on-option.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `(): std::convert::From` is not satisfied + --> $DIR/try-on-option.rs:17:5 + | +17 | x?; //~ the trait bound + | ^^ the trait `std::convert::From` is not implemented for `()` + | + = note: required by `std::convert::From::from` + +error[E0277]: the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`) + --> $DIR/try-on-option.rs:23:5 + | +23 | x?; //~ the `?` operator + | -- + | | + | cannot use the `?` operator in a function that returns `u32` + | in this macro invocation + | + = help: the trait `std::ops::Try` is not implemented for `u32` + = note: required by `std::ops::Try::from_error` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/suggestions/try-operator-on-main.rs b/src/test/ui/suggestions/try-operator-on-main.rs index eadd12924df66..e9d285941b762 100644 --- a/src/test/ui/suggestions/try-operator-on-main.rs +++ b/src/test/ui/suggestions/try-operator-on-main.rs @@ -14,20 +14,20 @@ use std::ops::Try; fn main() { // error for a `Try` type on a non-`Try` fn - std::fs::File::open("foo")?; + std::fs::File::open("foo")?; //~ ERROR the `?` operator can only // a non-`Try` type on a non-`Try` fn - ()?; + ()?; //~ ERROR the `?` operator can only // an unrelated use of `Try` - try_trait_generic::<()>(); + try_trait_generic::<()>(); //~ ERROR the trait bound } fn try_trait_generic() -> T { // and a non-`Try` object on a `Try` fn. - ()?; + ()?; //~ ERROR the `?` operator can only loop {} } diff --git a/src/test/ui/suggestions/try-operator-on-main.stderr b/src/test/ui/suggestions/try-operator-on-main.stderr index e83bf2abc1504..8b17e06065b5e 100644 --- a/src/test/ui/suggestions/try-operator-on-main.stderr +++ b/src/test/ui/suggestions/try-operator-on-main.stderr @@ -1,7 +1,7 @@ error[E0277]: the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`) --> $DIR/try-operator-on-main.rs:17:5 | -17 | std::fs::File::open("foo")?; +17 | std::fs::File::open("foo")?; //~ ERROR the `?` operator can only | --------------------------- | | | cannot use the `?` operator in a function that returns `()` @@ -13,7 +13,7 @@ error[E0277]: the `?` operator can only be used in a function that returns `Resu error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` --> $DIR/try-operator-on-main.rs:20:5 | -20 | ()?; +20 | ()?; //~ ERROR the `?` operator can only | --- | | | the `?` operator cannot be applied to type `()` @@ -25,7 +25,7 @@ error[E0277]: the `?` operator can only be applied to values that implement `std error[E0277]: the trait bound `(): std::ops::Try` is not satisfied --> $DIR/try-operator-on-main.rs:23:5 | -23 | try_trait_generic::<()>(); +23 | try_trait_generic::<()>(); //~ ERROR the trait bound | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Try` is not implemented for `()` | = note: required by `try_trait_generic` @@ -33,7 +33,7 @@ error[E0277]: the trait bound `(): std::ops::Try` is not satisfied error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` --> $DIR/try-operator-on-main.rs:30:5 | -30 | ()?; +30 | ()?; //~ ERROR the `?` operator can only | --- | | | the `?` operator cannot be applied to type `()` diff --git a/src/test/ui/suggestions/tuple-float-index.rs b/src/test/ui/suggestions/tuple-float-index.rs index 8bfbd0e74db22..0a188305a9228 100644 --- a/src/test/ui/suggestions/tuple-float-index.rs +++ b/src/test/ui/suggestions/tuple-float-index.rs @@ -11,5 +11,5 @@ // compile-flags: -Z parse-only fn main () { - (1, (2, 3)).1.1; + (1, (2, 3)).1.1; //~ ERROR unexpected token: `1.1` } diff --git a/src/test/ui/suggestions/tuple-float-index.stderr b/src/test/ui/suggestions/tuple-float-index.stderr index 4b1be26c86b0e..7d133f11cbb60 100644 --- a/src/test/ui/suggestions/tuple-float-index.stderr +++ b/src/test/ui/suggestions/tuple-float-index.stderr @@ -1,7 +1,7 @@ error: unexpected token: `1.1` --> $DIR/tuple-float-index.rs:14:17 | -14 | (1, (2, 3)).1.1; +14 | (1, (2, 3)).1.1; //~ ERROR unexpected token: `1.1` | ------------^^^ | | | | | unexpected token diff --git a/src/test/ui/suggestions/type-ascription-instead-of-initializer.rs b/src/test/ui/suggestions/type-ascription-instead-of-initializer.rs new file mode 100644 index 0000000000000..d80dad8fbd4c2 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-initializer.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x: Vec::with_capacity(10, 20); //~ ERROR expected type, found `10` + //~^ ERROR this function takes 1 parameter +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-initializer.stderr b/src/test/ui/suggestions/type-ascription-instead-of-initializer.stderr new file mode 100644 index 0000000000000..c3e8d7fcd61ac --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-initializer.stderr @@ -0,0 +1,17 @@ +error: expected type, found `10` + --> $DIR/type-ascription-instead-of-initializer.rs:12:31 + | +12 | let x: Vec::with_capacity(10, 20); //~ ERROR expected type, found `10` + | -- ^^ + | || + | |help: use `=` if you meant to assign + | while parsing the type for `x` + +error[E0061]: this function takes 1 parameter but 2 parameters were supplied + --> $DIR/type-ascription-instead-of-initializer.rs:12:31 + | +12 | let x: Vec::with_capacity(10, 20); //~ ERROR expected type, found `10` + | ^^^^^^ expected 1 parameter + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/suggestions/type-ascription-instead-of-statement-end.rs b/src/test/ui/suggestions/type-ascription-instead-of-statement-end.rs index 93de55a39e954..01d773dd5e103 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-statement-end.rs +++ b/src/test/ui/suggestions/type-ascription-instead-of-statement-end.rs @@ -12,9 +12,9 @@ fn main() { println!("test"): - 0; + 0; //~ ERROR expected type, found `0` } fn foo() { - println!("test"): 0; + println!("test"): 0; //~ ERROR expected type, found `0` } diff --git a/src/test/ui/suggestions/type-ascription-instead-of-statement-end.stderr b/src/test/ui/suggestions/type-ascription-instead-of-statement-end.stderr index 550048c7b88f1..9d26d00467480 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-statement-end.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-statement-end.stderr @@ -3,13 +3,13 @@ error: expected type, found `0` | 14 | println!("test"): | - help: did you mean to use `;` here? -15 | 0; +15 | 0; //~ ERROR expected type, found `0` | ^ expecting a type here because of type ascription error: expected type, found `0` --> $DIR/type-ascription-instead-of-statement-end.rs:19:23 | -19 | println!("test"): 0; +19 | println!("test"): 0; //~ ERROR expected type, found `0` | ^ expecting a type here because of type ascription error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/type-ascription-with-fn-call.rs b/src/test/ui/suggestions/type-ascription-with-fn-call.rs index 7c10bf98c8409..b2c25c37e8e32 100644 --- a/src/test/ui/suggestions/type-ascription-with-fn-call.rs +++ b/src/test/ui/suggestions/type-ascription-with-fn-call.rs @@ -12,7 +12,7 @@ fn main() { f() : - f(); + f(); //~ ERROR expected type, found function } fn f() {} diff --git a/src/test/ui/suggestions/type-ascription-with-fn-call.stderr b/src/test/ui/suggestions/type-ascription-with-fn-call.stderr index 93c65c263dd12..d5e0b00f3dff0 100644 --- a/src/test/ui/suggestions/type-ascription-with-fn-call.stderr +++ b/src/test/ui/suggestions/type-ascription-with-fn-call.stderr @@ -3,7 +3,7 @@ error[E0573]: expected type, found function `f` | 14 | f() : | - help: did you mean to use `;` here instead? -15 | f(); +15 | f(); //~ ERROR expected type, found function | ^^^ | | | not a type diff --git a/src/test/ui/token/bounds-obj-parens.rs b/src/test/ui/token/bounds-obj-parens.rs index 02c119cf727fe..9617df8fa21a9 100644 --- a/src/test/ui/token/bounds-obj-parens.rs +++ b/src/test/ui/token/bounds-obj-parens.rs @@ -14,4 +14,3 @@ type A = Box<(Fn(D::Error) -> E) + 'static + Send + Sync>; // OK (but see #39318 FAIL //~^ ERROR -//~| ERROR diff --git a/src/test/ui/token/issue-10636-2.rs b/src/test/ui/token/issue-10636-2.rs index 93759123618fb..c22baee680a02 100644 --- a/src/test/ui/token/issue-10636-2.rs +++ b/src/test/ui/token/issue-10636-2.rs @@ -15,6 +15,6 @@ pub fn trace_option(option: Option) { option.map(|some| 42; //~ NOTE: unclosed delimiter //~^ ERROR: expected one of //~| NOTE: expected one of - //~| NOTE: unexpected token + } //~ ERROR: incorrect close delimiter //~^ ERROR: expected expression, found `)` diff --git a/src/test/ui/token/issue-41155.rs b/src/test/ui/token/issue-41155.rs index 0f473c9e07388..550a90fc6af26 100644 --- a/src/test/ui/token/issue-41155.rs +++ b/src/test/ui/token/issue-41155.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -impl S { +impl S { //~ ERROR cannot find type pub -} +} //~ ERROR expected one of diff --git a/src/test/ui/token/issue-41155.stderr b/src/test/ui/token/issue-41155.stderr index 50a38da9d8c18..707784272ede8 100644 --- a/src/test/ui/token/issue-41155.stderr +++ b/src/test/ui/token/issue-41155.stderr @@ -3,13 +3,13 @@ error: expected one of `(`, `const`, `default`, `extern`, `fn`, `type`, or `unsa | 12 | pub | - expected one of 7 possible tokens here -13 | } +13 | } //~ ERROR expected one of | ^ unexpected token error[E0412]: cannot find type `S` in this scope --> $DIR/issue-41155.rs:11:6 | -11 | impl S { +11 | impl S { //~ ERROR cannot find type | ^ not found in this scope error[E0601]: main function not found diff --git a/src/test/ui/token/macro-incomplete-parse.rs b/src/test/ui/token/macro-incomplete-parse.rs index 08749373432f5..fd2561ce49653 100644 --- a/src/test/ui/token/macro-incomplete-parse.rs +++ b/src/test/ui/token/macro-incomplete-parse.rs @@ -21,7 +21,7 @@ macro_rules! ignored_item { macro_rules! ignored_expr { () => ( 1, //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `,` //~^ NOTE expected one of `.`, `;`, `?`, `}`, or an operator here - //~| NOTE unexpected token + 2 ) } diff --git a/src/test/ui/token/trailing-plus-in-bounds.rs b/src/test/ui/token/trailing-plus-in-bounds.rs index 2bb2c97790c12..72cae6abc2dbe 100644 --- a/src/test/ui/token/trailing-plus-in-bounds.rs +++ b/src/test/ui/token/trailing-plus-in-bounds.rs @@ -18,4 +18,3 @@ fn main() { FAIL //~^ ERROR -//~| ERROR diff --git a/src/test/ui/trait-method-private.rs b/src/test/ui/trait-method-private.rs index 5c1bd668ac649..54cf6e6783eb7 100644 --- a/src/test/ui/trait-method-private.rs +++ b/src/test/ui/trait-method-private.rs @@ -26,5 +26,5 @@ mod inner { fn main() { let foo = inner::Foo; - foo.method(); + foo.method(); //~ ERROR is private } diff --git a/src/test/ui/trait-method-private.stderr b/src/test/ui/trait-method-private.stderr index c7a7b689edc51..7406541a9da55 100644 --- a/src/test/ui/trait-method-private.stderr +++ b/src/test/ui/trait-method-private.stderr @@ -1,12 +1,14 @@ error[E0624]: method `method` is private --> $DIR/trait-method-private.rs:29:9 | -29 | foo.method(); +29 | foo.method(); //~ ERROR is private | ^^^^^^ | = help: items from traits can only be used if the trait is in scope - = note: the following trait is implemented but not in scope, perhaps add a `use` for it: - candidate #1: `use inner::Bar;` +help: the following trait is implemented but not in scope, perhaps add a `use` for it: + | +11 | use inner::Bar; + | error: aborting due to previous error diff --git a/src/test/ui/transmute/transmute-from-fn-item-types-error.rs b/src/test/ui/transmute/transmute-from-fn-item-types-error.rs index d60c97f1d59a8..0a0214a24ff2d 100644 --- a/src/test/ui/transmute/transmute-from-fn-item-types-error.rs +++ b/src/test/ui/transmute/transmute-from-fn-item-types-error.rs @@ -12,16 +12,16 @@ use std::mem; unsafe fn foo() -> (i8, *const (), Option) { let i = mem::transmute(bar); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR transmute called with types of different sizes + let p = mem::transmute(foo); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + let of = mem::transmute(main); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + (i, p, of) } @@ -30,15 +30,15 @@ unsafe fn bar() { // Error as usual if the resulting type is not pointer-sized. mem::transmute::<_, u8>(main); //~^ ERROR transmute called with types of different sizes - //~^^ NOTE transmuting between fn() {main} and u8 + mem::transmute::<_, *mut ()>(foo); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + mem::transmute::<_, fn()>(bar); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + // No error if a coercion would otherwise occur. mem::transmute::(main); @@ -46,16 +46,16 @@ unsafe fn bar() { unsafe fn baz() { mem::transmute::<_, *mut ()>(Some(foo)); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + mem::transmute::<_, fn()>(Some(bar)); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + mem::transmute::<_, Option>(Some(baz)); - //~^ ERROR is zero-sized and can't be transmuted - //~^^ NOTE cast with `as` to a pointer instead + //~^ ERROR can't transmute zero-sized type + // No error if a coercion would otherwise occur. mem::transmute::, usize>(Some(main)); diff --git a/src/test/ui/transmute/transmute-type-parameters.rs b/src/test/ui/transmute/transmute-type-parameters.rs index 117fc2cc5df24..fe340295f74c7 100644 --- a/src/test/ui/transmute/transmute-type-parameters.rs +++ b/src/test/ui/transmute/transmute-type-parameters.rs @@ -19,17 +19,17 @@ use std::mem::transmute; unsafe fn f(x: T) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: T (size can vary) to i32 +//~^ ERROR transmute called with types of different sizes } unsafe fn g(x: (T, i32)) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: (T, i32) (size can vary because of T) to i32 +//~^ ERROR transmute called with types of different sizes } unsafe fn h(x: [T; 10]) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: [T; 10] (size can vary because of T) to i32 +//~^ ERROR transmute called with types of different sizes } struct Bad { @@ -38,7 +38,7 @@ struct Bad { unsafe fn i(x: Bad) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: Bad (size can vary because of T) to i32 +//~^ ERROR transmute called with types of different sizes } enum Worse { @@ -48,12 +48,12 @@ enum Worse { unsafe fn j(x: Worse) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: Worse (size can vary because of T) to i32 +//~^ ERROR transmute called with types of different sizes } unsafe fn k(x: Option) { let _: i32 = transmute(x); -//~^ ERROR differently sized types: std::option::Option (size can vary because of T) to i32 +//~^ ERROR transmute called with types of different sizes } fn main() {} diff --git a/src/test/ui/type-check/assignment-in-if.rs b/src/test/ui/type-check/assignment-in-if.rs index 98dc55c666303..e4422f0b99aa4 100644 --- a/src/test/ui/type-check/assignment-in-if.rs +++ b/src/test/ui/type-check/assignment-in-if.rs @@ -24,25 +24,25 @@ fn main() { // `x { ... }` should not be interpreted as a struct literal here if x = x { //~^ ERROR mismatched types - //~| HELP did you mean to compare equality? + //~| HELP try comparing for equality println!("{}", x); } // Explicit parentheses on the left should match behavior of above if (x = x) { //~^ ERROR mismatched types - //~| HELP did you mean to compare equality? + //~| HELP try comparing for equality println!("{}", x); } // The struct literal interpretation is fine with explicit parentheses on the right if y = (Foo { foo: x }) { //~^ ERROR mismatched types - //~| HELP did you mean to compare equality? + //~| HELP try comparing for equality println!("{}", x); } // "invalid left-hand side expression" error is suppresed if 3 = x { //~^ ERROR mismatched types - //~| HELP did you mean to compare equality? + //~| HELP try comparing for equality println!("{}", x); } if (if true { x = 4 } else { x = 5 }) { diff --git a/src/test/ui/type-check/cannot_infer_local_or_array.rs b/src/test/ui/type-check/cannot_infer_local_or_array.rs index 0b35a9c3dbebc..3ec96144da544 100644 --- a/src/test/ui/type-check/cannot_infer_local_or_array.rs +++ b/src/test/ui/type-check/cannot_infer_local_or_array.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let x = []; + let x = []; //~ ERROR type annotations needed } diff --git a/src/test/ui/type-check/cannot_infer_local_or_array.stderr b/src/test/ui/type-check/cannot_infer_local_or_array.stderr index 8c52bb5a1d3a5..19369f5ca60fe 100644 --- a/src/test/ui/type-check/cannot_infer_local_or_array.stderr +++ b/src/test/ui/type-check/cannot_infer_local_or_array.stderr @@ -1,7 +1,7 @@ error[E0282]: type annotations needed --> $DIR/cannot_infer_local_or_array.rs:12:13 | -12 | let x = []; +12 | let x = []; //~ ERROR type annotations needed | - ^^ cannot infer type for `_` | | | consider giving `x` a type diff --git a/src/test/ui/type-check/cannot_infer_local_or_vec.stderr b/src/test/ui/type-check/cannot_infer_local_or_vec.stderr index 4788fad20889e..bbbcb9158ae5e 100644 --- a/src/test/ui/type-check/cannot_infer_local_or_vec.stderr +++ b/src/test/ui/type-check/cannot_infer_local_or_vec.stderr @@ -6,7 +6,7 @@ error[E0282]: type annotations needed | | | consider giving `x` a type | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/type-check/cannot_infer_local_or_vec_in_tuples.stderr b/src/test/ui/type-check/cannot_infer_local_or_vec_in_tuples.stderr index ccffadebe9ee2..6c47624d6dcfc 100644 --- a/src/test/ui/type-check/cannot_infer_local_or_vec_in_tuples.stderr +++ b/src/test/ui/type-check/cannot_infer_local_or_vec_in_tuples.stderr @@ -6,7 +6,7 @@ error[E0282]: type annotations needed | | | consider giving the pattern a type | - = note: this error originates in a macro outside of the current crate + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/type-check/issue-22897.rs b/src/test/ui/type-check/issue-22897.rs index 296dc81a89bcf..62eaa616d3199 100644 --- a/src/test/ui/type-check/issue-22897.rs +++ b/src/test/ui/type-check/issue-22897.rs @@ -11,5 +11,5 @@ fn main() { } fn unconstrained_type() { - []; + []; //~ ERROR type annotations needed } diff --git a/src/test/ui/type-check/issue-22897.stderr b/src/test/ui/type-check/issue-22897.stderr index 9568411885192..5ee350746bee8 100644 --- a/src/test/ui/type-check/issue-22897.stderr +++ b/src/test/ui/type-check/issue-22897.stderr @@ -1,7 +1,7 @@ error[E0282]: type annotations needed --> $DIR/issue-22897.rs:14:5 | -14 | []; +14 | []; //~ ERROR type annotations needed | ^^ cannot infer type for `[_; 0]` error: aborting due to previous error diff --git a/src/test/ui/type-check/issue-40294.rs b/src/test/ui/type-check/issue-40294.rs index d30a425d1099b..d2817062b27ff 100644 --- a/src/test/ui/type-check/issue-40294.rs +++ b/src/test/ui/type-check/issue-40294.rs @@ -12,7 +12,7 @@ trait Foo: Sized { fn foo(self); } -fn foo<'a,'b,T>(x: &'a T, y: &'b T) +fn foo<'a,'b,T>(x: &'a T, y: &'b T) //~ ERROR type annotations required where &'a T : Foo, &'b T : Foo { diff --git a/src/test/ui/type-check/issue-40294.stderr b/src/test/ui/type-check/issue-40294.stderr index bb3a371b26e0c..2ca97aa3ef067 100644 --- a/src/test/ui/type-check/issue-40294.stderr +++ b/src/test/ui/type-check/issue-40294.stderr @@ -1,7 +1,7 @@ error[E0283]: type annotations required: cannot resolve `&'a T: Foo` --> $DIR/issue-40294.rs:15:1 | -15 | / fn foo<'a,'b,T>(x: &'a T, y: &'b T) +15 | / fn foo<'a,'b,T>(x: &'a T, y: &'b T) //~ ERROR type annotations required 16 | | where &'a T : Foo, 17 | | &'b T : Foo 18 | | { diff --git a/src/test/ui/type-check/issue-41314.rs b/src/test/ui/type-check/issue-41314.rs index 5127a8ce17482..7b4d2f05f5fd0 100644 --- a/src/test/ui/type-check/issue-41314.rs +++ b/src/test/ui/type-check/issue-41314.rs @@ -14,6 +14,7 @@ enum X { fn main() { match X::Y(0) { - X::Y { number } => {} + X::Y { number } => {} //~ ERROR does not have a field named `number` + //~^ ERROR pattern does not mention field `0` } } diff --git a/src/test/ui/type-check/issue-41314.stderr b/src/test/ui/type-check/issue-41314.stderr index acae7a350877a..569924da6f446 100644 --- a/src/test/ui/type-check/issue-41314.stderr +++ b/src/test/ui/type-check/issue-41314.stderr @@ -1,13 +1,13 @@ error[E0026]: variant `X::Y` does not have a field named `number` --> $DIR/issue-41314.rs:17:16 | -17 | X::Y { number } => {} +17 | X::Y { number } => {} //~ ERROR does not have a field named `number` | ^^^^^^ variant `X::Y` does not have field `number` error[E0027]: pattern does not mention field `0` --> $DIR/issue-41314.rs:17:9 | -17 | X::Y { number } => {} +17 | X::Y { number } => {} //~ ERROR does not have a field named `number` | ^^^^^^^^^^^^^^^ missing field `0` | = note: trying to match a tuple variant with a struct variant pattern diff --git a/src/test/ui/type-check/missing_trait_impl.rs b/src/test/ui/type-check/missing_trait_impl.rs new file mode 100644 index 0000000000000..fe008db68a004 --- /dev/null +++ b/src/test/ui/type-check/missing_trait_impl.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { +} + +fn foo(x: T, y: T) { + let z = x + y; //~ ERROR binary operation `+` cannot be applied to type `T` + //~^ NOTE `T` might need a bound for `std::ops::Add` +} diff --git a/src/test/ui/type-check/missing_trait_impl.stderr b/src/test/ui/type-check/missing_trait_impl.stderr new file mode 100644 index 0000000000000..64f8167eb1d01 --- /dev/null +++ b/src/test/ui/type-check/missing_trait_impl.stderr @@ -0,0 +1,10 @@ +error[E0369]: binary operation `+` cannot be applied to type `T` + --> $DIR/missing_trait_impl.rs:15:13 + | +15 | let z = x + y; //~ ERROR binary operation `+` cannot be applied to type `T` + | ^^^^^ + | + = note: `T` might need a bound for `std::ops::Add` + +error: aborting due to previous error + diff --git a/src/test/ui/type-check/unknown_type_for_closure.rs b/src/test/ui/type-check/unknown_type_for_closure.rs index f1d357df12c43..0c355976330fd 100644 --- a/src/test/ui/type-check/unknown_type_for_closure.rs +++ b/src/test/ui/type-check/unknown_type_for_closure.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let x = |_| { }; + let x = |_| { }; //~ ERROR type annotations needed } diff --git a/src/test/ui/type-check/unknown_type_for_closure.stderr b/src/test/ui/type-check/unknown_type_for_closure.stderr index afbd15ca486bd..1d8c0ddc8b601 100644 --- a/src/test/ui/type-check/unknown_type_for_closure.stderr +++ b/src/test/ui/type-check/unknown_type_for_closure.stderr @@ -1,7 +1,7 @@ error[E0282]: type annotations needed --> $DIR/unknown_type_for_closure.rs:12:14 | -12 | let x = |_| { }; +12 | let x = |_| { }; //~ ERROR type annotations needed | ^ consider giving this closure parameter a type error: aborting due to previous error diff --git a/src/test/ui/unboxed-closure-no-cyclic-sig.rs b/src/test/ui/unboxed-closure-no-cyclic-sig.rs new file mode 100644 index 0000000000000..506bce0dbec21 --- /dev/null +++ b/src/test/ui/unboxed-closure-no-cyclic-sig.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that unboxed closures cannot capture their own type. +// +// Also regression test for issue #21410. + +fn g(_: F) where F: FnOnce(Option) {} + +fn main() { + g(|_| { }); //~ ERROR closure/generator type that references itself +} diff --git a/src/test/ui/unboxed-closure-no-cyclic-sig.stderr b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr new file mode 100644 index 0000000000000..75a87f70660d5 --- /dev/null +++ b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr @@ -0,0 +1,12 @@ +error[E0644]: closure/generator type that references itself + --> $DIR/unboxed-closure-no-cyclic-sig.rs:18:7 + | +18 | g(|_| { }); //~ ERROR closure/generator type that references itself + | ^^^^^^^^ cyclic type of infinite size + | + = note: closures cannot capture themselves or take themselves as argument; + this error may be the result of a recent compiler bug-fix, + see https://github.com/rust-lang/rust/issues/46062 for more details + +error: aborting due to previous error + diff --git a/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.rs b/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.rs new file mode 100644 index 0000000000000..481346ad4b6eb --- /dev/null +++ b/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.rs @@ -0,0 +1,26 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +fn foo(f: F) + where F: Fn() +{ +} + +fn main() { + // Test that this closure is inferred to `FnOnce` because it moves + // from `y.0`. This affects the error output (the error is that + // the closure implements `FnOnce`, not that it moves from inside + // a `Fn` closure.) + let y = (vec![1, 2, 3], 0); + let c = || drop(y.0); //~ ERROR expected a closure that implements the `Fn` trait + foo(c); +} diff --git a/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.stderr b/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.stderr new file mode 100644 index 0000000000000..d3d9ce2b34a1c --- /dev/null +++ b/src/test/ui/unboxed-closures-infer-fn-once-move-from-projection.stderr @@ -0,0 +1,16 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/unboxed-closures-infer-fn-once-move-from-projection.rs:24:13 + | +24 | let c = || drop(y.0); //~ ERROR expected a closure that implements the `Fn` trait + | ^^^^^^^^^^^^ +25 | foo(c); + | --- the requirement to implement `Fn` derives from here + | +note: closure is `FnOnce` because it moves the variable `y` out of its environment + --> $DIR/unboxed-closures-infer-fn-once-move-from-projection.rs:24:21 + | +24 | let c = || drop(y.0); //~ ERROR expected a closure that implements the `Fn` trait + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/union-fields.rs b/src/test/ui/union-fields.rs index 021f57e3eee0a..dc551bb899861 100644 --- a/src/test/ui/union-fields.rs +++ b/src/test/ui/union-fields.rs @@ -13,19 +13,19 @@ union U1 { a: u8, // should not be reported b: u8, // should not be reported - c: u8, // should be reported + c: u8, //~ ERROR field is never used } union U2 { - a: u8, // should be reported + a: u8, //~ ERROR field is never used b: u8, // should not be reported c: u8, // should not be reported } -union NoDropLike { a: u8 } // should be reported as unused +union NoDropLike { a: u8 } //~ ERROR field is never used union U { a: u8, // should not be reported b: u8, // should not be reported - c: u8, // should be reported + c: u8, //~ ERROR field is never used } type A = U; diff --git a/src/test/ui/union-fields.stderr b/src/test/ui/union-fields.stderr index f3a2702d5aefa..ffcd178ca548f 100644 --- a/src/test/ui/union-fields.stderr +++ b/src/test/ui/union-fields.stderr @@ -1,7 +1,7 @@ error: field is never used: `c` --> $DIR/union-fields.rs:16:5 | -16 | c: u8, // should be reported +16 | c: u8, //~ ERROR field is never used | ^^^^^ | note: lint level defined here @@ -13,19 +13,19 @@ note: lint level defined here error: field is never used: `a` --> $DIR/union-fields.rs:19:5 | -19 | a: u8, // should be reported +19 | a: u8, //~ ERROR field is never used | ^^^^^ error: field is never used: `a` --> $DIR/union-fields.rs:23:20 | -23 | union NoDropLike { a: u8 } // should be reported as unused +23 | union NoDropLike { a: u8 } //~ ERROR field is never used | ^^^^^ error: field is never used: `c` --> $DIR/union-fields.rs:28:5 | -28 | c: u8, // should be reported +28 | c: u8, //~ ERROR field is never used | ^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/union-sized-field.rs b/src/test/ui/union-sized-field.rs index eeca5ab740450..8999f1e0930be 100644 --- a/src/test/ui/union-sized-field.rs +++ b/src/test/ui/union-sized-field.rs @@ -11,16 +11,16 @@ #![feature(untagged_unions)] union Foo { - value: T, + value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied } struct Foo2 { - value: T, + value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied t: u32, } enum Foo3 { - Value(T), + Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied } fn main() {} diff --git a/src/test/ui/union-sized-field.stderr b/src/test/ui/union-sized-field.stderr index ea90d97c4c3d1..9586f9507391f 100644 --- a/src/test/ui/union-sized-field.stderr +++ b/src/test/ui/union-sized-field.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied --> $DIR/union-sized-field.rs:14:5 | -14 | value: T, +14 | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^^^^^^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` @@ -11,7 +11,7 @@ error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied --> $DIR/union-sized-field.rs:18:5 | -18 | value: T, +18 | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^^^^^^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` @@ -21,7 +21,7 @@ error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied --> $DIR/union-sized-field.rs:23:11 | -23 | Value(T), +23 | Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` diff --git a/src/test/ui/update-all-references.sh b/src/test/ui/update-all-references.sh index ddd69c399a5c1..bfc6f923f9d2e 100755 --- a/src/test/ui/update-all-references.sh +++ b/src/test/ui/update-all-references.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright 2015 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index aa99d35f7aa77..b9ded7d1e951c 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright 2015 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index db957a7a0fcda..c4e696e176085 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -11,7 +11,6 @@ extern crate toml; #[macro_use] extern crate serde_derive; -extern crate serde; use std::collections::BTreeMap; use std::env; @@ -83,16 +82,20 @@ static TARGETS: &'static [&'static str] = &[ "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", "sparc64-unknown-linux-gnu", + "sparcv9-sun-solaris", "wasm32-unknown-emscripten", + "wasm32-unknown-unknown", "x86_64-linux-android", "x86_64-apple-darwin", "x86_64-apple-ios", "x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", "x86_64-rumprun-netbsd", + "x86_64-sun-solaris", "x86_64-unknown-freebsd", "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-gnux32", "x86_64-unknown-linux-musl", "x86_64-unknown-netbsd", "x86_64-unknown-redox", @@ -166,18 +169,24 @@ struct Builder { rust_release: String, cargo_release: String, rls_release: String, + rustfmt_release: String, + input: PathBuf, output: PathBuf, gpg_passphrase: String, digests: BTreeMap, s3_address: String, date: String, - rust_version: String, - cargo_version: String, - rls_version: String, + + rust_version: Option, + cargo_version: Option, + rls_version: Option, + rustfmt_version: Option, + rust_git_commit_hash: Option, cargo_git_commit_hash: Option, rls_git_commit_hash: Option, + rustfmt_git_commit_hash: Option, } fn main() { @@ -188,6 +197,7 @@ fn main() { let rust_release = args.next().unwrap(); let cargo_release = args.next().unwrap(); let rls_release = args.next().unwrap(); + let rustfmt_release = args.next().unwrap(); let s3_address = args.next().unwrap(); let mut passphrase = String::new(); t!(io::stdin().read_to_string(&mut passphrase)); @@ -196,18 +206,24 @@ fn main() { rust_release, cargo_release, rls_release, + rustfmt_release, + input, output, gpg_passphrase: passphrase, digests: BTreeMap::new(), s3_address, date, - rust_version: String::new(), - cargo_version: String::new(), - rls_version: String::new(), + + rust_version: None, + cargo_version: None, + rls_version: None, + rustfmt_version: None, + rust_git_commit_hash: None, cargo_git_commit_hash: None, rls_git_commit_hash: None, + rustfmt_git_commit_hash: None, }.build(); } @@ -216,9 +232,12 @@ impl Builder { self.rust_version = self.version("rust", "x86_64-unknown-linux-gnu"); self.cargo_version = self.version("cargo", "x86_64-unknown-linux-gnu"); self.rls_version = self.version("rls", "x86_64-unknown-linux-gnu"); + self.rustfmt_version = self.version("rustfmt", "x86_64-unknown-linux-gnu"); + self.rust_git_commit_hash = self.git_commit_hash("rust", "x86_64-unknown-linux-gnu"); self.cargo_git_commit_hash = self.git_commit_hash("cargo", "x86_64-unknown-linux-gnu"); self.rls_git_commit_hash = self.git_commit_hash("rls", "x86_64-unknown-linux-gnu"); + self.rustfmt_git_commit_hash = self.git_commit_hash("rustfmt", "x86_64-unknown-linux-gnu"); self.digest_and_sign(); let manifest = self.build_manifest(); @@ -253,12 +272,21 @@ impl Builder { self.package("rust-docs", &mut manifest.pkg, TARGETS); self.package("rust-src", &mut manifest.pkg, &["*"]); self.package("rls-preview", &mut manifest.pkg, HOSTS); + self.package("rustfmt-preview", &mut manifest.pkg, HOSTS); self.package("rust-analysis", &mut manifest.pkg, TARGETS); - manifest.renames.insert("rls".to_owned(), Rename { to: "rls-preview".to_owned() }); + let rls_present = manifest.pkg.contains_key("rls-preview"); + let rustfmt_present = manifest.pkg.contains_key("rustfmt-preview"); + + if rls_present { + manifest.renames.insert("rls".to_owned(), Rename { to: "rls-preview".to_owned() }); + } let mut pkg = Package { - version: self.cached_version("rust").to_string(), + version: self.cached_version("rust") + .as_ref() + .expect("Couldn't find Rust version") + .clone(), git_commit_hash: self.cached_git_commit_hash("rust").clone(), target: BTreeMap::new(), }; @@ -291,10 +319,18 @@ impl Builder { }); } - extensions.push(Component { - pkg: "rls-preview".to_string(), - target: host.to_string(), - }); + if rls_present { + extensions.push(Component { + pkg: "rls-preview".to_string(), + target: host.to_string(), + }); + } + if rustfmt_present { + extensions.push(Component { + pkg: "rustfmt-preview".to_string(), + target: host.to_string(), + }); + } extensions.push(Component { pkg: "rust-analysis".to_string(), target: host.to_string(), @@ -331,6 +367,14 @@ impl Builder { pkgname: &str, dst: &mut BTreeMap, targets: &[&str]) { + let version = match *self.cached_version(pkgname) { + Some(ref version) => version.clone(), + None => { + println!("Skipping package {}", pkgname); + return; + } + }; + let targets = targets.iter().map(|name| { let filename = self.filename(pkgname, name); let digest = match self.digests.remove(&filename) { @@ -352,7 +396,7 @@ impl Builder { }).collect(); dst.insert(pkgname.to_string(), Package { - version: self.cached_version(pkgname).to_string(), + version, git_commit_hash: self.cached_git_commit_hash(pkgname).clone(), target: targets, }); @@ -372,16 +416,20 @@ impl Builder { format!("cargo-{}-{}.tar.gz", self.cargo_release, target) } else if component == "rls" || component == "rls-preview" { format!("rls-{}-{}.tar.gz", self.rls_release, target) + } else if component == "rustfmt" || component == "rustfmt-preview" { + format!("rustfmt-{}-{}.tar.gz", self.rustfmt_release, target) } else { format!("{}-{}-{}.tar.gz", component, self.rust_release, target) } } - fn cached_version(&self, component: &str) -> &str { + fn cached_version(&self, component: &str) -> &Option { if component == "cargo" { &self.cargo_version } else if component == "rls" || component == "rls-preview" { &self.rls_version + } else if component == "rustfmt" || component == "rustfmt-preview" { + &self.rustfmt_version } else { &self.rust_version } @@ -392,12 +440,14 @@ impl Builder { &self.cargo_git_commit_hash } else if component == "rls" || component == "rls-preview" { &self.rls_git_commit_hash + } else if component == "rustfmt" || component == "rustfmt-preview" { + &self.rustfmt_git_commit_hash } else { &self.rust_git_commit_hash } } - fn version(&self, component: &str, target: &str) -> String { + fn version(&self, component: &str, target: &str) -> Option { let mut cmd = Command::new("tar"); let filename = self.filename(component, target); cmd.arg("xf") @@ -405,13 +455,12 @@ impl Builder { .arg(format!("{}/version", filename.replace(".tar.gz", ""))) .arg("-O"); let output = t!(cmd.output()); - if !output.status.success() { - panic!("failed to learn version:\n\n{:?}\n\n{}\n\n{}", - cmd, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr)); + if output.status.success() { + Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } else { + // Perhaps we didn't build this package. + None } - String::from_utf8_lossy(&output.stdout).trim().to_string() } fn git_commit_hash(&self, component: &str, target: &str) -> Option { @@ -425,10 +474,6 @@ impl Builder { if output.status.success() { Some(String::from_utf8_lossy(&output.stdout).trim().to_string()) } else { - // This is always called after `.version()`. - // So if that didn’t fail but this does, - // that’s very probably because the tarball is valid - // but does not contain a `git-commit-hash` file. None } } diff --git a/src/tools/cargo b/src/tools/cargo index e447ac7e94b7f..5bb478a518bcf 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit e447ac7e94b7f56ab13e361f9e324dafe3eb0a34 +Subproject commit 5bb478a518bcf75537409e8b71f6b7cc4af362df diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 012ee835494e8..d04231bbac089 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -19,6 +19,7 @@ struct Test { name: &'static str, sha: &'static str, lock: Option<&'static str>, + packages: &'static [&'static str], } const TEST_REPOS: &'static [Test] = &[ @@ -27,30 +28,51 @@ const TEST_REPOS: &'static [Test] = &[ repo: "https://github.com/iron/iron", sha: "21c7dae29c3c214c08533c2a55ac649b418f2fe3", lock: Some(include_str!("lockfiles/iron-Cargo.lock")), + packages: &[], }, Test { name: "ripgrep", repo: "https://github.com/BurntSushi/ripgrep", sha: "b65bb37b14655e1a89c7cd19c8b011ef3e312791", lock: None, + packages: &[], }, Test { name: "tokei", repo: "https://github.com/Aaronepower/tokei", sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928", lock: None, + packages: &[], }, Test { name: "treeify", repo: "https://github.com/dzamlo/treeify", sha: "999001b223152441198f117a68fb81f57bc086dd", lock: None, + packages: &[], }, Test { name: "xsv", repo: "https://github.com/BurntSushi/xsv", - sha: "a9a7163f2a2953cea426fee1216bec914fe2f56a", + sha: "66956b6bfd62d6ac767a6b6499c982eae20a2c9f", lock: None, + packages: &[], + }, + Test { + name: "servo", + repo: "https://github.com/servo/servo", + sha: "17e97b9320fdb7cdb33bbc5f4d0fde0653bbf2e4", + lock: None, + // Only test Stylo a.k.a. Quantum CSS, the parts of Servo going into Firefox. + // This takes much less time to build than all of Servo and supports stable Rust. + packages: &["stylo_tests", "selectors"], + }, + Test { + name: "webrender", + repo: "https://github.com/servo/webrender", + sha: "57250b2b8fa63934f80e5376a29f7dcb3f759ad6", + lock: None, + packages: &[], }, ]; @@ -74,7 +96,7 @@ fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) { .write_all(lockfile.as_bytes()) .expect(""); } - if !run_cargo_test(cargo, &dir) { + if !run_cargo_test(cargo, &dir, test.packages) { panic!("tests failed for {}", test.repo); } } @@ -134,11 +156,17 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { out_dir } -fn run_cargo_test(cargo_path: &Path, crate_path: &Path) -> bool { - let status = Command::new(cargo_path) - .arg("test") +fn run_cargo_test(cargo_path: &Path, crate_path: &Path, packages: &[&str]) -> bool { + let mut command = Command::new(cargo_path); + command.arg("test"); + for name in packages { + command.arg("-p").arg(name); + } + let status = command // Disable rust-lang/cargo's cross-compile tests .env("CFG_DISABLE_CROSS_TESTS", "1") + // Relax #![deny(warnings)] in some crates + .env("RUSTFLAGS", "--cap-lints warn") .current_dir(crate_path) .status() .expect(""); diff --git a/src/tools/clippy b/src/tools/clippy index 25444585592f5..7d7fef1690218 160000 --- a/src/tools/clippy +++ b/src/tools/clippy @@ -1 +1 @@ -Subproject commit 25444585592f5da648edd5317fcdd21f2db8bb64 +Subproject commit 7d7fef1690218bbb406cf3bcadf7bb29dbb40cc5 diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index f8282cc5f8d9b..6fc9423a4139a 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -10,4 +10,10 @@ filetime = "0.1" getopts = "0.2" log = "0.3" rustc-serialize = "0.3" + +[target.'cfg(unix)'.dependencies] libc = "0.2" + +[target.'cfg(windows)'.dependencies] +miow = "0.2" +winapi = "0.2" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index cee7e52c7f3c6..660462ad419f7 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -34,6 +34,20 @@ pub enum Mode { MirOpt, } +impl Mode { + pub fn disambiguator(self) -> &'static str { + // Run-pass and pretty run-pass tests could run concurrently, and if they do, + // they need to keep their output segregated. Same is true for debuginfo tests that + // can be run both on gdb and lldb. + match self { + Pretty => ".pretty", + DebugInfoGdb => ".gdb", + DebugInfoLldb => ".lldb", + _ => "", + } + } +} + impl FromStr for Mode { type Err = (); fn from_str(s: &str) -> Result { @@ -201,6 +215,8 @@ pub struct Config { pub cc: String, pub cxx: String, pub cflags: String, + pub ar: String, + pub linker: Option, pub llvm_components: String, pub llvm_cxxflags: String, pub nodejs: Option, diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index b7fb3670165d6..251dd4d5edb4e 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -45,7 +45,7 @@ impl FromStr for ErrorKind { impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ErrorKind::Help => write!(f, "help"), + ErrorKind::Help => write!(f, "help message"), ErrorKind::Error => write!(f, "error"), ErrorKind::Note => write!(f, "note"), ErrorKind::Suggestion => write!(f, "suggestion"), diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index bb9bf57d55e2f..c853d53829c60 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -150,6 +150,14 @@ impl EarlyProps { // Ignore if actual version is smaller the minimum required // version &actual_version[..] < min_version + } else if line.starts_with("min-system-llvm-version") { + let min_version = line.trim_right() + .rsplit(' ') + .next() + .expect("Malformed llvm version directive"); + // Ignore if using system LLVM and actual version + // is smaller the minimum required version + !(config.system_llvm && &actual_version[..] < min_version) } else { false } @@ -259,9 +267,9 @@ impl TestProps { props } - pub fn from_file(testfile: &Path, config: &Config) -> Self { + pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { let mut props = TestProps::new(); - props.load_from(testfile, None, config); + props.load_from(testfile, cfg, config); props } @@ -269,10 +277,10 @@ impl TestProps { /// tied to a particular revision `foo` (indicated by writing /// `//[foo]`), then the property is ignored unless `cfg` is /// `Some("foo")`. - pub fn load_from(&mut self, - testfile: &Path, - cfg: Option<&str>, - config: &Config) { + fn load_from(&mut self, + testfile: &Path, + cfg: Option<&str>, + config: &Config) { iter_header(testfile, cfg, &mut |ln| { @@ -531,7 +539,7 @@ impl Config { let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap(); name == "test" || - name == util::get_os(&self.target) || // target + util::matches_os(&self.target, name) || // target name == util::get_arch(&self.target) || // architecture name == util::get_pointer_width(&self.target) || // pointer width name == self.stage_id.split('-').next().unwrap() || // stage @@ -567,6 +575,19 @@ impl Config { None } } + + pub fn find_rust_src_root(&self) -> Option { + let mut path = self.src_base.clone(); + let path_postfix = Path::new("src/etc/lldb_batchmode.py"); + + while path.pop() { + if path.join(&path_postfix).is_file() { + return Some(path); + } + } + + None + } } pub fn lldb_version_to_int(version_string: &str) -> isize { diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 77ee93c30078b..99d7dc7816649 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -36,6 +36,7 @@ struct DiagnosticSpan { column_end: usize, is_primary: bool, label: Option, + suggested_replacement: Option, expansion: Option>, } @@ -56,6 +57,25 @@ struct DiagnosticCode { explanation: Option, } +pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String { + output.lines() + .filter_map(|line| if line.starts_with('{') { + match json::decode::(line) { + Ok(diagnostic) => diagnostic.rendered, + Err(error) => { + proc_res.fatal(Some(&format!("failed to decode compiler output as json: \ + `{}`\noutput: {}\nline: {}", + error, + line, + output))); + } + } + } else { + None + }) + .collect() +} + pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec { output.lines() .flat_map(|line| parse_line(file_name, line, output, proc_res)) @@ -164,15 +184,15 @@ fn push_expected_errors(expected_errors: &mut Vec, } // If the message has a suggestion, register that. - if let Some(ref rendered) = diagnostic.rendered { - let start_line = primary_spans.iter().map(|s| s.line_start).min().expect("\ - every suggestion should have at least one span"); - for (index, line) in rendered.lines().enumerate() { - expected_errors.push(Error { - line_num: start_line + index, - kind: Some(ErrorKind::Suggestion), - msg: line.to_string(), - }); + for span in primary_spans { + if let Some(ref suggested_replacement) = span.suggested_replacement { + for (index, line) in suggested_replacement.lines().enumerate() { + expected_errors.push(Error { + line_num: span.line_start + index, + kind: Some(ErrorKind::Suggestion), + msg: line.to_string(), + }); + } } } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 26c447d01d364..6da37df19279a 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -11,10 +11,11 @@ #![crate_name = "compiletest"] #![feature(test)] +#![feature(slice_rotate)] #![deny(warnings)] -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(unix)] extern crate libc; extern crate test; extern crate getopts; @@ -47,6 +48,7 @@ pub mod runtest; pub mod common; pub mod errors; mod raise_fd_limit; +mod read2; fn main() { env_logger::init().unwrap(); @@ -102,6 +104,8 @@ pub fn parse_config(args: Vec ) -> Config { .reqopt("", "cc", "path to a C compiler", "PATH") .reqopt("", "cxx", "path to a C++ compiler", "PATH") .reqopt("", "cflags", "flags for the C compiler", "FLAGS") + .optopt("", "ar", "path to an archiver", "PATH") + .optopt("", "linker", "path to a linker", "PATH") .reqopt("", "llvm-components", "list of LLVM components built in", "LIST") .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS") .optopt("", "nodejs", "the name of nodejs", "PATH") @@ -198,6 +202,8 @@ pub fn parse_config(args: Vec ) -> Config { cc: matches.opt_str("cc").unwrap(), cxx: matches.opt_str("cxx").unwrap(), cflags: matches.opt_str("cflags").unwrap(), + ar: matches.opt_str("ar").unwrap_or("ar".into()), + linker: matches.opt_str("linker"), llvm_components: matches.opt_str("llvm-components").unwrap(), llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(), nodejs: matches.opt_str("nodejs"), @@ -234,6 +240,8 @@ pub fn log_config(config: &Config) { logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir)); logv(c, format!("adb_device_status: {}", config.adb_device_status)); + logv(c, format!("ar: {}", config.ar)); + logv(c, format!("linker: {:?}", config.linker)); logv(c, format!("verbose: {}", config.verbose)); logv(c, format!("quiet: {}", config.quiet)); logv(c, "\n".to_string()); @@ -485,24 +493,42 @@ fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf { config.stage_id); config.build_base.canonicalize() .unwrap_or_else(|_| config.build_base.clone()) + .join(&testpaths.relative_dir) .join(stamp_name) } fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool { + let rust_src_dir = config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let stamp = mtime(&stamp(config, testpaths)); - let mut inputs = vec![ - mtime(&testpaths.file), - mtime(&config.rustc_path), - ]; + let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)]; for aux in props.aux.iter() { - inputs.push(mtime(&testpaths.file.parent().unwrap() - .join("auxiliary") - .join(aux))); + inputs.push(mtime( + &testpaths.file.parent().unwrap().join("auxiliary").join( + aux, + ), + )); + } + // Relevant pretty printer files + let pretty_printer_files = [ + "src/etc/debugger_pretty_printers_common.py", + "src/etc/gdb_load_rust_pretty_printers.py", + "src/etc/gdb_rust_pretty_printing.py", + "src/etc/lldb_batchmode.py", + "src/etc/lldb_rust_formatters.py", + ]; + for pretty_printer_file in &pretty_printer_files { + inputs.push(mtime(&rust_src_dir.join(pretty_printer_file))); } for lib in config.run_lib_path.read_dir().unwrap() { let lib = lib.unwrap(); inputs.push(mtime(&lib.path())); } + if let Some(ref rustdoc_path) = config.rustdoc_path { + inputs.push(mtime(&rustdoc_path)); + inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py"))); + } inputs.iter().any(|input| *input > stamp) } diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs new file mode 100644 index 0000000000000..1d8816c7db132 --- /dev/null +++ b/src/tools/compiletest/src/read2.rs @@ -0,0 +1,208 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs` +// Consider unify the read2() in libstd, cargo and this to prevent further code duplication. + +pub use self::imp::read2; + +#[cfg(not(any(unix, windows)))] +mod imp { + use std::io::{self, Read}; + use std::process::{ChildStdout, ChildStderr}; + + pub fn read2(out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + let mut buffer = Vec::new(); + out_pipe.read_to_end(&mut buffer)?; + data(true, &mut buffer, true); + buffer.clear(); + err_pipe.read_to_end(&mut buffer)?; + data(false, &mut buffer, true); + Ok(()) + } +} + +#[cfg(unix)] +mod imp { + use std::io::prelude::*; + use std::io; + use std::mem; + use std::os::unix::prelude::*; + use std::process::{ChildStdout, ChildStderr}; + use libc; + + pub fn read2(mut out_pipe: ChildStdout, + mut err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + unsafe { + libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); + libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); + } + + let mut out_done = false; + let mut err_done = false; + let mut out = Vec::new(); + let mut err = Vec::new(); + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = out_pipe.as_raw_fd(); + fds[0].events = libc::POLLIN; + fds[1].fd = err_pipe.as_raw_fd(); + fds[1].events = libc::POLLIN; + loop { + // wait for either pipe to become readable using `select` + let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) }; + if r == -1 { + let err = io::Error::last_os_error(); + if err.kind() == io::ErrorKind::Interrupted { + continue + } + return Err(err) + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + let handle = |res: io::Result<_>| { + match res { + Ok(_) => Ok(true), + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + Ok(false) + } else { + Err(e) + } + } + } + }; + if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? { + out_done = true; + } + data(true, &mut out, out_done); + if !err_done && fds[1].revents != 0 && handle(err_pipe.read_to_end(&mut err))? { + err_done = true; + } + data(false, &mut err, err_done); + + if out_done && err_done { + return Ok(()) + } + } + } +} + +#[cfg(windows)] +mod imp { + extern crate miow; + extern crate winapi; + + use std::io; + use std::os::windows::prelude::*; + use std::process::{ChildStdout, ChildStderr}; + use std::slice; + + use self::miow::iocp::{CompletionPort, CompletionStatus}; + use self::miow::pipe::NamedPipe; + use self::miow::Overlapped; + use self::winapi::ERROR_BROKEN_PIPE; + + struct Pipe<'a> { + dst: &'a mut Vec, + overlapped: Overlapped, + pipe: NamedPipe, + done: bool, + } + + pub fn read2(out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + let mut out = Vec::new(); + let mut err = Vec::new(); + + let port = CompletionPort::new(1)?; + port.add_handle(0, &out_pipe)?; + port.add_handle(1, &err_pipe)?; + + unsafe { + let mut out_pipe = Pipe::new(out_pipe, &mut out); + let mut err_pipe = Pipe::new(err_pipe, &mut err); + + out_pipe.read()?; + err_pipe.read()?; + + let mut status = [CompletionStatus::zero(), CompletionStatus::zero()]; + + while !out_pipe.done || !err_pipe.done { + for status in port.get_many(&mut status, None)? { + if status.token() == 0 { + out_pipe.complete(status); + data(true, out_pipe.dst, out_pipe.done); + out_pipe.read()?; + } else { + err_pipe.complete(status); + data(false, err_pipe.dst, err_pipe.done); + err_pipe.read()?; + } + } + } + + Ok(()) + } + } + + impl<'a> Pipe<'a> { + unsafe fn new(p: P, dst: &'a mut Vec) -> Pipe<'a> { + Pipe { + dst: dst, + pipe: NamedPipe::from_raw_handle(p.into_raw_handle()), + overlapped: Overlapped::zero(), + done: false, + } + } + + unsafe fn read(&mut self) -> io::Result<()> { + let dst = slice_to_end(self.dst); + match self.pipe.read_overlapped(dst, self.overlapped.raw()) { + Ok(_) => Ok(()), + Err(e) => { + if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) { + self.done = true; + Ok(()) + } else { + Err(e) + } + } + } + } + + unsafe fn complete(&mut self, status: &CompletionStatus) { + let prev = self.dst.len(); + self.dst.set_len(prev + status.bytes_transferred() as usize); + if status.bytes_transferred() == 0 { + self.done = true; + } + } + } + + unsafe fn slice_to_end(v: &mut Vec) -> &mut [u8] { + if v.capacity() == 0 { + v.reserve(16); + } + if v.capacity() == v.len() { + v.reserve(1); + } + slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), + v.capacity() - v.len()) + } +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 10ef326d9db8c..7d20bb74c7aeb 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -25,10 +25,11 @@ use std::collections::HashSet; use std::env; use std::ffi::OsString; use std::fs::{self, File, create_dir_all}; +use std::fmt; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Path, PathBuf}; -use std::process::{Command, Output, ExitStatus, Stdio}; +use std::process::{Command, Output, ExitStatus, Stdio, Child}; use std::str; use extract_gdb_version; @@ -68,7 +69,7 @@ pub fn run(config: Config, testpaths: &TestPaths) { print!("\n\n"); } debug!("running {:?}", testpaths.file.display()); - let base_props = TestProps::from_file(&testpaths.file, &config); + let base_props = TestProps::from_file(&testpaths.file, None, &config); let base_cx = TestCx { config: &config, props: &base_props, @@ -80,8 +81,9 @@ pub fn run(config: Config, testpaths: &TestPaths) { base_cx.run_revision() } else { for revision in &base_props.revisions { - let mut revision_props = base_props.clone(); - revision_props.load_from(&testpaths.file, Some(revision), &config); + let revision_props = TestProps::from_file(&testpaths.file, + Some(revision), + &config); let rev_cx = TestCx { config: &config, props: &revision_props, @@ -571,9 +573,10 @@ actual:\n\ } } - _=> { - let rust_src_root = self.find_rust_src_root() - .expect("Could not find Rust source root"); + _ => { + let rust_src_root = self.config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let rust_pp_module_rel_path = Path::new("./src/etc"); let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path) .to_str() @@ -664,19 +667,6 @@ actual:\n\ self.check_debugger_output(&debugger_run_result, &check_lines); } - fn find_rust_src_root(&self) -> Option { - let mut path = self.config.src_base.clone(); - let path_postfix = Path::new("src/etc/lldb_batchmode.py"); - - while path.pop() { - if path.join(&path_postfix).is_file() { - return Some(path); - } - } - - None - } - fn run_debuginfo_lldb_test(&self) { assert!(self.revision.is_none(), "revisions not relevant here"); @@ -735,7 +725,9 @@ actual:\n\ script_str.push_str("version\n"); // Switch LLDB into "Rust mode" - let rust_src_root = self.find_rust_src_root().expect("Could not find Rust source root"); + let rust_src_root = self.config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py"); let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path) .to_str() @@ -1050,7 +1042,7 @@ actual:\n\ None => { if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) { self.error( - &format!("{}:{}: unexpected {:?}: '{}'", + &format!("{}:{}: unexpected {}: '{}'", file_name, actual_error.line_num, actual_error.kind.as_ref() @@ -1164,6 +1156,9 @@ actual:\n\ .arg("-o").arg(out_dir) .arg(&self.testpaths.file) .args(&self.props.compile_flags); + if let Some(ref linker) = self.config.linker { + rustdoc.arg("--linker").arg(linker).arg("-Z").arg("unstable-options"); + } self.compose_and_run_compiler(rustdoc, None) } @@ -1276,6 +1271,7 @@ actual:\n\ let crate_type = if aux_props.no_prefer_dynamic { None } else if (self.config.target.contains("musl") && !aux_props.force_host) || + self.config.target.contains("wasm32") || self.config.target.contains("emscripten") { // We primarily compile all auxiliary libraries as dynamic libraries // to avoid code size bloat and large binaries as much as possible @@ -1350,12 +1346,14 @@ actual:\n\ if let Some(input) = input { child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap(); } - let Output { status, stdout, stderr } = child.wait_with_output().unwrap(); + + let Output { status, stdout, stderr } = read2_abbreviated(child) + .expect("failed to read output"); let result = ProcRes { status, - stdout: String::from_utf8(stdout).unwrap(), - stderr: String::from_utf8(stderr).unwrap(), + stdout: String::from_utf8_lossy(&stdout).into_owned(), + stderr: String::from_utf8_lossy(&stderr).into_owned(), cmdline, }; @@ -1372,7 +1370,7 @@ actual:\n\ // Optionally prevent default --target if specified in test compile-flags. let custom_target = self.props.compile_flags .iter() - .fold(false, |acc, x| acc || x.starts_with("--target")); + .any(|x| x.starts_with("--target")); if !custom_target { let target = if self.props.force_host { @@ -1390,6 +1388,8 @@ actual:\n\ if let Some(ref incremental_dir) = self.props.incremental_dir { rustc.args(&["-Z", &format!("incremental={}", incremental_dir.display())]); + rustc.args(&["-Z", "incremental-verify-ich"]); + rustc.args(&["-Z", "incremental-queries"]); } match self.config.mode { @@ -1403,6 +1403,11 @@ actual:\n\ rustc.args(&["--error-format", "json"]); } } + Ui => { + if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) { + rustc.args(&["--error-format", "json"]); + } + } MirOpt => { rustc.args(&[ "-Zdump-mir=all", @@ -1410,6 +1415,7 @@ actual:\n\ "-Zdump-mir-exclude-pass-number"]); let mir_dump_dir = self.get_mir_dump_dir(); + let _ = fs::remove_dir_all(&mir_dump_dir); create_dir_all(mir_dump_dir.as_path()).unwrap(); let mut dir_opt = "-Zdump-mir-dir=".to_string(); dir_opt.push_str(mir_dump_dir.to_str().unwrap()); @@ -1426,13 +1432,15 @@ actual:\n\ Codegen | Rustdoc | RunMake | - Ui | CodegenUnits => { // do not use JSON output } } - if !self.props.no_prefer_dynamic { + + if self.config.target == "wasm32-unknown-unknown" { + // rustc.arg("-g"); // get any backtrace at all on errors + } else if !self.props.no_prefer_dynamic { rustc.args(&["-C", "prefer-dynamic"]); } @@ -1450,6 +1458,9 @@ actual:\n\ } else { rustc.args(self.split_maybe_args(&self.config.target_rustcflags)); } + if let Some(ref linker) = self.config.linker { + rustc.arg(format!("-Clinker={}", linker)); + } rustc.args(&self.props.compile_flags); @@ -1470,6 +1481,10 @@ actual:\n\ let mut fname = f.file_name().unwrap().to_os_string(); fname.push(".js"); f.set_file_name(&fname); + } else if self.config.target.contains("wasm32") { + let mut fname = f.file_name().unwrap().to_os_string(); + fname.push(".wasm"); + f.set_file_name(&fname); } else if !env::consts::EXE_SUFFIX.is_empty() { let mut fname = f.file_name().unwrap().to_os_string(); fname.push(env::consts::EXE_SUFFIX); @@ -1492,6 +1507,22 @@ actual:\n\ } } + // If this is otherwise wasm , then run tests under nodejs with our + // shim + if self.config.target.contains("wasm32") { + if let Some(ref p) = self.config.nodejs { + args.push(p.clone()); + } else { + self.fatal("no NodeJS binary found (--nodejs)"); + } + + let src = self.config.src_base + .parent().unwrap() // chop off `run-pass` + .parent().unwrap() // chop off `test` + .parent().unwrap(); // chop off `src` + args.push(src.join("src/etc/wasm32-shim.js").display().to_string()); + } + let exe_file = self.make_exe_name(); // FIXME (#9639): This needs to handle non-utf8 paths @@ -1567,7 +1598,7 @@ actual:\n\ fn aux_output_dir_name(&self) -> PathBuf { let f = self.output_base_name(); let mut fname = f.file_name().unwrap().to_os_string(); - fname.push(&format!(".{}.libaux", self.config.mode)); + fname.push(&format!("{}.aux", self.config.mode.disambiguator())); f.with_file_name(&fname) } @@ -1636,7 +1667,9 @@ actual:\n\ cmd.arg("-a").arg("-u"); cmd.arg(filename); cmd.arg("-nobanner"); - let output = match cmd.output() { + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); + let output = match cmd.spawn().and_then(read2_abbreviated) { Ok(output) => output, Err(_) => return, }; @@ -1717,11 +1750,13 @@ actual:\n\ if self.props.check_test_line_numbers_match { self.check_rustdoc_test_option(proc_res); } else { - let root = self.find_rust_src_root().unwrap(); - let res = self.cmd2procres(Command::new(&self.config.docck_python) - .arg(root.join("src/etc/htmldocck.py")) - .arg(out_dir) - .arg(&self.testpaths.file)); + let root = self.config.find_rust_src_root().unwrap(); + let res = self.cmd2procres( + Command::new(&self.config.docck_python) + .arg(root.join("src/etc/htmldocck.py")) + .arg(out_dir) + .arg(&self.testpaths.file), + ); if !res.status.success() { self.fatal_proc_rec("htmldocck failed!", &res); } @@ -2036,7 +2071,6 @@ actual:\n\ // Add an extra flag pointing at the incremental directory. let mut revision_props = self.props.clone(); revision_props.incremental_dir = Some(incremental_dir); - revision_props.compile_flags.push(String::from("-Zincremental-info")); let revision_cx = TestCx { config: self.config, @@ -2095,6 +2129,8 @@ actual:\n\ let mut cmd = Command::new(make); cmd.current_dir(&self.testpaths.file) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.docck_python) .env("S", src_root) @@ -2109,6 +2145,10 @@ actual:\n\ .env("LLVM_COMPONENTS", &self.config.llvm_components) .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags); + if let Some(ref linker) = self.config.linker { + cmd.env("RUSTC_LINKER", linker); + } + // We don't want RUSTFLAGS set from the outside to interfere with // compiler flags set in the test cases: cmd.env_remove("RUSTFLAGS"); @@ -2131,14 +2171,15 @@ actual:\n\ .env("CXX", &self.config.cxx); } else { cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) - .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags)); + .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags)) + .env("AR", &self.config.ar); if self.config.target.contains("windows") { cmd.env("IS_WINDOWS", "1"); } } - let output = cmd.output().expect("failed to spawn `make`"); + let output = cmd.spawn().and_then(read2_abbreviated).expect("failed to spawn `make`"); if !output.status.success() { let res = ProcRes { status: output.status, @@ -2174,6 +2215,11 @@ actual:\n\ } fn run_ui_test(&self) { + // if the user specified a format in the ui test + // print the output to the stderr file, otherwise extract + // the rendered error messages from json and print them + let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format")); + let proc_res = self.compile_test(); let expected_stderr_path = self.expected_output_path("stderr"); @@ -2184,8 +2230,15 @@ actual:\n\ let normalized_stdout = self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout); + + let stderr = if explicit { + proc_res.stderr.clone() + } else { + json::extract_rendered(&proc_res.stderr, &proc_res) + }; + let normalized_stderr = - self.normalize_output(&proc_res.stderr, &self.props.normalize_stderr); + self.normalize_output(&stderr, &self.props.normalize_stderr); let mut errors = 0; errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout); @@ -2204,6 +2257,8 @@ actual:\n\ &proc_res); } + let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); + if self.props.run_pass { let proc_res = self.exec_compiled_test(); @@ -2211,6 +2266,15 @@ actual:\n\ self.fatal_proc_rec("test run failed!", &proc_res); } } + if !explicit { + if !expected_errors.is_empty() || !proc_res.status.success() { + // "// error-pattern" comments + self.check_expected_errors(expected_errors, &proc_res); + } else if !self.props.error_patterns.is_empty() || !proc_res.status.success() { + // "//~ERROR comments" + self.check_error_patterns(&proc_res.stderr, &proc_res); + } + } } fn run_mir_opt_test(&self) { @@ -2237,7 +2301,7 @@ actual:\n\ let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len()); let tests_text_str = String::from(tests_text); let mut curr_test : Option<&str> = None; - let mut curr_test_contents = Vec::new(); + let mut curr_test_contents = vec![ExpectedLine::Elision]; for l in tests_text_str.lines() { debug!("line: {:?}", l); if l.starts_with("// START ") { @@ -2251,11 +2315,14 @@ actual:\n\ self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents); curr_test = None; curr_test_contents.clear(); + curr_test_contents.push(ExpectedLine::Elision); } else if l.is_empty() { // ignore + } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." { + curr_test_contents.push(ExpectedLine::Elision) } else if l.starts_with("// ") { let (_, test_content) = l.split_at("// ".len()); - curr_test_contents.push(test_content); + curr_test_contents.push(ExpectedLine::Text(test_content)); } } } @@ -2273,68 +2340,138 @@ actual:\n\ } } - fn compare_mir_test_output(&self, test_name: &str, expected_content: &[&str]) { + fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) { let mut output_file = PathBuf::new(); output_file.push(self.get_mir_dump_dir()); output_file.push(test_name); debug!("comparing the contests of: {:?}", output_file); debug!("with: {:?}", expected_content); + if !output_file.exists() { + panic!("Output file `{}` from test does not exist", + output_file.into_os_string().to_string_lossy()); + } self.check_mir_test_timestamp(test_name, &output_file); let mut dumped_file = fs::File::open(output_file.clone()).unwrap(); let mut dumped_string = String::new(); dumped_file.read_to_string(&mut dumped_string).unwrap(); let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty()); - let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty()); + let mut expected_lines = expected_content.iter().filter(|&l| { + if let &ExpectedLine::Text(l) = l { + !l.is_empty() + } else { + true + } + }).peekable(); - // We expect each non-empty line from expected_content to appear - // in the dump in order, but there may be extra lines interleaved - while let Some(expected_line) = expected_lines.next() { + let compare = |expected_line, dumped_line| { let e_norm = normalize_mir_line(expected_line); - if e_norm.is_empty() { - continue; + let d_norm = normalize_mir_line(dumped_line); + debug!("found: {:?}", d_norm); + debug!("expected: {:?}", e_norm); + e_norm == d_norm + }; + + let error = |expected_line, extra_msg| { + let normalize_all = dumped_string.lines() + .map(nocomment_mir_line) + .filter(|l| !l.is_empty()) + .collect::>() + .join("\n"); + let f = |l: &ExpectedLine<_>| match l { + &ExpectedLine::Elision => "... (elided)".into(), + &ExpectedLine::Text(t) => t }; - let mut found = false; - while let Some(dumped_line) = dumped_lines.next() { - let d_norm = normalize_mir_line(dumped_line); - debug!("found: {:?}", d_norm); - debug!("expected: {:?}", e_norm); - if e_norm == d_norm { - found = true; - break; - }; - } - if !found { - let normalize_all = dumped_string.lines() - .map(nocomment_mir_line) - .filter(|l| !l.is_empty()) - .collect::>() - .join("\n"); - panic!("ran out of mir dump output to match against.\n\ - Did not find expected line: {:?}\n\ - Expected:\n{}\n\ - Actual:\n{}", - expected_line, - expected_content.join("\n"), - normalize_all); + let expected_content = expected_content.iter() + .map(|l| f(l)) + .collect::>() + .join("\n"); + panic!("Did not find expected line, error: {}\n\ + Actual Line: {:?}\n\ + Expected:\n{}\n\ + Actual:\n{}", + extra_msg, + expected_line, + expected_content, + normalize_all); + }; + + // We expect each non-empty line to appear consecutively, non-consecutive lines + // must be separated by at least one Elision + let mut start_block_line = None; + while let Some(dumped_line) = dumped_lines.next() { + match expected_lines.next() { + Some(&ExpectedLine::Text(expected_line)) => { + let normalized_expected_line = normalize_mir_line(expected_line); + if normalized_expected_line.contains(":{") { + start_block_line = Some(expected_line); + } + + if !compare(expected_line, dumped_line) { + error!("{:?}", start_block_line); + error(expected_line, + format!("Mismatch in lines\nCurrnt block: {}\nExpected Line: {:?}", + start_block_line.unwrap_or("None"), dumped_line)); + } + }, + Some(&ExpectedLine::Elision) => { + // skip any number of elisions in a row. + while let Some(&&ExpectedLine::Elision) = expected_lines.peek() { + expected_lines.next(); + } + if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() { + let mut found = compare(expected_line, dumped_line); + if found { + continue; + } + while let Some(dumped_line) = dumped_lines.next() { + found = compare(expected_line, dumped_line); + if found { + break; + } + } + if !found { + error(expected_line, "ran out of mir dump to match against".into()); + } + } + }, + None => {}, } } } fn get_mir_dump_dir(&self) -> PathBuf { - let mut mir_dump_dir = PathBuf::from(self.config.build_base - .as_path() - .to_str() - .unwrap()); + let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); debug!("input_file: {:?}", self.testpaths.file); - mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap()); + mir_dump_dir.push(&self.testpaths.relative_dir); + mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); mir_dump_dir } fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String { let parent_dir = self.testpaths.file.parent().unwrap(); - let parent_dir_str = parent_dir.display().to_string(); - let mut normalized = output.replace(&parent_dir_str, "$DIR") + let cflags = self.props.compile_flags.join(" "); + let json = cflags.contains("--error-format json") || + cflags.contains("--error-format pretty-json") || + cflags.contains("--error-format=json") || + cflags.contains("--error-format=pretty-json"); + let parent_dir_str = if json { + parent_dir.display().to_string().replace("\\", "\\\\") + } else { + parent_dir.display().to_string() + }; + + let mut normalized = output.replace(&parent_dir_str, "$DIR"); + + if json { + // escaped newlines in json strings should be readable + // in the stderr files. There's no point int being correct, + // since only humans process the stderr files. + // Thus we just turn escaped newlines back into newlines. + normalized = normalized.replace("\\n", "\n"); + } + + normalized = normalized.replace("\\\\", "\\") // denormalize for paths on windows .replace("\\", "/") // normalize for paths on windows .replace("\r\n", "\n") // normalize for linebreaks on windows .replace("\t", "\\t"); // makes tabs visible @@ -2439,6 +2576,25 @@ enum TargetLocation { ThisDirectory(PathBuf), } +#[derive(Clone, PartialEq, Eq)] +enum ExpectedLine> { + Elision, + Text(T) +} + +impl fmt::Debug for ExpectedLine +where + T: AsRef + fmt::Debug +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let &ExpectedLine::Text(ref t) = self { + write!(formatter, "{:?}", t) + } else { + write!(formatter, "\"...\" (Elision)") + } + } +} + fn normalize_mir_line(line: &str) -> String { nocomment_mir_line(line).replace(char::is_whitespace, "") } @@ -2451,3 +2607,76 @@ fn nocomment_mir_line(line: &str) -> &str { line } } + +fn read2_abbreviated(mut child: Child) -> io::Result { + use std::mem::replace; + use read2::read2; + + const HEAD_LEN: usize = 160 * 1024; + const TAIL_LEN: usize = 256 * 1024; + + enum ProcOutput { + Full(Vec), + Abbreviated { + head: Vec, + skipped: usize, + tail: Box<[u8]>, + } + } + + impl ProcOutput { + fn extend(&mut self, data: &[u8]) { + let new_self = match *self { + ProcOutput::Full(ref mut bytes) => { + bytes.extend_from_slice(data); + let new_len = bytes.len(); + if new_len <= HEAD_LEN + TAIL_LEN { + return; + } + let tail = bytes.split_off(new_len - TAIL_LEN).into_boxed_slice(); + let head = replace(bytes, Vec::new()); + let skipped = new_len - HEAD_LEN - TAIL_LEN; + ProcOutput::Abbreviated { head, skipped, tail } + } + ProcOutput::Abbreviated { ref mut skipped, ref mut tail, .. } => { + *skipped += data.len(); + if data.len() <= TAIL_LEN { + tail[..data.len()].copy_from_slice(data); + tail.rotate(data.len()); + } else { + tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]); + } + return; + } + }; + *self = new_self; + } + + fn into_bytes(self) -> Vec { + match self { + ProcOutput::Full(bytes) => bytes, + ProcOutput::Abbreviated { mut head, skipped, tail } => { + write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap(); + head.extend_from_slice(&tail); + head + } + } + } + } + + let mut stdout = ProcOutput::Full(Vec::new()); + let mut stderr = ProcOutput::Full(Vec::new()); + + drop(child.stdin.take()); + read2(child.stdout.take().unwrap(), child.stderr.take().unwrap(), &mut |is_stdout, data, _| { + if is_stdout { &mut stdout } else { &mut stderr }.extend(data); + data.clear(); + })?; + let status = child.wait()?; + + Ok(Output { + status, + stdout: stdout.into_bytes(), + stderr: stderr.into_bytes(), + }) +} diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 85fa38bbd3be7..c00f28eae67af 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -51,10 +51,15 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("wasm32", "wasm32"), ]; -pub fn get_os(triple: &str) -> &'static str { +pub fn matches_os(triple: &str, name: &str) -> bool { + // For the wasm32 bare target we ignore anything also ignored on emscripten + // and then we also recognize `wasm32-bare` as the os for the target + if triple == "wasm32-unknown-unknown" { + return name == "emscripten" || name == "wasm32-bare" + } for &(triple_os, os) in OS_TABLE { if triple.contains(triple_os) { - return os; + return os == name; } } panic!("Cannot determine OS from triple"); @@ -73,7 +78,7 @@ pub fn get_env(triple: &str) -> Option<&str> { } pub fn get_pointer_width(triple: &str) -> &'static str { - if triple.contains("64") || triple.starts_with("s390x") { + if (triple.contains("64") && !triple.ends_with("gnux32")) || triple.starts_with("s390x") { "64bit" } else { "32bit" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 3ea2e6313af4c..3b0925d3ef6a4 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -69,15 +69,33 @@ struct FileEntry { type Cache = HashMap; +fn small_url_encode(s: &str) -> String { + s.replace("<", "%3C") + .replace(">", "%3E") + .replace(" ", "%20") + .replace("?", "%3F") + .replace("'", "%27") + .replace("&", "%26") + .replace(",", "%2C") + .replace(":", "%3A") + .replace(";", "%3B") + .replace("[", "%5B") + .replace("]", "%5D") + .replace("\"", "%22") +} + impl FileEntry { fn parse_ids(&mut self, file: &Path, contents: &str, errors: &mut bool) { if self.ids.is_empty() { with_attrs_in_source(contents, " id", |fragment, i, _| { let frag = fragment.trim_left_matches("#").to_owned(); + let encoded = small_url_encode(&frag); if !self.ids.insert(frag) { *errors = true; println!("{}:{}: id is not unique: `{}`", file.display(), i, fragment); } + // Just in case, we also add the encoded id. + self.ids.insert(encoded); }); } } diff --git a/src/tools/miri b/src/tools/miri index 80853e2f24a01..dd630a2a26310 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 80853e2f24a01db96fe9821e468dd2af75a4d2e5 +Subproject commit dd630a2a263105f104b82dd53a1b9b90d4206a3d diff --git a/src/tools/rls b/src/tools/rls index 93b47d14cef57..194f8286f1270 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit 93b47d14cef5720bba7cfb4dcb8078fbf1f706c1 +Subproject commit 194f8286f12701de80c95a457723a33ccad014ed diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index e57c105008ade..4aa096246bcf3 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -8,5 +8,5 @@ license = "MIT/Apache-2.0" clap = "2.25.0" [dependencies.mdbook] -version = "0.0.25" +version = "0.0.26" default-features = false diff --git a/src/tools/rustfmt b/src/tools/rustfmt index 22eb5241c0ee5..426ba1cdabf32 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit 22eb5241c0ee5bb7eaf95e270a2b1500e82bf767 +Subproject commit 426ba1cdabf323cee7c42b71ca34eac1ee3192cd diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index 11d5dbe736e81..f6e42c8dc17b1 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -31,9 +31,9 @@ pub fn check(path: &Path, bad: &mut bool) { if let Ok(mut file) = fs::File::open("/proc/version") { let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); - // Probably on Windows Linux Subsystem, all files will be marked as - // executable, so skip checking. - if contents.contains("Microsoft") { + // Probably on Windows Linux Subsystem or Docker via VirtualBox, + // all files will be marked as executable, so skip checking. + if contents.contains("Microsoft") || contents.contains("boot2docker") { return; } } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 38df657769430..bc2767c7bcc5d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -35,11 +35,8 @@ static EXCEPTIONS: &'static [&'static str] = &[ "thread-id", // Apache-2.0, mdbook "cssparser", // MPL-2.0, rustdoc "smallvec", // MPL-2.0, rustdoc - // FIXME: remove magenta references when "everything" has moved over to using the zircon name. - "magenta-sys", // BSD-3-Clause, rustdoc - "magenta", // BSD-3-Clause, rustdoc - "zircon-sys", // BSD-3-Clause, rustdoc - "zircon", // BSD-3-Clause, rustdoc + "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo + "fuchsia-zircon", // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir) "cssparser-macros", // MPL-2.0, rustdoc "selectors", // MPL-2.0, rustdoc ]; diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index a2a264490a141..9736c03993081 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -351,6 +351,26 @@ fn map_lib_features(base_src_path: &Path, } } becoming_feature = None; + if line.contains("rustc_const_unstable(") { + // const fn features are handled specially + let feature_name = match find_attr_val(line, "feature") { + Some(name) => name, + None => err!("malformed stability attribute"), + }; + let feature = Feature { + level: Status::Unstable, + since: "None".to_owned(), + has_gate_test: false, + // Whether there is a common tracking issue + // for these feature gates remains an open question + // https://github.com/rust-lang/rust/issues/24111#issuecomment-340283184 + // But we take 24111 otherwise they will be shown as + // "internal to the compiler" which they are not. + tracking_issue: Some(24111), + }; + mf(Ok((feature_name, feature)), file, i + 1); + continue; + } let level = if line.contains("[unstable(") { Status::Unstable } else if line.contains("[stable(") { diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 5b24d748e9697..02f665e3991bc 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -33,10 +33,9 @@ macro_rules! t { macro_rules! tidy_error { ($bad:expr, $fmt:expr, $($arg:tt)*) => ({ - use std::io::Write; *$bad = true; - write!(::std::io::stderr(), "tidy error: ").expect("could not write to stderr"); - writeln!(::std::io::stderr(), $fmt, $($arg)*).expect("could not write to stderr"); + eprint!("tidy error: "); + eprintln!($fmt, $($arg)*); }); } @@ -51,12 +50,13 @@ pub mod unstable_book; fn filter_dirs(path: &Path) -> bool { let skip = [ + "src/binaryen", + "src/dlmalloc", "src/jemalloc", "src/llvm", "src/libbacktrace", "src/libcompiler_builtins", "src/compiler-rt", - "src/rustllvm", "src/liblibc", "src/vendor", "src/rt/hoedown", diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 433192a21ec9c..f6640c902bcb4 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -22,7 +22,6 @@ use tidy::*; use std::process; use std::path::PathBuf; use std::env; -use std::io::{self, Write}; fn main() { let path = env::args_os().skip(1).next().expect("need an argument"); @@ -44,7 +43,7 @@ fn main() { } if bad { - writeln!(io::stderr(), "some tidy checks failed").expect("could not write to stderr"); + eprintln!("some tidy checks failed"); process::exit(1); } } diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index a689d8a8be411..40d84b98d3a7d 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -50,6 +50,11 @@ const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest "#; +const LLVM_UNREACHABLE_INFO: &str = r"\ +C++ code used llvm_unreachable, which triggers undefined behavior +when executed when assertions are disabled. +Use llvm::report_fatal_error for increased robustness."; + /// Parser states for line_is_url. #[derive(PartialEq)] #[allow(non_camel_case_types)] @@ -108,7 +113,7 @@ pub fn check(path: &Path, bad: &mut bool) { let mut contents = String::new(); super::walk(path, &mut super::filter_dirs, &mut |file| { let filename = file.file_name().unwrap().to_string_lossy(); - let extensions = [".rs", ".py", ".js", ".sh", ".c", ".h"]; + let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"]; if extensions.iter().all(|e| !filename.ends_with(e)) || filename.starts_with(".#") { return @@ -153,6 +158,9 @@ pub fn check(path: &Path, bad: &mut bool) { if line.ends_with("```ignore") || line.ends_with("```rust,ignore") { err(UNEXPLAINED_IGNORE_DOCTEST_INFO); } + if filename.ends_with(".cpp") && line.contains("llvm_unreachable") { + err(LLVM_UNREACHABLE_INFO); + } } if !licenseck(file, &contents) { tidy_error!(bad, "{}: incorrect license", file.display()); diff --git a/src/tools/toolstate.toml b/src/tools/toolstate.toml index 1700daa0aff14..13295ef3ee70a 100644 --- a/src/tools/toolstate.toml +++ b/src/tools/toolstate.toml @@ -3,7 +3,7 @@ # # There are three states a tool can be in: # 1. Broken: The tool doesn't build -# 2. Building: The tool builds but its tests are failing +# 2. Compiling: The tool builds but its tests are failing # 3. Testing: The tool builds and its tests are passing # # In the future there will be further states like "Distributing", which @@ -23,14 +23,13 @@ # Each tool has a list of people to ping # ping @oli-obk @RalfJung @eddyb -miri = "Broken" +miri = "Testing" # ping @Manishearth @llogiq @mcarton @oli-obk -clippy = "Broken" +clippy = "Testing" # ping @nrc -rls = "Testing" +rls = "Broken" # ping @nrc -rustfmt = "Testing" - +rustfmt = "Broken"